Blame view

net/phonet/pep.c 29.7 KB
2b27bdcc2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
2
3
4
5
6
7
8
  /*
   * File: pep.c
   *
   * Phonet pipe protocol end point socket
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
31fdc5553   Rémi Denis-Courmont   net: remove my fu...
9
   * Author: Rémi Denis-Courmont
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
10
11
12
   */
  
  #include <linux/kernel.h>
174cd4b1e   Ingo Molnar   sched/headers: Pr...
13
  #include <linux/sched/signal.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
15
16
17
18
19
20
  #include <linux/socket.h>
  #include <net/sock.h>
  #include <net/tcp_states.h>
  #include <asm/ioctls.h>
  
  #include <linux/phonet.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
21
  #include <linux/module.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
22
23
  #include <net/phonet/phonet.h>
  #include <net/phonet/pep.h>
02a47617c   Rémi Denis-Courmont   Phonet: implement...
24
  #include <net/phonet/gprs.h>
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
25
26
27
28
29
30
31
32
33
  
  /* sk_state values:
   * TCP_CLOSE		sock not in use yet
   * TCP_CLOSE_WAIT	disconnected pipe
   * TCP_LISTEN		listening pipe endpoint
   * TCP_SYN_RECV		connected pipe in disabled state
   * TCP_ESTABLISHED	connected pipe in enabled state
   *
   * pep_sock locking:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
34
   *  - sk_state, hlist: sock lock needed
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
35
36
37
38
39
40
   *  - listener: read only
   *  - pipe_handle: read only
   */
  
  #define CREDITS_MAX	10
  #define CREDITS_THR	7
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */
  
  /* Get the next TLV sub-block. */
  static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
  					void *buf)
  {
  	void *data = NULL;
  	struct {
  		u8 sb_type;
  		u8 sb_len;
  	} *ph, h;
  	int buflen = *plen;
  
  	ph = skb_header_pointer(skb, 0, 2, &h);
  	if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
  		return NULL;
  	ph->sb_len -= 2;
  	*ptype = ph->sb_type;
  	*plen = ph->sb_len;
  
  	if (buflen > ph->sb_len)
  		buflen = ph->sb_len;
  	data = skb_header_pointer(skb, 2, buflen, buf);
  	__skb_pull(skb, 2 + ph->sb_len);
  	return data;
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  static struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload,
  					int len, gfp_t priority)
  {
  	struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);
  	if (!skb)
  		return NULL;
  	skb_set_owner_w(skb, sk);
  
  	skb_reserve(skb, MAX_PNPIPE_HEADER);
  	__skb_put(skb, len);
  	skb_copy_to_linear_data(skb, payload, len);
  	__skb_push(skb, sizeof(struct pnpipehdr));
  	skb_reset_transport_header(skb);
  	return skb;
  }
  
  static int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code,
  			const void *data, int len, gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
85
86
87
88
  {
  	const struct pnpipehdr *oph = pnp_hdr(oskb);
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
89
  	struct sockaddr_pn peer;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
90

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
91
  	skb = pep_alloc_skb(sk, data, len, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
92
93
  	if (!skb)
  		return -ENOMEM;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
94

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
95
96
97
98
99
  	ph = pnp_hdr(skb);
  	ph->utid = oph->utid;
  	ph->message_id = oph->message_id + 1; /* REQ -> RESP */
  	ph->pipe_handle = oph->pipe_handle;
  	ph->error_code = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
100
101
  	pn_skb_get_src_sockaddr(oskb, &peer);
  	return pn_skb_send(sk, skb, &peer);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
102
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
103
104
  static int pep_indicate(struct sock *sk, u8 id, u8 code,
  			const void *data, int len, gfp_t priority)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
105
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
106
  	struct pep_sock *pn = pep_sk(sk);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
107
108
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
109

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
110
  	skb = pep_alloc_skb(sk, data, len, priority);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
111
112
  	if (!skb)
  		return -ENOMEM;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
113

8d98efa84   Kumar Sanghvi   Phonet: Implement...
114
  	ph = pnp_hdr(skb);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
115
116
  	ph->utid = 0;
  	ph->message_id = id;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
117
  	ph->pipe_handle = pn->pipe_handle;
6321aa197   Arnd Bergmann   phonet: fix build...
118
  	ph->error_code = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
119
  	return pn_skb_send(sk, skb, NULL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
120
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
121
  #define PAD 0x00
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
122
123
  static int pipe_handler_request(struct sock *sk, u8 id, u8 code,
  				const void *data, int len)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
124
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
125
  	struct pep_sock *pn = pep_sk(sk);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
126
127
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
128

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
129
  	skb = pep_alloc_skb(sk, data, len, GFP_KERNEL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
130
131
  	if (!skb)
  		return -ENOMEM;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
132

8d98efa84   Kumar Sanghvi   Phonet: Implement...
133
  	ph = pnp_hdr(skb);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
134
135
  	ph->utid = id; /* whatever */
  	ph->message_id = id;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
136
  	ph->pipe_handle = pn->pipe_handle;
6321aa197   Arnd Bergmann   phonet: fix build...
137
  	ph->error_code = code;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
138
  	return pn_skb_send(sk, skb, NULL);
8d98efa84   Kumar Sanghvi   Phonet: Implement...
139
  }
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
140
  static int pipe_handler_send_created_ind(struct sock *sk)
8d98efa84   Kumar Sanghvi   Phonet: Implement...
141
  {
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
142
  	struct pep_sock *pn = pep_sk(sk);
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
143
144
145
146
  	u8 data[4] = {
  		PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2),
  		pn->tx_fc, pn->rx_fc,
  	};
8d98efa84   Kumar Sanghvi   Phonet: Implement...
147

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
148
149
150
  	return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */,
  				data, 4, GFP_ATOMIC);
  }
8d98efa84   Kumar Sanghvi   Phonet: Implement...
151

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
  {
  	static const u8 data[20] = {
  		PAD, PAD, PAD, 2 /* sub-blocks */,
  		PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
  			PN_MULTI_CREDIT_FLOW_CONTROL,
  			PN_ONE_CREDIT_FLOW_CONTROL,
  			PN_LEGACY_FLOW_CONTROL,
  			PAD,
  		PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
  			PN_MULTI_CREDIT_FLOW_CONTROL,
  			PN_ONE_CREDIT_FLOW_CONTROL,
  			PN_LEGACY_FLOW_CONTROL,
  			PAD,
  	};
  
  	might_sleep();
  	return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
  				GFP_KERNEL);
  }
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
172
173
  static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code,
  				gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
