diff options
Diffstat (limited to 'net/phonet/pep.c')
-rw-r--r-- | net/phonet/pep.c | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/net/phonet/pep.c b/net/phonet/pep.c new file mode 100644 index 000000000000..c5dfecb207d2 --- /dev/null +++ b/net/phonet/pep.c | |||
@@ -0,0 +1,908 @@ | |||
1 | /* | ||
2 | * File: pep.c | ||
3 | * | ||
4 | * Phonet pipe protocol end point socket | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Author: RĂ©mi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
22 | * 02110-1301 USA | ||
23 | */ | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/socket.h> | ||
27 | #include <net/sock.h> | ||
28 | #include <net/tcp_states.h> | ||
29 | #include <asm/ioctls.h> | ||
30 | |||
31 | #include <linux/phonet.h> | ||
32 | #include <net/phonet/phonet.h> | ||
33 | #include <net/phonet/pep.h> | ||
34 | |||
35 | /* sk_state values: | ||
36 | * TCP_CLOSE sock not in use yet | ||
37 | * TCP_CLOSE_WAIT disconnected pipe | ||
38 | * TCP_LISTEN listening pipe endpoint | ||
39 | * TCP_SYN_RECV connected pipe in disabled state | ||
40 | * TCP_ESTABLISHED connected pipe in enabled state | ||
41 | * | ||
42 | * pep_sock locking: | ||
43 | * - sk_state, ackq, hlist: sock lock needed | ||
44 | * - listener: read only | ||
45 | * - pipe_handle: read only | ||
46 | */ | ||
47 | |||
48 | #define CREDITS_MAX 10 | ||
49 | #define CREDITS_THR 7 | ||
50 | |||
51 | static const struct sockaddr_pn pipe_srv = { | ||
52 | .spn_family = AF_PHONET, | ||
53 | .spn_resource = 0xD9, /* pipe service */ | ||
54 | }; | ||
55 | |||
56 | #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ | ||
57 | |||
58 | /* Get the next TLV sub-block. */ | ||
59 | static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, | ||
60 | void *buf) | ||
61 | { | ||
62 | void *data = NULL; | ||
63 | struct { | ||
64 | u8 sb_type; | ||
65 | u8 sb_len; | ||
66 | } *ph, h; | ||
67 | int buflen = *plen; | ||
68 | |||
69 | ph = skb_header_pointer(skb, 0, 2, &h); | ||
70 | if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) | ||
71 | return NULL; | ||
72 | ph->sb_len -= 2; | ||
73 | *ptype = ph->sb_type; | ||
74 | *plen = ph->sb_len; | ||
75 | |||
76 | if (buflen > ph->sb_len) | ||
77 | buflen = ph->sb_len; | ||
78 | data = skb_header_pointer(skb, 2, buflen, buf); | ||
79 | __skb_pull(skb, 2 + ph->sb_len); | ||
80 | return data; | ||
81 | } | ||
82 | |||
83 | static int pep_reply(struct sock *sk, struct sk_buff *oskb, | ||
84 | u8 code, const void *data, int len, gfp_t priority) | ||
85 | { | ||
86 | const struct pnpipehdr *oph = pnp_hdr(oskb); | ||
87 | struct pnpipehdr *ph; | ||
88 | struct sk_buff *skb; | ||
89 | |||
90 | skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); | ||
91 | if (!skb) | ||
92 | return -ENOMEM; | ||
93 | skb_set_owner_w(skb, sk); | ||
94 | |||
95 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
96 | __skb_put(skb, len); | ||
97 | skb_copy_to_linear_data(skb, data, len); | ||
98 | __skb_push(skb, sizeof(*ph)); | ||
99 | skb_reset_transport_header(skb); | ||
100 | ph = pnp_hdr(skb); | ||
101 | ph->utid = oph->utid; | ||
102 | ph->message_id = oph->message_id + 1; /* REQ -> RESP */ | ||
103 | ph->pipe_handle = oph->pipe_handle; | ||
104 | ph->error_code = code; | ||
105 | |||
106 | return pn_skb_send(sk, skb, &pipe_srv); | ||
107 | } | ||
108 | |||
109 | #define PAD 0x00 | ||
110 | static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) | ||
111 | { | ||
112 | static const u8 data[20] = { | ||
113 | PAD, PAD, PAD, 2 /* sub-blocks */, | ||
114 | PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD, | ||
115 | PN_MULTI_CREDIT_FLOW_CONTROL, | ||
116 | PN_ONE_CREDIT_FLOW_CONTROL, | ||
117 | PN_LEGACY_FLOW_CONTROL, | ||
118 | PAD, | ||
119 | PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD, | ||
120 | PN_MULTI_CREDIT_FLOW_CONTROL, | ||
121 | PN_ONE_CREDIT_FLOW_CONTROL, | ||
122 | PN_LEGACY_FLOW_CONTROL, | ||
123 | PAD, | ||
124 | }; | ||
125 | |||
126 | might_sleep(); | ||
127 | return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), | ||
128 | GFP_KERNEL); | ||
129 | } | ||
130 | |||
131 | static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) | ||
132 | { | ||
133 | static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; | ||
134 | WARN_ON(code == PN_PIPE_NO_ERROR); | ||
135 | return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); | ||
136 | } | ||
137 | |||
138 | /* Control requests are not sent by the pipe service and have a specific | ||
139 | * message format. */ | ||
140 | static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code) | ||
141 | { | ||
142 | const struct pnpipehdr *oph = pnp_hdr(oskb); | ||
143 | struct sk_buff *skb; | ||
144 | struct pnpipehdr *ph; | ||
145 | struct sockaddr_pn dst; | ||
146 | |||
147 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC); | ||
148 | if (!skb) | ||
149 | return -ENOMEM; | ||
150 | skb_set_owner_w(skb, sk); | ||
151 | |||
152 | skb_reserve(skb, MAX_PHONET_HEADER); | ||
153 | ph = (struct pnpipehdr *)skb_put(skb, sizeof(*ph) + 4); | ||
154 | |||
155 | ph->utid = oph->utid; | ||
156 | ph->message_id = PNS_PEP_CTRL_RESP; | ||
157 | ph->pipe_handle = oph->pipe_handle; | ||
158 | ph->data[0] = oph->data[1]; /* CTRL id */ | ||
159 | ph->data[1] = oph->data[0]; /* PEP type */ | ||
160 | ph->data[2] = code; /* error code, at an usual offset */ | ||
161 | ph->data[3] = PAD; | ||
162 | ph->data[4] = PAD; | ||
163 | |||
164 | pn_skb_get_src_sockaddr(oskb, &dst); | ||
165 | return pn_skb_send(sk, skb, &dst); | ||
166 | } | ||
167 | |||
168 | static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) | ||
169 | { | ||
170 | struct pep_sock *pn = pep_sk(sk); | ||
171 | struct pnpipehdr *ph; | ||
172 | struct sk_buff *skb; | ||
173 | |||
174 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); | ||
175 | if (!skb) | ||
176 | return -ENOMEM; | ||
177 | skb_set_owner_w(skb, sk); | ||
178 | |||
179 | skb_reserve(skb, MAX_PNPIPE_HEADER + 4); | ||
180 | __skb_push(skb, sizeof(*ph) + 4); | ||
181 | skb_reset_transport_header(skb); | ||
182 | ph = pnp_hdr(skb); | ||
183 | ph->utid = 0; | ||
184 | ph->message_id = PNS_PEP_STATUS_IND; | ||
185 | ph->pipe_handle = pn->pipe_handle; | ||
186 | ph->pep_type = PN_PEP_TYPE_COMMON; | ||
187 | ph->data[1] = type; | ||
188 | ph->data[2] = PAD; | ||
189 | ph->data[3] = PAD; | ||
190 | ph->data[4] = status; | ||
191 | |||
192 | return pn_skb_send(sk, skb, &pipe_srv); | ||
193 | } | ||
194 | |||
195 | /* Send our RX flow control information to the sender. | ||
196 | * Socket must be locked. */ | ||
197 | static void pipe_grant_credits(struct sock *sk) | ||
198 | { | ||
199 | struct pep_sock *pn = pep_sk(sk); | ||
200 | |||
201 | BUG_ON(sk->sk_state != TCP_ESTABLISHED); | ||
202 | |||
203 | switch (pn->rx_fc) { | ||
204 | case PN_LEGACY_FLOW_CONTROL: /* TODO */ | ||
205 | break; | ||
206 | case PN_ONE_CREDIT_FLOW_CONTROL: | ||
207 | pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, | ||
208 | PEP_IND_READY, GFP_ATOMIC); | ||
209 | pn->rx_credits = 1; | ||
210 | break; | ||
211 | case PN_MULTI_CREDIT_FLOW_CONTROL: | ||
212 | if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX) | ||
213 | break; | ||
214 | if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS, | ||
215 | CREDITS_MAX - pn->rx_credits, | ||
216 | GFP_ATOMIC) == 0) | ||
217 | pn->rx_credits = CREDITS_MAX; | ||
218 | break; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) | ||
223 | { | ||
224 | struct pep_sock *pn = pep_sk(sk); | ||
225 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
226 | |||
227 | if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) | ||
228 | return -EINVAL; | ||
229 | |||
230 | if (hdr->data[0] != PN_PEP_TYPE_COMMON) { | ||
231 | LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", | ||
232 | (unsigned)hdr->data[0]); | ||
233 | return -EOPNOTSUPP; | ||
234 | } | ||
235 | |||
236 | switch (hdr->data[1]) { | ||
237 | case PN_PEP_IND_FLOW_CONTROL: | ||
238 | switch (pn->tx_fc) { | ||
239 | case PN_LEGACY_FLOW_CONTROL: | ||
240 | switch (hdr->data[4]) { | ||
241 | case PEP_IND_BUSY: | ||
242 | pn->tx_credits = 0; | ||
243 | break; | ||
244 | case PEP_IND_READY: | ||
245 | pn->tx_credits = 1; | ||
246 | break; | ||
247 | } | ||
248 | break; | ||
249 | case PN_ONE_CREDIT_FLOW_CONTROL: | ||
250 | if (hdr->data[4] == PEP_IND_READY) | ||
251 | pn->tx_credits = 1; | ||
252 | break; | ||
253 | } | ||
254 | break; | ||
255 | |||
256 | case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: | ||
257 | if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) | ||
258 | break; | ||
259 | if (pn->tx_credits + hdr->data[4] > 0xff) | ||
260 | pn->tx_credits = 0xff; | ||
261 | else | ||
262 | pn->tx_credits += hdr->data[4]; | ||
263 | break; | ||
264 | |||
265 | default: | ||
266 | LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", | ||
267 | (unsigned)hdr->data[1]); | ||
268 | return -EOPNOTSUPP; | ||
269 | } | ||
270 | if (pn->tx_credits) | ||
271 | sk->sk_write_space(sk); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb) | ||
276 | { | ||
277 | struct pep_sock *pn = pep_sk(sk); | ||
278 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
279 | u8 n_sb = hdr->data[0]; | ||
280 | |||
281 | pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
282 | __skb_pull(skb, sizeof(*hdr)); | ||
283 | while (n_sb > 0) { | ||
284 | u8 type, buf[2], len = sizeof(buf); | ||
285 | u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
286 | |||
287 | if (data == NULL) | ||
288 | return -EINVAL; | ||
289 | switch (type) { | ||
290 | case PN_PIPE_SB_NEGOTIATED_FC: | ||
291 | if (len < 2 || (data[0] | data[1]) > 3) | ||
292 | break; | ||
293 | pn->tx_fc = data[0] & 3; | ||
294 | pn->rx_fc = data[1] & 3; | ||
295 | break; | ||
296 | } | ||
297 | n_sb--; | ||
298 | } | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* Queue an skb to a connected sock. | ||
303 | * Socket lock must be held. */ | ||
304 | static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | ||
305 | { | ||
306 | struct pep_sock *pn = pep_sk(sk); | ||
307 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
308 | int err = 0; | ||
309 | |||
310 | BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); | ||
311 | |||
312 | switch (hdr->message_id) { | ||
313 | case PNS_PEP_CONNECT_REQ: | ||
314 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | ||
315 | break; | ||
316 | |||
317 | case PNS_PEP_DISCONNECT_REQ: | ||
318 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
319 | sk->sk_state = TCP_CLOSE_WAIT; | ||
320 | if (!sock_flag(sk, SOCK_DEAD)) | ||
321 | sk->sk_state_change(sk); | ||
322 | break; | ||
323 | |||
324 | case PNS_PEP_ENABLE_REQ: | ||
325 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ | ||
326 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
327 | break; | ||
328 | |||
329 | case PNS_PEP_RESET_REQ: | ||
330 | switch (hdr->state_after_reset) { | ||
331 | case PN_PIPE_DISABLE: | ||
332 | pn->init_enable = 0; | ||
333 | break; | ||
334 | case PN_PIPE_ENABLE: | ||
335 | pn->init_enable = 1; | ||
336 | break; | ||
337 | default: /* not allowed to send an error here!? */ | ||
338 | err = -EINVAL; | ||
339 | goto out; | ||
340 | } | ||
341 | /* fall through */ | ||
342 | case PNS_PEP_DISABLE_REQ: | ||
343 | pn->tx_credits = 0; | ||
344 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
345 | break; | ||
346 | |||
347 | case PNS_PEP_CTRL_REQ: | ||
348 | /* TODO */ | ||
349 | pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR); | ||
350 | break; | ||
351 | |||
352 | case PNS_PIPE_DATA: | ||
353 | __skb_pull(skb, 3); /* Pipe data header */ | ||
354 | if (!pn_flow_safe(pn->rx_fc)) { | ||
355 | err = sock_queue_rcv_skb(sk, skb); | ||
356 | if (!err) | ||
357 | return 0; | ||
358 | break; | ||
359 | } | ||
360 | |||
361 | if (pn->rx_credits == 0) { | ||
362 | err = -ENOBUFS; | ||
363 | break; | ||
364 | } | ||
365 | pn->rx_credits--; | ||
366 | skb->dev = NULL; | ||
367 | skb_set_owner_r(skb, sk); | ||
368 | err = skb->len; | ||
369 | skb_queue_tail(&sk->sk_receive_queue, skb); | ||
370 | if (!sock_flag(sk, SOCK_DEAD)) | ||
371 | sk->sk_data_ready(sk, err); | ||
372 | return 0; | ||
373 | |||
374 | case PNS_PEP_STATUS_IND: | ||
375 | pipe_rcv_status(sk, skb); | ||
376 | break; | ||
377 | |||
378 | case PNS_PIPE_REDIRECTED_IND: | ||
379 | err = pipe_rcv_created(sk, skb); | ||
380 | break; | ||
381 | |||
382 | case PNS_PIPE_CREATED_IND: | ||
383 | err = pipe_rcv_created(sk, skb); | ||
384 | if (err) | ||
385 | break; | ||
386 | /* fall through */ | ||
387 | case PNS_PIPE_RESET_IND: | ||
388 | if (!pn->init_enable) | ||
389 | break; | ||
390 | /* fall through */ | ||
391 | case PNS_PIPE_ENABLED_IND: | ||
392 | if (!pn_flow_safe(pn->tx_fc)) { | ||
393 | pn->tx_credits = 1; | ||
394 | sk->sk_write_space(sk); | ||
395 | } | ||
396 | if (sk->sk_state == TCP_ESTABLISHED) | ||
397 | break; /* Nothing to do */ | ||
398 | sk->sk_state = TCP_ESTABLISHED; | ||
399 | pipe_grant_credits(sk); | ||
400 | break; | ||
401 | |||
402 | case PNS_PIPE_DISABLED_IND: | ||
403 | sk->sk_state = TCP_SYN_RECV; | ||
404 | pn->rx_credits = 0; | ||
405 | break; | ||
406 | |||
407 | default: | ||
408 | LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n", | ||
409 | hdr->message_id); | ||
410 | err = -EINVAL; | ||
411 | } | ||
412 | out: | ||
413 | kfree_skb(skb); | ||
414 | return err; | ||
415 | } | ||
416 | |||
417 | /* Destroy connected sock. */ | ||
418 | static void pipe_destruct(struct sock *sk) | ||
419 | { | ||
420 | skb_queue_purge(&sk->sk_receive_queue); | ||
421 | } | ||
422 | |||
423 | static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) | ||
424 | { | ||
425 | struct sock *newsk; | ||
426 | struct pep_sock *newpn, *pn = pep_sk(sk); | ||
427 | struct pnpipehdr *hdr; | ||
428 | struct sockaddr_pn dst; | ||
429 | u16 peer_type; | ||
430 | u8 pipe_handle, enabled, n_sb; | ||
431 | |||
432 | if (!pskb_pull(skb, sizeof(*hdr) + 4)) | ||
433 | return -EINVAL; | ||
434 | |||
435 | hdr = pnp_hdr(skb); | ||
436 | pipe_handle = hdr->pipe_handle; | ||
437 | switch (hdr->state_after_connect) { | ||
438 | case PN_PIPE_DISABLE: | ||
439 | enabled = 0; | ||
440 | break; | ||
441 | case PN_PIPE_ENABLE: | ||
442 | enabled = 1; | ||
443 | break; | ||
444 | default: | ||
445 | pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | peer_type = hdr->other_pep_type << 8; | ||
449 | |||
450 | if (unlikely(sk->sk_state != TCP_LISTEN) || sk_acceptq_is_full(sk)) { | ||
451 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | ||
452 | return -ENOBUFS; | ||
453 | } | ||
454 | |||
455 | /* Parse sub-blocks (options) */ | ||
456 | n_sb = hdr->data[4]; | ||
457 | while (n_sb > 0) { | ||
458 | u8 type, buf[1], len = sizeof(buf); | ||
459 | const u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
460 | |||
461 | if (data == NULL) | ||
462 | return -EINVAL; | ||
463 | switch (type) { | ||
464 | case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: | ||
465 | if (len < 1) | ||
466 | return -EINVAL; | ||
467 | peer_type = (peer_type & 0xff00) | data[0]; | ||
468 | break; | ||
469 | } | ||
470 | n_sb--; | ||
471 | } | ||
472 | |||
473 | skb = skb_clone(skb, GFP_ATOMIC); | ||
474 | if (!skb) | ||
475 | return -ENOMEM; | ||
476 | |||
477 | /* Create a new to-be-accepted sock */ | ||
478 | newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); | ||
479 | if (!newsk) { | ||
480 | kfree_skb(skb); | ||
481 | return -ENOMEM; | ||
482 | } | ||
483 | sock_init_data(NULL, newsk); | ||
484 | newsk->sk_state = TCP_SYN_RECV; | ||
485 | newsk->sk_backlog_rcv = pipe_do_rcv; | ||
486 | newsk->sk_protocol = sk->sk_protocol; | ||
487 | newsk->sk_destruct = pipe_destruct; | ||
488 | |||
489 | newpn = pep_sk(newsk); | ||
490 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
491 | newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); | ||
492 | newpn->pn_sk.resource = pn->pn_sk.resource; | ||
493 | newpn->pipe_handle = pipe_handle; | ||
494 | newpn->peer_type = peer_type; | ||
495 | newpn->rx_credits = newpn->tx_credits = 0; | ||
496 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
497 | newpn->init_enable = enabled; | ||
498 | |||
499 | BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); | ||
500 | skb_queue_head(&newsk->sk_receive_queue, skb); | ||
501 | if (!sock_flag(sk, SOCK_DEAD)) | ||
502 | sk->sk_data_ready(sk, 0); | ||
503 | |||
504 | sk_acceptq_added(sk); | ||
505 | sk_add_node(newsk, &pn->ackq); | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | /* Listening sock must be locked */ | ||
510 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, | ||
511 | const struct sockaddr_pn *dst, | ||
512 | u8 pipe_handle) | ||
513 | { | ||
514 | struct hlist_node *node; | ||
515 | struct sock *sknode; | ||
516 | u16 dobj = pn_sockaddr_get_object(dst); | ||
517 | |||
518 | sk_for_each(sknode, node, hlist) { | ||
519 | struct pep_sock *pnnode = pep_sk(sknode); | ||
520 | |||
521 | /* Ports match, but addresses might not: */ | ||
522 | if (pnnode->pn_sk.sobject != dobj) | ||
523 | continue; | ||
524 | if (pnnode->pipe_handle != pipe_handle) | ||
525 | continue; | ||
526 | if (sknode->sk_state == TCP_CLOSE_WAIT) | ||
527 | continue; | ||
528 | |||
529 | sock_hold(sknode); | ||
530 | return sknode; | ||
531 | } | ||
532 | return NULL; | ||
533 | } | ||
534 | |||
535 | /* | ||
536 | * Deliver an skb to a listening sock. | ||
537 | * Socket lock must be held. | ||
538 | * We then queue the skb to the right connected sock (if any). | ||
539 | */ | ||
540 | static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) | ||
541 | { | ||
542 | struct pep_sock *pn = pep_sk(sk); | ||
543 | struct sock *sknode; | ||
544 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
545 | struct sockaddr_pn dst; | ||
546 | int err = NET_RX_SUCCESS; | ||
547 | u8 pipe_handle; | ||
548 | |||
549 | if (!pskb_may_pull(skb, sizeof(*hdr))) | ||
550 | goto drop; | ||
551 | |||
552 | hdr = pnp_hdr(skb); | ||
553 | pipe_handle = hdr->pipe_handle; | ||
554 | if (pipe_handle == PN_PIPE_INVALID_HANDLE) | ||
555 | goto drop; | ||
556 | |||
557 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
558 | |||
559 | /* Look for an existing pipe handle */ | ||
560 | sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); | ||
561 | if (sknode) | ||
562 | return sk_receive_skb(sknode, skb, 1); | ||
563 | |||
564 | /* Look for a pipe handle pending accept */ | ||
565 | sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); | ||
566 | if (sknode) { | ||
567 | sock_put(sknode); | ||
568 | if (net_ratelimit()) | ||
569 | printk(KERN_WARNING"Phonet unconnected PEP ignored"); | ||
570 | err = NET_RX_DROP; | ||
571 | goto drop; | ||
572 | } | ||
573 | |||
574 | switch (hdr->message_id) { | ||
575 | case PNS_PEP_CONNECT_REQ: | ||
576 | err = pep_connreq_rcv(sk, skb); | ||
577 | break; | ||
578 | |||
579 | case PNS_PEP_DISCONNECT_REQ: | ||
580 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
581 | break; | ||
582 | |||
583 | case PNS_PEP_CTRL_REQ: | ||
584 | pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE); | ||
585 | break; | ||
586 | |||
587 | case PNS_PEP_RESET_REQ: | ||
588 | case PNS_PEP_ENABLE_REQ: | ||
589 | case PNS_PEP_DISABLE_REQ: | ||
590 | /* invalid handle is not even allowed here! */ | ||
591 | default: | ||
592 | err = NET_RX_DROP; | ||
593 | } | ||
594 | drop: | ||
595 | kfree_skb(skb); | ||
596 | return err; | ||
597 | } | ||
598 | |||
599 | /* associated socket ceases to exist */ | ||
600 | static void pep_sock_close(struct sock *sk, long timeout) | ||
601 | { | ||
602 | struct pep_sock *pn = pep_sk(sk); | ||
603 | |||
604 | sk_common_release(sk); | ||
605 | |||
606 | lock_sock(sk); | ||
607 | if (sk->sk_state == TCP_LISTEN) { | ||
608 | /* Destroy the listen queue */ | ||
609 | struct sock *sknode; | ||
610 | struct hlist_node *p, *n; | ||
611 | |||
612 | sk_for_each_safe(sknode, p, n, &pn->ackq) | ||
613 | sk_del_node_init(sknode); | ||
614 | sk->sk_state = TCP_CLOSE; | ||
615 | } | ||
616 | release_sock(sk); | ||
617 | } | ||
618 | |||
619 | static int pep_wait_connreq(struct sock *sk, int noblock) | ||
620 | { | ||
621 | struct task_struct *tsk = current; | ||
622 | struct pep_sock *pn = pep_sk(sk); | ||
623 | long timeo = sock_rcvtimeo(sk, noblock); | ||
624 | |||
625 | for (;;) { | ||
626 | DEFINE_WAIT(wait); | ||
627 | |||
628 | if (sk->sk_state != TCP_LISTEN) | ||
629 | return -EINVAL; | ||
630 | if (!hlist_empty(&pn->ackq)) | ||
631 | break; | ||
632 | if (!timeo) | ||
633 | return -EWOULDBLOCK; | ||
634 | if (signal_pending(tsk)) | ||
635 | return sock_intr_errno(timeo); | ||
636 | |||
637 | prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait, | ||
638 | TASK_INTERRUPTIBLE); | ||
639 | release_sock(sk); | ||
640 | timeo = schedule_timeout(timeo); | ||
641 | lock_sock(sk); | ||
642 | finish_wait(&sk->sk_socket->wait, &wait); | ||
643 | } | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) | ||
649 | { | ||
650 | struct pep_sock *pn = pep_sk(sk); | ||
651 | struct sock *newsk = NULL; | ||
652 | struct sk_buff *oskb; | ||
653 | int err; | ||
654 | |||
655 | lock_sock(sk); | ||
656 | err = pep_wait_connreq(sk, flags & O_NONBLOCK); | ||
657 | if (err) | ||
658 | goto out; | ||
659 | |||
660 | newsk = __sk_head(&pn->ackq); | ||
661 | |||
662 | oskb = skb_dequeue(&newsk->sk_receive_queue); | ||
663 | err = pep_accept_conn(newsk, oskb); | ||
664 | if (err) { | ||
665 | skb_queue_head(&newsk->sk_receive_queue, oskb); | ||
666 | newsk = NULL; | ||
667 | goto out; | ||
668 | } | ||
669 | |||
670 | sock_hold(sk); | ||
671 | pep_sk(newsk)->listener = sk; | ||
672 | |||
673 | sock_hold(newsk); | ||
674 | sk_del_node_init(newsk); | ||
675 | sk_acceptq_removed(sk); | ||
676 | sk_add_node(newsk, &pn->hlist); | ||
677 | __sock_put(newsk); | ||
678 | |||
679 | out: | ||
680 | release_sock(sk); | ||
681 | *errp = err; | ||
682 | return newsk; | ||
683 | } | ||
684 | |||
685 | static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) | ||
686 | { | ||
687 | int answ; | ||
688 | |||
689 | switch (cmd) { | ||
690 | case SIOCINQ: | ||
691 | if (sk->sk_state == TCP_LISTEN) | ||
692 | return -EINVAL; | ||
693 | |||
694 | lock_sock(sk); | ||
695 | if (!skb_queue_empty(&sk->sk_receive_queue)) | ||
696 | answ = skb_peek(&sk->sk_receive_queue)->len; | ||
697 | else | ||
698 | answ = 0; | ||
699 | release_sock(sk); | ||
700 | return put_user(answ, (int __user *)arg); | ||
701 | } | ||
702 | |||
703 | return -ENOIOCTLCMD; | ||
704 | } | ||
705 | |||
706 | static int pep_init(struct sock *sk) | ||
707 | { | ||
708 | struct pep_sock *pn = pep_sk(sk); | ||
709 | |||
710 | INIT_HLIST_HEAD(&pn->ackq); | ||
711 | INIT_HLIST_HEAD(&pn->hlist); | ||
712 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, | ||
717 | struct msghdr *msg, size_t len) | ||
718 | { | ||
719 | struct pep_sock *pn = pep_sk(sk); | ||
720 | struct sk_buff *skb = NULL; | ||
721 | struct pnpipehdr *ph; | ||
722 | long timeo; | ||
723 | int flags = msg->msg_flags; | ||
724 | int err, done; | ||
725 | |||
726 | if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR)) | ||
727 | return -EOPNOTSUPP; | ||
728 | |||
729 | lock_sock(sk); | ||
730 | timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); | ||
731 | if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) { | ||
732 | err = -ENOTCONN; | ||
733 | goto out; | ||
734 | } | ||
735 | if (sk->sk_state != TCP_ESTABLISHED) { | ||
736 | /* Wait until the pipe gets to enabled state */ | ||
737 | disabled: | ||
738 | err = sk_stream_wait_connect(sk, &timeo); | ||
739 | if (err) | ||
740 | goto out; | ||
741 | |||
742 | if (sk->sk_state == TCP_CLOSE_WAIT) { | ||
743 | err = -ECONNRESET; | ||
744 | goto out; | ||
745 | } | ||
746 | } | ||
747 | BUG_ON(sk->sk_state != TCP_ESTABLISHED); | ||
748 | |||
749 | /* Wait until flow control allows TX */ | ||
750 | done = pn->tx_credits > 0; | ||
751 | while (!done) { | ||
752 | DEFINE_WAIT(wait); | ||
753 | |||
754 | if (!timeo) { | ||
755 | err = -EAGAIN; | ||
756 | goto out; | ||
757 | } | ||
758 | if (signal_pending(current)) { | ||
759 | err = sock_intr_errno(timeo); | ||
760 | goto out; | ||
761 | } | ||
762 | |||
763 | prepare_to_wait(&sk->sk_socket->wait, &wait, | ||
764 | TASK_INTERRUPTIBLE); | ||
765 | done = sk_wait_event(sk, &timeo, pn->tx_credits > 0); | ||
766 | finish_wait(&sk->sk_socket->wait, &wait); | ||
767 | |||
768 | if (sk->sk_state != TCP_ESTABLISHED) | ||
769 | goto disabled; | ||
770 | } | ||
771 | |||
772 | if (!skb) { | ||
773 | skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, | ||
774 | flags & MSG_DONTWAIT, &err); | ||
775 | if (skb == NULL) | ||
776 | goto out; | ||
777 | skb_reserve(skb, MAX_PHONET_HEADER + 3); | ||
778 | |||
779 | if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits) | ||
780 | goto disabled; /* sock_alloc_send_skb might sleep */ | ||
781 | } | ||
782 | |||
783 | err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); | ||
784 | if (err < 0) | ||
785 | goto out; | ||
786 | |||
787 | __skb_push(skb, 3); | ||
788 | skb_reset_transport_header(skb); | ||
789 | ph = pnp_hdr(skb); | ||
790 | ph->utid = 0; | ||
791 | ph->message_id = PNS_PIPE_DATA; | ||
792 | ph->pipe_handle = pn->pipe_handle; | ||
793 | if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */ | ||
794 | pn->tx_credits--; | ||
795 | |||
796 | err = pn_skb_send(sk, skb, &pipe_srv); | ||
797 | if (err >= 0) | ||
798 | err = len; /* success! */ | ||
799 | skb = NULL; | ||
800 | out: | ||
801 | release_sock(sk); | ||
802 | kfree_skb(skb); | ||
803 | return err; | ||
804 | } | ||
805 | |||
806 | static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, | ||
807 | struct msghdr *msg, size_t len, int noblock, | ||
808 | int flags, int *addr_len) | ||
809 | { | ||
810 | struct sk_buff *skb; | ||
811 | int err; | ||
812 | |||
813 | if (unlikely(flags & MSG_OOB)) | ||
814 | return -EOPNOTSUPP; | ||
815 | if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE))) | ||
816 | return -ENOTCONN; | ||
817 | |||
818 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
819 | lock_sock(sk); | ||
820 | if (skb == NULL) { | ||
821 | if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT) | ||
822 | err = -ECONNRESET; | ||
823 | release_sock(sk); | ||
824 | return err; | ||
825 | } | ||
826 | |||
827 | if (sk->sk_state == TCP_ESTABLISHED) | ||
828 | pipe_grant_credits(sk); | ||
829 | release_sock(sk); | ||
830 | |||
831 | msg->msg_flags |= MSG_EOR; | ||
832 | |||
833 | if (skb->len > len) | ||
834 | msg->msg_flags |= MSG_TRUNC; | ||
835 | else | ||
836 | len = skb->len; | ||
837 | |||
838 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); | ||
839 | if (!err) | ||
840 | err = (flags & MSG_TRUNC) ? skb->len : len; | ||
841 | |||
842 | skb_free_datagram(sk, skb); | ||
843 | return err; | ||
844 | } | ||
845 | |||
846 | static void pep_sock_unhash(struct sock *sk) | ||
847 | { | ||
848 | struct pep_sock *pn = pep_sk(sk); | ||
849 | struct sock *skparent = NULL; | ||
850 | |||
851 | lock_sock(sk); | ||
852 | if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { | ||
853 | skparent = pn->listener; | ||
854 | sk_del_node_init(sk); | ||
855 | release_sock(sk); | ||
856 | |||
857 | sk = skparent; | ||
858 | pn = pep_sk(skparent); | ||
859 | lock_sock(sk); | ||
860 | } | ||
861 | /* Unhash a listening sock only when it is closed | ||
862 | * and all of its active connected pipes are closed. */ | ||
863 | if (hlist_empty(&pn->hlist)) | ||
864 | pn_sock_unhash(&pn->pn_sk.sk); | ||
865 | release_sock(sk); | ||
866 | |||
867 | if (skparent) | ||
868 | sock_put(skparent); | ||
869 | } | ||
870 | |||
871 | static struct proto pep_proto = { | ||
872 | .close = pep_sock_close, | ||
873 | .accept = pep_sock_accept, | ||
874 | .ioctl = pep_ioctl, | ||
875 | .init = pep_init, | ||
876 | .sendmsg = pep_sendmsg, | ||
877 | .recvmsg = pep_recvmsg, | ||
878 | .backlog_rcv = pep_do_rcv, | ||
879 | .hash = pn_sock_hash, | ||
880 | .unhash = pep_sock_unhash, | ||
881 | .get_port = pn_sock_get_port, | ||
882 | .obj_size = sizeof(struct pep_sock), | ||
883 | .owner = THIS_MODULE, | ||
884 | .name = "PNPIPE", | ||
885 | }; | ||
886 | |||
887 | static struct phonet_protocol pep_pn_proto = { | ||
888 | .ops = &phonet_stream_ops, | ||
889 | .prot = &pep_proto, | ||
890 | .sock_type = SOCK_SEQPACKET, | ||
891 | }; | ||
892 | |||
893 | static int __init pep_register(void) | ||
894 | { | ||
895 | return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); | ||
896 | } | ||
897 | |||
898 | static void __exit pep_unregister(void) | ||
899 | { | ||
900 | phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto); | ||
901 | } | ||
902 | |||
903 | module_init(pep_register); | ||
904 | module_exit(pep_unregister); | ||
905 | MODULE_AUTHOR("Remi Denis-Courmont, Nokia"); | ||
906 | MODULE_DESCRIPTION("Phonet pipe protocol"); | ||
907 | MODULE_LICENSE("GPL"); | ||
908 | MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE); | ||