174
175
176
  {
  	static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };
  	WARN_ON(code == PN_PIPE_NO_ERROR);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
177
  	return pep_reply(sk, skb, code, data, sizeof(data), priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
178
179
180
181
  }
  
  /* Control requests are not sent by the pipe service and have a specific
   * message format. */
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
182
183
  static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,
  				gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
184
185
186
187
188
  {
  	const struct pnpipehdr *oph = pnp_hdr(oskb);
  	struct sk_buff *skb;
  	struct pnpipehdr *ph;
  	struct sockaddr_pn dst;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
189
  	u8 data[4] = {
6321aa197   Arnd Bergmann   phonet: fix build...
190
  		oph->pep_type, /* PEP type */
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
191
192
193
  		code, /* error code, at an unusual offset */
  		PAD, PAD,
  	};
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
194

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
195
  	skb = pep_alloc_skb(sk, data, 4, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
196
197
  	if (!skb)
  		return -ENOMEM;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
198

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
199
  	ph = pnp_hdr(skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
200
201
202
  	ph->utid = oph->utid;
  	ph->message_id = PNS_PEP_CTRL_RESP;
  	ph->pipe_handle = oph->pipe_handle;
6321aa197   Arnd Bergmann   phonet: fix build...
203
  	ph->data0 = oph->data[0]; /* CTRL id */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
204
205
206
207
208
209
210
  
  	pn_skb_get_src_sockaddr(oskb, &dst);
  	return pn_skb_send(sk, skb, &dst);
  }
  
  static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)
  {
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
211
  	u8 data[4] = { type, PAD, PAD, status };
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
212

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
213
214
  	return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON,
  				data, 4, priority);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
215
216
217
218
  }
  
  /* Send our RX flow control information to the sender.
   * Socket must be locked. */
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
219
  static void pipe_grant_credits(struct sock *sk, gfp_t priority)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
220
221
222
223
224
225
226
227
228
  {
  	struct pep_sock *pn = pep_sk(sk);
  
  	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
  
  	switch (pn->rx_fc) {
  	case PN_LEGACY_FLOW_CONTROL: /* TODO */
  		break;
  	case PN_ONE_CREDIT_FLOW_CONTROL:
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
229
230
231
  		if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL,
  					PEP_IND_READY, priority) == 0)
  			pn->rx_credits = 1;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
232
233
234
235
236
237
  		break;
  	case PN_MULTI_CREDIT_FLOW_CONTROL:
  		if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)
  			break;
  		if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
  					CREDITS_MAX - pn->rx_credits,
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
238
  					priority) == 0)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
239
240
241
242
243
244
245
246
  			pn->rx_credits = CREDITS_MAX;
  		break;
  	}
  }
  
  static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
a91e7d471   Kumar Sanghvi   Phonet: Correct h...
247
  	struct pnpipehdr *hdr;
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
248
  	int wake = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
249
250
251
  
  	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
  		return -EINVAL;
a91e7d471   Kumar Sanghvi   Phonet: Correct h...
252
  	hdr = pnp_hdr(skb);
6321aa197   Arnd Bergmann   phonet: fix build...
253
  	if (hdr->pep_type != PN_PEP_TYPE_COMMON) {
ba7a46f16   Joe Perches   net: Convert LIMI...
254
255
  		net_dbg_ratelimited("Phonet unknown PEP type: %u
  ",
6321aa197   Arnd Bergmann   phonet: fix build...
256
  				    (unsigned int)hdr->pep_type);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
257
258
  		return -EOPNOTSUPP;
  	}
6321aa197   Arnd Bergmann   phonet: fix build...
259
  	switch (hdr->data[0]) {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
260
261
262
  	case PN_PEP_IND_FLOW_CONTROL:
  		switch (pn->tx_fc) {
  		case PN_LEGACY_FLOW_CONTROL:
6321aa197   Arnd Bergmann   phonet: fix build...
263
  			switch (hdr->data[3]) {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
264
  			case PEP_IND_BUSY:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
265
  				atomic_set(&pn->tx_credits, 0);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
266
267
  				break;
  			case PEP_IND_READY:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
268
  				atomic_set(&pn->tx_credits, wake = 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
269
270
271
272
  				break;
  			}
  			break;
  		case PN_ONE_CREDIT_FLOW_CONTROL:
6321aa197   Arnd Bergmann   phonet: fix build...
273
  			if (hdr->data[3] == PEP_IND_READY)
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
274
  				atomic_set(&pn->tx_credits, wake = 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
275
276
277
278
279
280
281
  			break;
  		}
  		break;
  
  	case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
  		if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
  			break;
6321aa197   Arnd Bergmann   phonet: fix build...
282
  		atomic_add(wake = hdr->data[3], &pn->tx_credits);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
283
284
285
  		break;
  
  	default:
ba7a46f16   Joe Perches   net: Convert LIMI...
286
287
  		net_dbg_ratelimited("Phonet unknown PEP indication: %u
  ",
6321aa197   Arnd Bergmann   phonet: fix build...
288
  				    (unsigned int)hdr->data[0]);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
289
290
  		return -EOPNOTSUPP;
  	}
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
291
  	if (wake)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
292
293
294
295
296
297
298
299
  		sk->sk_write_space(sk);
  	return 0;
  }
  
  static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
6321aa197   Arnd Bergmann   phonet: fix build...
300
  	u8 n_sb = hdr->data0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  
  	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
  	__skb_pull(skb, sizeof(*hdr));
  	while (n_sb > 0) {
  		u8 type, buf[2], len = sizeof(buf);
  		u8 *data = pep_get_sb(skb, &type, &len, buf);
  
  		if (data == NULL)
  			return -EINVAL;
  		switch (type) {
  		case PN_PIPE_SB_NEGOTIATED_FC:
  			if (len < 2 || (data[0] | data[1]) > 3)
  				break;
  			pn->tx_fc = data[0] & 3;
  			pn->rx_fc = data[1] & 3;
  			break;
  		}
  		n_sb--;
  	}
  	return 0;
  }
  
  /* Queue an skb to a connected sock.
   * Socket lock must be held. */
  static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
329
  	struct sk_buff_head *queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
330
331
332
333
334
335
  	int err = 0;
  
  	BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
  
  	switch (hdr->message_id) {
  	case PNS_PEP_CONNECT_REQ:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
336
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  		break;
  
  	case PNS_PEP_DISCONNECT_REQ:
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		sk->sk_state = TCP_CLOSE_WAIT;
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  		break;
  
  	case PNS_PEP_ENABLE_REQ:
  		/* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_RESET_REQ:
  		switch (hdr->state_after_reset) {
  		case PN_PIPE_DISABLE:
  			pn->init_enable = 0;
  			break;
  		case PN_PIPE_ENABLE:
  			pn->init_enable = 1;
  			break;
  		default: /* not allowed to send an error here!? */
  			err = -EINVAL;
  			goto out;
  		}
  		/* fall through */
  	case PNS_PEP_DISABLE_REQ:
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
365
  		atomic_set(&pn->tx_credits, 0);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
366
367
368
369
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_CTRL_REQ:
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
370
371
  		if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {
  			atomic_inc(&sk->sk_drops);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
372
  			break;
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
373
  		}
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
374
375
376
  		__skb_pull(skb, 4);
  		queue = &pn->ctrlreq_queue;
  		goto queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
377

fc6a11075   Rémi Denis-Courmont   Phonet: zero-copy...
378
379
380
  	case PNS_PIPE_ALIGNED_DATA:
  		__skb_pull(skb, 1);
  		/* fall through */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
381
382
383
384
385
  	case PNS_PIPE_DATA:
  		__skb_pull(skb, 3); /* Pipe data header */
  		if (!pn_flow_safe(pn->rx_fc)) {
  			err = sock_queue_rcv_skb(sk, skb);
  			if (!err)
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
386
387
  				return NET_RX_SUCCESS;
  			err = -ENOBUFS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
388
389
390
391
  			break;
  		}
  
  		if (pn->rx_credits == 0) {
2e2fb4b33   Rémi Denis-Courmont   Phonet: account f...
392
  			atomic_inc(&sk->sk_drops);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
393
394
395
396
  			err = -ENOBUFS;
  			break;
  		}
  		pn->rx_credits--;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
397
398
  		queue = &sk->sk_receive_queue;
  		goto queue;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  
  	case PNS_PEP_STATUS_IND:
  		pipe_rcv_status(sk, skb);
  		break;
  
  	case PNS_PIPE_REDIRECTED_IND:
  		err = pipe_rcv_created(sk, skb);
  		break;
  
  	case PNS_PIPE_CREATED_IND:
  		err = pipe_rcv_created(sk, skb);
  		if (err)
  			break;
  		/* fall through */
  	case PNS_PIPE_RESET_IND:
  		if (!pn->init_enable)
  			break;
  		/* fall through */
  	case PNS_PIPE_ENABLED_IND:
  		if (!pn_flow_safe(pn->tx_fc)) {
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
419
  			atomic_set(&pn->tx_credits, 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
420
421
422
423
424
  			sk->sk_write_space(sk);
  		}
  		if (sk->sk_state == TCP_ESTABLISHED)
  			break; /* Nothing to do */
  		sk->sk_state = TCP_ESTABLISHED;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
425
  		pipe_grant_credits(sk, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
426
427
428
429
430
431
432
433
  		break;
  
  	case PNS_PIPE_DISABLED_IND:
  		sk->sk_state = TCP_SYN_RECV;
  		pn->rx_credits = 0;
  		break;
  
  	default:
ba7a46f16   Joe Perches   net: Convert LIMI...
434
435
436
  		net_dbg_ratelimited("Phonet unknown PEP message: %u
  ",
  				    hdr->message_id);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
437
438
439
440
  		err = -EINVAL;
  	}
  out:
  	kfree_skb(skb);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
441
  	return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
442
443
444
445
  
  queue:
  	skb->dev = NULL;
  	skb_set_owner_r(skb, sk);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
446
447
  	skb_queue_tail(queue, skb);
  	if (!sock_flag(sk, SOCK_DEAD))
676d23690   David S. Miller   net: Fix use afte...
448
  		sk->sk_data_ready(sk);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
449
  	return NET_RX_SUCCESS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
450
451
452
453
454
  }
  
  /* Destroy connected sock. */
  static void pipe_destruct(struct sock *sk)
  {
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
455
  	struct pep_sock *pn = pep_sk(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
456
  	skb_queue_purge(&sk->sk_receive_queue);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
457
  	skb_queue_purge(&pn->ctrlreq_queue);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
458
  }
95c961747   Eric Dumazet   net: cleanup unsi...
459
  static u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n)
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
460
  {
95c961747   Eric Dumazet   net: cleanup unsi...
461
  	unsigned int i;
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
462
463
464
465
466
467
468
469
470
471
  	u8 final_fc = PN_NO_FLOW_CONTROL;
  
  	for (i = 0; i < n; i++) {
  		u8 fc = fcs[i];
  
  		if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL)
  			final_fc = fc;
  	}
  	return final_fc;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
472
473
474
  static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
475
476
477
478
479
480
481
  	struct pnpipehdr *hdr;
  	u8 n_sb;
  
  	if (!pskb_pull(skb, sizeof(*hdr) + 4))
  		return -EINVAL;
  
  	hdr = pnp_hdr(skb);
297edb600   Rémi Denis-Courmont   Phonet: support a...
482
483
  	if (hdr->error_code != PN_PIPE_NO_ERROR)
  		return -ECONNREFUSED;
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
484
485
  
  	/* Parse sub-blocks */
6321aa197   Arnd Bergmann   phonet: fix build...
486
  	n_sb = hdr->data[3];
8f44fcc72   Rémi Denis-Courmont   Phonet: fix flawe...
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  	while (n_sb > 0) {
  		u8 type, buf[6], len = sizeof(buf);
  		const u8 *data = pep_get_sb(skb, &type, &len, buf);
  
  		if (data == NULL)
  			return -EINVAL;
  
  		switch (type) {
  		case PN_PIPE_SB_REQUIRED_FC_TX:
  			if (len < 2 || len < data[0])
  				break;
  			pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2);
  			break;
  
  		case PN_PIPE_SB_PREFERRED_FC_RX:
  			if (len < 2 || len < data[0])
  				break;
  			pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2);
  			break;
  
  		}
  		n_sb--;
  	}
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
510

44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
511
  	return pipe_handler_send_created_ind(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
512
  }
297edb600   Rémi Denis-Courmont   Phonet: support a...
513

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
  static int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pnpipehdr *hdr = pnp_hdr(skb);
  
  	if (hdr->error_code != PN_PIPE_NO_ERROR)
  		return -ECONNREFUSED;
  
  	return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */,
  		NULL, 0, GFP_ATOMIC);
  
  }
  
  static void pipe_start_flow_control(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  
  	if (!pn_flow_safe(pn->tx_fc)) {
  		atomic_set(&pn->tx_credits, 1);
  		sk->sk_write_space(sk);
  	}
  	pipe_grant_credits(sk, GFP_ATOMIC);
  }
297edb600   Rémi Denis-Courmont   Phonet: support a...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  /* Queue an skb to an actively connected sock.
   * Socket lock must be held. */
  static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *hdr = pnp_hdr(skb);
  	int err = NET_RX_SUCCESS;
  
  	switch (hdr->message_id) {
  	case PNS_PIPE_ALIGNED_DATA:
  		__skb_pull(skb, 1);
  		/* fall through */
  	case PNS_PIPE_DATA:
  		__skb_pull(skb, 3); /* Pipe data header */
  		if (!pn_flow_safe(pn->rx_fc)) {
  			err = sock_queue_rcv_skb(sk, skb);
  			if (!err)
  				return NET_RX_SUCCESS;
  			err = NET_RX_DROP;
  			break;
  		}
  
  		if (pn->rx_credits == 0) {
  			atomic_inc(&sk->sk_drops);
  			err = NET_RX_DROP;
  			break;
  		}
  		pn->rx_credits--;
  		skb->dev = NULL;
  		skb_set_owner_r(skb, sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
566
567
  		skb_queue_tail(&sk->sk_receive_queue, skb);
  		if (!sock_flag(sk, SOCK_DEAD))
676d23690   David S. Miller   net: Fix use afte...
568
  			sk->sk_data_ready(sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
569
570
571
572
573
574
575
576
577
578
579
  		return NET_RX_SUCCESS;
  
  	case PNS_PEP_CONNECT_RESP:
  		if (sk->sk_state != TCP_SYN_SENT)
  			break;
  		if (!sock_flag(sk, SOCK_DEAD))
  			sk->sk_state_change(sk);
  		if (pep_connresp_rcv(sk, skb)) {
  			sk->sk_state = TCP_CLOSE_WAIT;
  			break;
  		}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
580
581
582
583
584
585
586
  		if (pn->init_enable == PN_PIPE_DISABLE)
  			sk->sk_state = TCP_SYN_RECV;
  		else {
  			sk->sk_state = TCP_ESTABLISHED;
  			pipe_start_flow_control(sk);
  		}
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
587

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
588
589
590
591
592
593
594
  	case PNS_PEP_ENABLE_RESP:
  		if (sk->sk_state != TCP_SYN_SENT)
  			break;
  
  		if (pep_enableresp_rcv(sk, skb)) {
  			sk->sk_state = TCP_CLOSE_WAIT;
  			break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
595
  		}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
596
597
598
  
  		sk->sk_state = TCP_ESTABLISHED;
  		pipe_start_flow_control(sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
599
600
601
602
603
604
605
606
607
608
609
610
611
  		break;
  
  	case PNS_PEP_DISCONNECT_RESP:
  		/* sock should already be dead, nothing to do */
  		break;
  
  	case PNS_PEP_STATUS_IND:
  		pipe_rcv_status(sk, skb);
  		break;
  	}
  	kfree_skb(skb);
  	return err;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
612

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
613
614
615
616
617
  /* Listening sock must be locked */
  static struct sock *pep_find_pipe(const struct hlist_head *hlist,
  					const struct sockaddr_pn *dst,
  					u8 pipe_handle)
  {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
618
619
  	struct sock *sknode;
  	u16 dobj = pn_sockaddr_get_object(dst);
b67bfe0d4   Sasha Levin   hlist: drop the n...
620
  	sk_for_each(sknode, hlist) {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  		struct pep_sock *pnnode = pep_sk(sknode);
  
  		/* Ports match, but addresses might not: */
  		if (pnnode->pn_sk.sobject != dobj)
  			continue;
  		if (pnnode->pipe_handle != pipe_handle)
  			continue;
  		if (sknode->sk_state == TCP_CLOSE_WAIT)
  			continue;
  
  		sock_hold(sknode);
  		return sknode;
  	}
  	return NULL;
  }
  
  /*
   * Deliver an skb to a listening sock.
   * Socket lock must be held.
   * We then queue the skb to the right connected sock (if any).
   */
  static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct sock *sknode;
2ddc1ac1b   Rémi Denis-Courmont   Phonet: do not co...
646
  	struct pnpipehdr *hdr;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
647
  	struct sockaddr_pn dst;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  	u8 pipe_handle;
  
  	if (!pskb_may_pull(skb, sizeof(*hdr)))
  		goto drop;
  
  	hdr = pnp_hdr(skb);
  	pipe_handle = hdr->pipe_handle;
  	if (pipe_handle == PN_PIPE_INVALID_HANDLE)
  		goto drop;
  
  	pn_skb_get_dst_sockaddr(skb, &dst);
  
  	/* Look for an existing pipe handle */
  	sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
  	if (sknode)
  		return sk_receive_skb(sknode, skb, 1);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
664
665
  	switch (hdr->message_id) {
  	case PNS_PEP_CONNECT_REQ:
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
666
667
668
669
670
671
672
673
  		if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) {
  			pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE,
  					GFP_ATOMIC);
  			break;
  		}
  		skb_queue_head(&sk->sk_receive_queue, skb);
  		sk_acceptq_added(sk);
  		if (!sock_flag(sk, SOCK_DEAD))
676d23690   David S. Miller   net: Fix use afte...
674
  			sk->sk_data_ready(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
675
  		return NET_RX_SUCCESS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
676
677
678
679
680
681
  
  	case PNS_PEP_DISCONNECT_REQ:
  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
  		break;
  
  	case PNS_PEP_CTRL_REQ:
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
682
  		pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
683
684
685
686
687
688
  		break;
  
  	case PNS_PEP_RESET_REQ:
  	case PNS_PEP_ENABLE_REQ:
  	case PNS_PEP_DISABLE_REQ:
  		/* invalid handle is not even allowed here! */
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
689
  		break;
297edb600   Rémi Denis-Courmont   Phonet: support a...
690
691
692
693
694
695
  
  	default:
  		if ((1 << sk->sk_state)
  				& ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT))
  			/* actively connected socket */
  			return pipe_handler_do_rcv(sk, skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
696
697
698
  	}
  drop:
  	kfree_skb(skb);
0ebbf3186   Rémi Denis-Courmont   Phonet: correct p...
699
  	return NET_RX_SUCCESS;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
700
  }
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
701
702
703
704
705
  static int pipe_do_remove(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *ph;
  	struct sk_buff *skb;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
706
  	skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL);
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
707
708
  	if (!skb)
  		return -ENOMEM;
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
709
710
711
712
  	ph = pnp_hdr(skb);
  	ph->utid = 0;
  	ph->message_id = PNS_PIPE_REMOVE_REQ;
  	ph->pipe_handle = pn->pipe_handle;
6321aa197   Arnd Bergmann   phonet: fix build...
713
  	ph->data0 = PAD;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
714
  	return pn_skb_send(sk, skb, NULL);
6482f554e   Rémi Denis-Courmont   Phonet: remove da...
715
  }
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
716
717
718
719
  /* associated socket ceases to exist */
  static void pep_sock_close(struct sock *sk, long timeout)
  {
  	struct pep_sock *pn = pep_sk(sk);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
720
  	int ifindex = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
721

e513480e2   Rémi Denis-Courmont   Phonet: fix poten...
722
  	sock_hold(sk); /* keep a reference after sk_common_release() */
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
723
724
725
  	sk_common_release(sk);
  
  	lock_sock(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
726
  	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) {
297edb600   Rémi Denis-Courmont   Phonet: support a...
727
728
729
730
731
732
  		if (sk->sk_backlog_rcv == pipe_do_rcv)
  			/* Forcefully remove dangling Phonet pipe */
  			pipe_do_remove(sk);
  		else
  			pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD,
  						NULL, 0);
2feb61816   Rémi Denis-Courmont   Phonet: remove re...
733
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
734
  	sk->sk_state = TCP_CLOSE;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
735

02a47617c   Rémi Denis-Courmont   Phonet: implement...
736
737
  	ifindex = pn->ifindex;
  	pn->ifindex = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
738
  	release_sock(sk);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
739
740
741
  
  	if (ifindex)
  		gprs_detach(sk);
e513480e2   Rémi Denis-Courmont   Phonet: fix poten...
742
  	sock_put(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
743
  }
cdfbabfb2   David Howells   net: Work around ...
744
745
  static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp,
  				    bool kern)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
746
  {
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
747
748
749
750
751
752
753
754
755
  	struct pep_sock *pn = pep_sk(sk), *newpn;
  	struct sock *newsk = NULL;
  	struct sk_buff *skb;
  	struct pnpipehdr *hdr;
  	struct sockaddr_pn dst, src;
  	int err;
  	u16 peer_type;
  	u8 pipe_handle, enabled, n_sb;
  	u8 aligned = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
756

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
757
758
759
  	skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
  	if (!skb)
  		return NULL;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
760

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
761
762
763
764
  	lock_sock(sk);
  	if (sk->sk_state != TCP_LISTEN) {
  		err = -EINVAL;
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
765
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
766
  	sk_acceptq_removed(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
767

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
768
769
770
  	err = -EPROTO;
  	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
771

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
  	hdr = pnp_hdr(skb);
  	pipe_handle = hdr->pipe_handle;
  	switch (hdr->state_after_connect) {
  	case PN_PIPE_DISABLE:
  		enabled = 0;
  		break;
  	case PN_PIPE_ENABLE:
  		enabled = 1;
  		break;
  	default:
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
  				GFP_KERNEL);
  		goto drop;
  	}
  	peer_type = hdr->other_pep_type << 8;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
787

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
788
  	/* Parse sub-blocks (options) */
6321aa197   Arnd Bergmann   phonet: fix build...
789
  	n_sb = hdr->data[3];
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
790
791
792
  	while (n_sb > 0) {
  		u8 type, buf[1], len = sizeof(buf);
  		const u8 *data = pep_get_sb(skb, &type, &len, buf);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
793

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  		if (data == NULL)
  			goto drop;
  		switch (type) {
  		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
  			if (len < 1)
  				goto drop;
  			peer_type = (peer_type & 0xff00) | data[0];
  			break;
  		case PN_PIPE_SB_ALIGNED_DATA:
  			aligned = data[0] != 0;
  			break;
  		}
  		n_sb--;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
808

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
809
810
811
812
  	/* Check for duplicate pipe handle */
  	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
  	if (unlikely(newsk)) {
  		__sock_put(newsk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
813
  		newsk = NULL;
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
814
815
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
  		goto drop;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
816
  	}
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
817
  	/* Create a new to-be-accepted sock */
cdfbabfb2   David Howells   net: Work around ...
818
819
  	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot,
  			 kern);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
  	if (!newsk) {
  		pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
  		err = -ENOBUFS;
  		goto drop;
  	}
  
  	sock_init_data(NULL, newsk);
  	newsk->sk_state = TCP_SYN_RECV;
  	newsk->sk_backlog_rcv = pipe_do_rcv;
  	newsk->sk_protocol = sk->sk_protocol;
  	newsk->sk_destruct = pipe_destruct;
  
  	newpn = pep_sk(newsk);
  	pn_skb_get_dst_sockaddr(skb, &dst);
  	pn_skb_get_src_sockaddr(skb, &src);
  	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
  	newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
  	newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
838
  	sock_hold(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
839
840
841
842
843
844
845
846
847
848
  	newpn->listener = sk;
  	skb_queue_head_init(&newpn->ctrlreq_queue);
  	newpn->pipe_handle = pipe_handle;
  	atomic_set(&newpn->tx_credits, 0);
  	newpn->ifindex = 0;
  	newpn->peer_type = peer_type;
  	newpn->rx_credits = 0;
  	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
  	newpn->init_enable = enabled;
  	newpn->aligned = aligned;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
849

f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
850
851
852
853
854
855
  	err = pep_accept_conn(newsk, skb);
  	if (err) {
  		sock_put(newsk);
  		newsk = NULL;
  		goto drop;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
856
  	sk_add_node(newsk, &pn->hlist);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
857
  drop:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
858
  	release_sock(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
859
  	kfree_skb(skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
860
861
862
  	*errp = err;
  	return newsk;
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
863
864
865
  static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)
  {
  	struct pep_sock *pn = pep_sk(sk);
297edb600   Rémi Denis-Courmont   Phonet: support a...
866
  	int err;
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
867
  	u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD };
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
868

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
869
870
  	if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE)
  		pn->pipe_handle = 1; /* anything but INVALID_HANDLE */
297edb600   Rémi Denis-Courmont   Phonet: support a...
871
  	err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ,
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
872
  				pn->init_enable, data, 4);
297edb600   Rémi Denis-Courmont   Phonet: support a...
873
874
875
876
  	if (err) {
  		pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
  		return err;
  	}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
877

297edb600   Rémi Denis-Courmont   Phonet: support a...
878
  	sk->sk_state = TCP_SYN_SENT;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
879
880
881
882
883
884
885
886
887
888
889
890
891
892
  
  	return 0;
  }
  
  static int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len)
  {
  	int err;
  
  	err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD,
  				NULL, 0);
  	if (err)
  		return err;
  
  	sk->sk_state = TCP_SYN_SENT;
297edb600   Rémi Denis-Courmont   Phonet: support a...
893
  	return 0;
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
894
  }
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
895

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
896
897
  static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
  {
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
898
  	struct pep_sock *pn = pep_sk(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
899
  	int answ;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
900
  	int ret = -ENOIOCTLCMD;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
901
902
903
  
  	switch (cmd) {
  	case SIOCINQ:
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
904
905
906
907
  		if (sk->sk_state == TCP_LISTEN) {
  			ret = -EINVAL;
  			break;
  		}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
908
909
  
  		lock_sock(sk);
f64f9e719   Joe Perches   net: Move && and ...
910
911
  		if (sock_flag(sk, SOCK_URGINLINE) &&
  		    !skb_queue_empty(&pn->ctrlreq_queue))
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
912
913
  			answ = skb_peek(&pn->ctrlreq_queue)->len;
  		else if (!skb_queue_empty(&sk->sk_receive_queue))
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
914
915
916
917
  			answ = skb_peek(&sk->sk_receive_queue)->len;
  		else
  			answ = 0;
  		release_sock(sk);
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
918
919
920
921
922
923
924
925
926
927
928
929
930
  		ret = put_user(answ, (int __user *)arg);
  		break;
  
  	case SIOCPNENABLEPIPE:
  		lock_sock(sk);
  		if (sk->sk_state == TCP_SYN_SENT)
  			ret =  -EBUSY;
  		else if (sk->sk_state == TCP_ESTABLISHED)
  			ret = -EISCONN;
  		else
  			ret = pep_sock_enable(sk, NULL, 0);
  		release_sock(sk);
  		break;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
931
  	}
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
932
  	return ret;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
933
934
935
936
937
  }
  
  static int pep_init(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
f7ae8d59f   Rémi Denis-Courmont   Phonet: allocate ...
938
  	sk->sk_destruct = pipe_destruct;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
939
  	INIT_HLIST_HEAD(&pn->hlist);
297edb600   Rémi Denis-Courmont   Phonet: support a...
940
  	pn->listener = NULL;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
941
  	skb_queue_head_init(&pn->ctrlreq_queue);
297edb600   Rémi Denis-Courmont   Phonet: support a...
942
943
944
  	atomic_set(&pn->tx_credits, 0);
  	pn->ifindex = 0;
  	pn->peer_type = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
945
  	pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
297edb600   Rémi Denis-Courmont   Phonet: support a...
946
947
948
949
  	pn->rx_credits = 0;
  	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
  	pn->init_enable = 1;
  	pn->aligned = 0;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
950
951
  	return 0;
  }
02a47617c   Rémi Denis-Courmont   Phonet: implement...
952
  static int pep_setsockopt(struct sock *sk, int level, int optname,
b7058842c   David S. Miller   net: Make setsock...
953
  				char __user *optval, unsigned int optlen)
02a47617c   Rémi Denis-Courmont   Phonet: implement...
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
  {
  	struct pep_sock *pn = pep_sk(sk);
  	int val = 0, err = 0;
  
  	if (level != SOL_PNPIPE)
  		return -ENOPROTOOPT;
  	if (optlen >= sizeof(int)) {
  		if (get_user(val, (int __user *) optval))
  			return -EFAULT;
  	}
  
  	lock_sock(sk);
  	switch (optname) {
  	case PNPIPE_ENCAP:
  		if (val && val != PNPIPE_ENCAP_IP) {
  			err = -EINVAL;
  			break;
  		}
  		if (!pn->ifindex == !val)
  			break; /* Nothing to do! */
  		if (!capable(CAP_NET_ADMIN)) {
  			err = -EPERM;
  			break;
  		}
  		if (val) {
  			release_sock(sk);
  			err = gprs_attach(sk);
  			if (err > 0) {
  				pn->ifindex = err;
  				err = 0;
  			}
  		} else {
  			pn->ifindex = 0;
  			release_sock(sk);
  			gprs_detach(sk);
  			err = 0;
  		}
  		goto out_norel;
03789f267   Rémi Denis-Courmont   Phonet: cleanup p...
992

bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
993
994
995
996
997
998
999
1000
1001
1002
1003
  	case PNPIPE_HANDLE:
  		if ((sk->sk_state == TCP_CLOSE) &&
  			(val >= 0) && (val < PN_PIPE_INVALID_HANDLE))
  			pn->pipe_handle = val;
  		else
  			err = -EINVAL;
  		break;
  
  	case PNPIPE_INITSTATE:
  		pn->init_enable = !!val;
  		break;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
  	default:
  		err = -ENOPROTOOPT;
  	}
  	release_sock(sk);
  
  out_norel:
  	return err;
  }
  
  static int pep_getsockopt(struct sock *sk, int level, int optname,
  				char __user *optval, int __user *optlen)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	int len, val;
  
  	if (level != SOL_PNPIPE)
  		return -ENOPROTOOPT;
  	if (get_user(len, optlen))
  		return -EFAULT;
  
  	switch (optname) {
  	case PNPIPE_ENCAP:
  		val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
  		break;
8d98efa84   Kumar Sanghvi   Phonet: Implement...
1028

03789f267   Rémi Denis-Courmont   Phonet: cleanup p...
1029
1030
1031
  	case PNPIPE_IFINDEX:
  		val = pn->ifindex;
  		break;
acaf7df61   Rémi Denis-Courmont   Phonet: provide p...
1032
1033
1034
1035
1036
  	case PNPIPE_HANDLE:
  		val = pn->pipe_handle;
  		if (val == PN_PIPE_INVALID_HANDLE)
  			return -EINVAL;
  		break;
bdb6e697b   Dinesh Kumar Sharma   Phonet: set the p...
1037
1038
1039
  	case PNPIPE_INITSTATE:
  		val = pn->init_enable;
  		break;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
  	default:
  		return -ENOPROTOOPT;
  	}
  
  	len = min_t(unsigned int, sizeof(int), len);
  	if (put_user(len, optlen))
  		return -EFAULT;
  	if (put_user(val, (int __user *) optval))
  		return -EFAULT;
  	return 0;
  }
  
  static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct pnpipehdr *ph;
e1a5964f0   Rémi Denis-Courmont   Phonet: restore f...
1056
  	int err;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1057

be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1058
1059
1060
1061
1062
  	if (pn_flow_safe(pn->tx_fc) &&
  	    !atomic_add_unless(&pn->tx_credits, -1, 0)) {
  		kfree_skb(skb);
  		return -ENOBUFS;
  	}
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1063
  	skb_push(skb, 3 + pn->aligned);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1064
1065
1066
  	skb_reset_transport_header(skb);
  	ph = pnp_hdr(skb);
  	ph->utid = 0;
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1067
1068
  	if (pn->aligned) {
  		ph->message_id = PNS_PIPE_ALIGNED_DATA;
6321aa197   Arnd Bergmann   phonet: fix build...
1069
  		ph->data0 = 0; /* padding */
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1070
1071
  	} else
  		ph->message_id = PNS_PIPE_DATA;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1072
  	ph->pipe_handle = pn->pipe_handle;
14ba8faeb   Rémi Denis-Courmont   Phonet: use socke...
1073
  	err = pn_skb_send(sk, skb, NULL);
e1a5964f0   Rémi Denis-Courmont   Phonet: restore f...
1074
1075
1076
1077
  
  	if (err && pn_flow_safe(pn->tx_fc))
  		atomic_inc(&pn->tx_credits);
  	return err;
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1078
  }
1b7841404   Ying Xue   net: Remove iocb ...
1079
  static int pep_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1080
1081
  {
  	struct pep_sock *pn = pep_sk(sk);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1082
  	struct sk_buff *skb;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1083
1084
1085
  	long timeo;
  	int flags = msg->msg_flags;
  	int err, done;
bcf1b70ac   Sasha Levin   phonet: Check inp...
1086
1087
  	if (len > USHRT_MAX)
  		return -EMSGSIZE;
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1088
1089
1090
  	if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|
  				MSG_CMSG_COMPAT)) ||
  			!(msg->msg_flags & MSG_EOR))
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1091
  		return -EOPNOTSUPP;
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1092
1093
1094
  	skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
  					flags & MSG_DONTWAIT, &err);
  	if (!skb)
02ac3268a   Rémi Denis-Courmont   Phonet: correct s...
1095
  		return err;
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1096

638be3445   Rémi Denis-Courmont   Phonet: fix align...
1097
  	skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned);
6ce8e9ce5   Al Viro   new helper: memcp...
1098
  	err = memcpy_from_msg(skb_put(skb, len), msg, len);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1099
1100
  	if (err < 0)
  		goto outfree;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
  	lock_sock(sk);
  	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
  	if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
  		err = -ENOTCONN;
  		goto out;
  	}
  	if (sk->sk_state != TCP_ESTABLISHED) {
  		/* Wait until the pipe gets to enabled state */
  disabled:
  		err = sk_stream_wait_connect(sk, &timeo);
  		if (err)
  			goto out;
  
  		if (sk->sk_state == TCP_CLOSE_WAIT) {
  			err = -ECONNRESET;
  			goto out;
  		}
  	}
  	BUG_ON(sk->sk_state != TCP_ESTABLISHED);
  
  	/* Wait until flow control allows TX */
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1122
  	done = atomic_read(&pn->tx_credits);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1123
  	while (!done) {
d9dc8b0f8   WANG Cong   net: fix sleeping...
1124
  		DEFINE_WAIT_FUNC(wait, woken_wake_function);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1125
1126
1127
1128
1129
1130
1131
1132
1133
  
  		if (!timeo) {
  			err = -EAGAIN;
  			goto out;
  		}
  		if (signal_pending(current)) {
  			err = sock_intr_errno(timeo);
  			goto out;
  		}
d9dc8b0f8   WANG Cong   net: fix sleeping...
1134
1135
1136
  		add_wait_queue(sk_sleep(sk), &wait);
  		done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits), &wait);
  		remove_wait_queue(sk_sleep(sk), &wait);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1137
1138
1139
1140
  
  		if (sk->sk_state != TCP_ESTABLISHED)
  			goto disabled;
  	}
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1141
  	err = pipe_skb_send(sk, skb);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1142
1143
1144
1145
1146
  	if (err >= 0)
  		err = len; /* success! */
  	skb = NULL;
  out:
  	release_sock(sk);
b1704374f   Rémi Denis-Courmont   Phonet: allocate ...
1147
  outfree:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1148
1149
1150
  	kfree_skb(skb);
  	return err;
  }
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1151
1152
1153
  int pep_writeable(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
be677730a   Rémi Denis-Courmont   Phonet: use atomi...
1154
  	return atomic_read(&pn->tx_credits);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1155
1156
1157
1158
1159
1160
  }
  
  int pep_write(struct sock *sk, struct sk_buff *skb)
  {
  	struct sk_buff *rskb, *fs;
  	int flen = 0;
fea93ecef   Rémi Denis-Courmont   Phonet: zero-copy...
1161
1162
  	if (pep_sk(sk)->aligned)
  		return pipe_skb_send(sk, skb);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
  	rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
  	if (!rskb) {
  		kfree_skb(skb);
  		return -ENOMEM;
  	}
  	skb_shinfo(rskb)->frag_list = skb;
  	rskb->len += skb->len;
  	rskb->data_len += rskb->len;
  	rskb->truesize += rskb->len;
  
  	/* Avoid nested fragments */
5c313e9a7   David S. Miller   phonet: Use frag ...
1174
  	skb_walk_frags(skb, fs)
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1175
1176
  		flen += fs->len;
  	skb->next = skb_shinfo(skb)->frag_list;
5c313e9a7   David S. Miller   phonet: Use frag ...
1177
  	skb_frag_list_init(skb);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
  	skb->len -= flen;
  	skb->data_len -= flen;
  	skb->truesize -= flen;
  
  	skb_reserve(rskb, MAX_PHONET_HEADER + 3);
  	return pipe_skb_send(sk, rskb);
  }
  
  struct sk_buff *pep_read(struct sock *sk)
  {
  	struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
  
  	if (sk->sk_state == TCP_ESTABLISHED)
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
1191
  		pipe_grant_credits(sk, GFP_ATOMIC);
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1192
1193
  	return skb;
  }
1b7841404   Ying Xue   net: Remove iocb ...
1194
1195
  static int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
  		       int noblock, int flags, int *addr_len)
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1196
1197
1198
  {
  	struct sk_buff *skb;
  	int err;
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1199
1200
1201
  	if (flags & ~(MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|MSG_WAITALL|
  			MSG_NOSIGNAL|MSG_CMSG_COMPAT))
  		return -EOPNOTSUPP;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1202
1203
  	if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
  		return -ENOTCONN;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1204
1205
1206
  	if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) {
  		/* Dequeue and acknowledge control request */
  		struct pep_sock *pn = pep_sk(sk);
82ecbcb9c   Rémi Denis-Courmont   Phonet: reject un...
1207
1208
  		if (flags & MSG_PEEK)
  			return -EOPNOTSUPP;
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  		skb = skb_dequeue(&pn->ctrlreq_queue);
  		if (skb) {
  			pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR,
  						GFP_KERNEL);
  			msg->msg_flags |= MSG_OOB;
  			goto copy;
  		}
  		if (flags & MSG_OOB)
  			return -EINVAL;
  	}
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
  	skb = skb_recv_datagram(sk, flags, noblock, &err);
  	lock_sock(sk);
  	if (skb == NULL) {
  		if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
  			err = -ECONNRESET;
  		release_sock(sk);
  		return err;
  	}
  
  	if (sk->sk_state == TCP_ESTABLISHED)
44c9ab16d   Rémi Denis-Courmont   Phonet: factor co...
1229
  		pipe_grant_credits(sk, GFP_KERNEL);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1230
  	release_sock(sk);
c41bd97f8   Rémi Denis-Courmont   Phonet: receive p...
1231
  copy:
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1232
  	msg->msg_flags |= MSG_EOR;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1233
1234
1235
1236
  	if (skb->len > len)
  		msg->msg_flags |= MSG_TRUNC;
  	else
  		len = skb->len;
51f3d02b9   David S. Miller   net: Add and use ...
1237
  	err = skb_copy_datagram_msg(skb, 0, msg, len);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
  	if (!err)
  		err = (flags & MSG_TRUNC) ? skb->len : len;
  
  	skb_free_datagram(sk, skb);
  	return err;
  }
  
  static void pep_sock_unhash(struct sock *sk)
  {
  	struct pep_sock *pn = pep_sk(sk);
  	struct sock *skparent = NULL;
  
  	lock_sock(sk);
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
1251

297edb600   Rémi Denis-Courmont   Phonet: support a...
1252
  	if (pn->listener != NULL) {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1253
  		skparent = pn->listener;
297edb600   Rémi Denis-Courmont   Phonet: support a...
1254
  		pn->listener = NULL;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1255
  		release_sock(sk);
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1256
  		pn = pep_sk(skparent);
7dfde179c   Rémi Denis-Courmont   Phonet: listening...
1257
1258
1259
  		lock_sock(skparent);
  		sk_del_node_init(sk);
  		sk = skparent;
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1260
  	}
297edb600   Rémi Denis-Courmont   Phonet: support a...
1261

9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
  	/* Unhash a listening sock only when it is closed
  	 * and all of its active connected pipes are closed. */
  	if (hlist_empty(&pn->hlist))
  		pn_sock_unhash(&pn->pn_sk.sk);
  	release_sock(sk);
  
  	if (skparent)
  		sock_put(skparent);
  }
  
  static struct proto pep_proto = {
  	.close		= pep_sock_close,
  	.accept		= pep_sock_accept,
b3d625538   Kumar Sanghvi   Phonet: 'connect'...
1275
  	.connect	= pep_sock_connect,
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1276
1277
  	.ioctl		= pep_ioctl,
  	.init		= pep_init,
02a47617c   Rémi Denis-Courmont   Phonet: implement...
1278
1279
  	.setsockopt	= pep_setsockopt,
  	.getsockopt	= pep_getsockopt,
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
  	.sendmsg	= pep_sendmsg,
  	.recvmsg	= pep_recvmsg,
  	.backlog_rcv	= pep_do_rcv,
  	.hash		= pn_sock_hash,
  	.unhash		= pep_sock_unhash,
  	.get_port	= pn_sock_get_port,
  	.obj_size	= sizeof(struct pep_sock),
  	.owner		= THIS_MODULE,
  	.name		= "PNPIPE",
  };
548ec1147   Lin Zhang   net: phonet: mark...
1290
  static const struct phonet_protocol pep_pn_proto = {
9641458d3   Rémi Denis-Courmont   Phonet: Pipe End ...
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
  	.ops		= &phonet_stream_ops,
  	.prot		= &pep_proto,
  	.sock_type	= SOCK_SEQPACKET,
  };
  
  static int __init pep_register(void)
  {
  	return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
  }
  
  static void __exit pep_unregister(void)
  {
  	phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
  }
  
  module_init(pep_register);
  module_exit(pep_unregister);
  MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
  MODULE_DESCRIPTION("Phonet pipe protocol");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE);