diff options
Diffstat (limited to 'net/phonet/pep.c')
-rw-r--r-- | net/phonet/pep.c | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/net/phonet/pep.c b/net/phonet/pep.c new file mode 100644 index 000000000000..bc6d50f83249 --- /dev/null +++ b/net/phonet/pep.c | |||
@@ -0,0 +1,1076 @@ | |||
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 | #include <net/phonet/gprs.h> | ||
35 | |||
36 | /* sk_state values: | ||
37 | * TCP_CLOSE sock not in use yet | ||
38 | * TCP_CLOSE_WAIT disconnected pipe | ||
39 | * TCP_LISTEN listening pipe endpoint | ||
40 | * TCP_SYN_RECV connected pipe in disabled state | ||
41 | * TCP_ESTABLISHED connected pipe in enabled state | ||
42 | * | ||
43 | * pep_sock locking: | ||
44 | * - sk_state, ackq, hlist: sock lock needed | ||
45 | * - listener: read only | ||
46 | * - pipe_handle: read only | ||
47 | */ | ||
48 | |||
49 | #define CREDITS_MAX 10 | ||
50 | #define CREDITS_THR 7 | ||
51 | |||
52 | static const struct sockaddr_pn pipe_srv = { | ||
53 | .spn_family = AF_PHONET, | ||
54 | .spn_resource = 0xD9, /* pipe service */ | ||
55 | }; | ||
56 | |||
57 | #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ | ||
58 | |||
59 | /* Get the next TLV sub-block. */ | ||
60 | static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, | ||
61 | void *buf) | ||
62 | { | ||
63 | void *data = NULL; | ||
64 | struct { | ||
65 | u8 sb_type; | ||
66 | u8 sb_len; | ||
67 | } *ph, h; | ||
68 | int buflen = *plen; | ||
69 | |||
70 | ph = skb_header_pointer(skb, 0, 2, &h); | ||
71 | if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) | ||
72 | return NULL; | ||
73 | ph->sb_len -= 2; | ||
74 | *ptype = ph->sb_type; | ||
75 | *plen = ph->sb_len; | ||
76 | |||
77 | if (buflen > ph->sb_len) | ||
78 | buflen = ph->sb_len; | ||
79 | data = skb_header_pointer(skb, 2, buflen, buf); | ||
80 | __skb_pull(skb, 2 + ph->sb_len); | ||
81 | return data; | ||
82 | } | ||
83 | |||
84 | static int pep_reply(struct sock *sk, struct sk_buff *oskb, | ||
85 | u8 code, const void *data, int len, gfp_t priority) | ||
86 | { | ||
87 | const struct pnpipehdr *oph = pnp_hdr(oskb); | ||
88 | struct pnpipehdr *ph; | ||
89 | struct sk_buff *skb; | ||
90 | |||
91 | skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); | ||
92 | if (!skb) | ||
93 | return -ENOMEM; | ||
94 | skb_set_owner_w(skb, sk); | ||
95 | |||
96 | skb_reserve(skb, MAX_PNPIPE_HEADER); | ||
97 | __skb_put(skb, len); | ||
98 | skb_copy_to_linear_data(skb, data, len); | ||
99 | __skb_push(skb, sizeof(*ph)); | ||
100 | skb_reset_transport_header(skb); | ||
101 | ph = pnp_hdr(skb); | ||
102 | ph->utid = oph->utid; | ||
103 | ph->message_id = oph->message_id + 1; /* REQ -> RESP */ | ||
104 | ph->pipe_handle = oph->pipe_handle; | ||
105 | ph->error_code = code; | ||
106 | |||
107 | return pn_skb_send(sk, skb, &pipe_srv); | ||
108 | } | ||
109 | |||
110 | #define PAD 0x00 | ||
111 | static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) | ||
112 | { | ||
113 | static const u8 data[20] = { | ||
114 | PAD, PAD, PAD, 2 /* sub-blocks */, | ||
115 | PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD, | ||
116 | PN_MULTI_CREDIT_FLOW_CONTROL, | ||
117 | PN_ONE_CREDIT_FLOW_CONTROL, | ||
118 | PN_LEGACY_FLOW_CONTROL, | ||
119 | PAD, | ||
120 | PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD, | ||
121 | PN_MULTI_CREDIT_FLOW_CONTROL, | ||
122 | PN_ONE_CREDIT_FLOW_CONTROL, | ||
123 | PN_LEGACY_FLOW_CONTROL, | ||
124 | PAD, | ||
125 | }; | ||
126 | |||
127 | might_sleep(); | ||
128 | return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), | ||
129 | GFP_KERNEL); | ||
130 | } | ||
131 | |||
132 | static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) | ||
133 | { | ||
134 | static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; | ||
135 | WARN_ON(code == PN_PIPE_NO_ERROR); | ||
136 | return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); | ||
137 | } | ||
138 | |||
139 | /* Control requests are not sent by the pipe service and have a specific | ||
140 | * message format. */ | ||
141 | static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code, | ||
142 | gfp_t priority) | ||
143 | { | ||
144 | const struct pnpipehdr *oph = pnp_hdr(oskb); | ||
145 | struct sk_buff *skb; | ||
146 | struct pnpipehdr *ph; | ||
147 | struct sockaddr_pn dst; | ||
148 | |||
149 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); | ||
150 | if (!skb) | ||
151 | return -ENOMEM; | ||
152 | skb_set_owner_w(skb, sk); | ||
153 | |||
154 | skb_reserve(skb, MAX_PHONET_HEADER); | ||
155 | ph = (struct pnpipehdr *)skb_put(skb, sizeof(*ph) + 4); | ||
156 | |||
157 | ph->utid = oph->utid; | ||
158 | ph->message_id = PNS_PEP_CTRL_RESP; | ||
159 | ph->pipe_handle = oph->pipe_handle; | ||
160 | ph->data[0] = oph->data[1]; /* CTRL id */ | ||
161 | ph->data[1] = oph->data[0]; /* PEP type */ | ||
162 | ph->data[2] = code; /* error code, at an usual offset */ | ||
163 | ph->data[3] = PAD; | ||
164 | ph->data[4] = PAD; | ||
165 | |||
166 | pn_skb_get_src_sockaddr(oskb, &dst); | ||
167 | return pn_skb_send(sk, skb, &dst); | ||
168 | } | ||
169 | |||
170 | static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) | ||
171 | { | ||
172 | struct pep_sock *pn = pep_sk(sk); | ||
173 | struct pnpipehdr *ph; | ||
174 | struct sk_buff *skb; | ||
175 | |||
176 | skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); | ||
177 | if (!skb) | ||
178 | return -ENOMEM; | ||
179 | skb_set_owner_w(skb, sk); | ||
180 | |||
181 | skb_reserve(skb, MAX_PNPIPE_HEADER + 4); | ||
182 | __skb_push(skb, sizeof(*ph) + 4); | ||
183 | skb_reset_transport_header(skb); | ||
184 | ph = pnp_hdr(skb); | ||
185 | ph->utid = 0; | ||
186 | ph->message_id = PNS_PEP_STATUS_IND; | ||
187 | ph->pipe_handle = pn->pipe_handle; | ||
188 | ph->pep_type = PN_PEP_TYPE_COMMON; | ||
189 | ph->data[1] = type; | ||
190 | ph->data[2] = PAD; | ||
191 | ph->data[3] = PAD; | ||
192 | ph->data[4] = status; | ||
193 | |||
194 | return pn_skb_send(sk, skb, &pipe_srv); | ||
195 | } | ||
196 | |||
197 | /* Send our RX flow control information to the sender. | ||
198 | * Socket must be locked. */ | ||
199 | static void pipe_grant_credits(struct sock *sk) | ||
200 | { | ||
201 | struct pep_sock *pn = pep_sk(sk); | ||
202 | |||
203 | BUG_ON(sk->sk_state != TCP_ESTABLISHED); | ||
204 | |||
205 | switch (pn->rx_fc) { | ||
206 | case PN_LEGACY_FLOW_CONTROL: /* TODO */ | ||
207 | break; | ||
208 | case PN_ONE_CREDIT_FLOW_CONTROL: | ||
209 | pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, | ||
210 | PEP_IND_READY, GFP_ATOMIC); | ||
211 | pn->rx_credits = 1; | ||
212 | break; | ||
213 | case PN_MULTI_CREDIT_FLOW_CONTROL: | ||
214 | if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX) | ||
215 | break; | ||
216 | if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS, | ||
217 | CREDITS_MAX - pn->rx_credits, | ||
218 | GFP_ATOMIC) == 0) | ||
219 | pn->rx_credits = CREDITS_MAX; | ||
220 | break; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) | ||
225 | { | ||
226 | struct pep_sock *pn = pep_sk(sk); | ||
227 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
228 | |||
229 | if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) | ||
230 | return -EINVAL; | ||
231 | |||
232 | if (hdr->data[0] != PN_PEP_TYPE_COMMON) { | ||
233 | LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", | ||
234 | (unsigned)hdr->data[0]); | ||
235 | return -EOPNOTSUPP; | ||
236 | } | ||
237 | |||
238 | switch (hdr->data[1]) { | ||
239 | case PN_PEP_IND_FLOW_CONTROL: | ||
240 | switch (pn->tx_fc) { | ||
241 | case PN_LEGACY_FLOW_CONTROL: | ||
242 | switch (hdr->data[4]) { | ||
243 | case PEP_IND_BUSY: | ||
244 | pn->tx_credits = 0; | ||
245 | break; | ||
246 | case PEP_IND_READY: | ||
247 | pn->tx_credits = 1; | ||
248 | break; | ||
249 | } | ||
250 | break; | ||
251 | case PN_ONE_CREDIT_FLOW_CONTROL: | ||
252 | if (hdr->data[4] == PEP_IND_READY) | ||
253 | pn->tx_credits = 1; | ||
254 | break; | ||
255 | } | ||
256 | break; | ||
257 | |||
258 | case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: | ||
259 | if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) | ||
260 | break; | ||
261 | if (pn->tx_credits + hdr->data[4] > 0xff) | ||
262 | pn->tx_credits = 0xff; | ||
263 | else | ||
264 | pn->tx_credits += hdr->data[4]; | ||
265 | break; | ||
266 | |||
267 | default: | ||
268 | LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", | ||
269 | (unsigned)hdr->data[1]); | ||
270 | return -EOPNOTSUPP; | ||
271 | } | ||
272 | if (pn->tx_credits) | ||
273 | sk->sk_write_space(sk); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb) | ||
278 | { | ||
279 | struct pep_sock *pn = pep_sk(sk); | ||
280 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
281 | u8 n_sb = hdr->data[0]; | ||
282 | |||
283 | pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
284 | __skb_pull(skb, sizeof(*hdr)); | ||
285 | while (n_sb > 0) { | ||
286 | u8 type, buf[2], len = sizeof(buf); | ||
287 | u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
288 | |||
289 | if (data == NULL) | ||
290 | return -EINVAL; | ||
291 | switch (type) { | ||
292 | case PN_PIPE_SB_NEGOTIATED_FC: | ||
293 | if (len < 2 || (data[0] | data[1]) > 3) | ||
294 | break; | ||
295 | pn->tx_fc = data[0] & 3; | ||
296 | pn->rx_fc = data[1] & 3; | ||
297 | break; | ||
298 | } | ||
299 | n_sb--; | ||
300 | } | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | /* Queue an skb to a connected sock. | ||
305 | * Socket lock must be held. */ | ||
306 | static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | ||
307 | { | ||
308 | struct pep_sock *pn = pep_sk(sk); | ||
309 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
310 | struct sk_buff_head *queue; | ||
311 | int err = 0; | ||
312 | |||
313 | BUG_ON(sk->sk_state == TCP_CLOSE_WAIT); | ||
314 | |||
315 | switch (hdr->message_id) { | ||
316 | case PNS_PEP_CONNECT_REQ: | ||
317 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | ||
318 | break; | ||
319 | |||
320 | case PNS_PEP_DISCONNECT_REQ: | ||
321 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
322 | sk->sk_state = TCP_CLOSE_WAIT; | ||
323 | if (!sock_flag(sk, SOCK_DEAD)) | ||
324 | sk->sk_state_change(sk); | ||
325 | break; | ||
326 | |||
327 | case PNS_PEP_ENABLE_REQ: | ||
328 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ | ||
329 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
330 | break; | ||
331 | |||
332 | case PNS_PEP_RESET_REQ: | ||
333 | switch (hdr->state_after_reset) { | ||
334 | case PN_PIPE_DISABLE: | ||
335 | pn->init_enable = 0; | ||
336 | break; | ||
337 | case PN_PIPE_ENABLE: | ||
338 | pn->init_enable = 1; | ||
339 | break; | ||
340 | default: /* not allowed to send an error here!? */ | ||
341 | err = -EINVAL; | ||
342 | goto out; | ||
343 | } | ||
344 | /* fall through */ | ||
345 | case PNS_PEP_DISABLE_REQ: | ||
346 | pn->tx_credits = 0; | ||
347 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
348 | break; | ||
349 | |||
350 | case PNS_PEP_CTRL_REQ: | ||
351 | if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) | ||
352 | break; | ||
353 | __skb_pull(skb, 4); | ||
354 | queue = &pn->ctrlreq_queue; | ||
355 | goto queue; | ||
356 | |||
357 | case PNS_PIPE_DATA: | ||
358 | __skb_pull(skb, 3); /* Pipe data header */ | ||
359 | if (!pn_flow_safe(pn->rx_fc)) { | ||
360 | err = sock_queue_rcv_skb(sk, skb); | ||
361 | if (!err) | ||
362 | return 0; | ||
363 | break; | ||
364 | } | ||
365 | |||
366 | if (pn->rx_credits == 0) { | ||
367 | err = -ENOBUFS; | ||
368 | break; | ||
369 | } | ||
370 | pn->rx_credits--; | ||
371 | queue = &sk->sk_receive_queue; | ||
372 | goto queue; | ||
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 | queue: | ||
417 | skb->dev = NULL; | ||
418 | skb_set_owner_r(skb, sk); | ||
419 | err = skb->len; | ||
420 | skb_queue_tail(queue, skb); | ||
421 | if (!sock_flag(sk, SOCK_DEAD)) | ||
422 | sk->sk_data_ready(sk, err); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* Destroy connected sock. */ | ||
427 | static void pipe_destruct(struct sock *sk) | ||
428 | { | ||
429 | struct pep_sock *pn = pep_sk(sk); | ||
430 | |||
431 | skb_queue_purge(&sk->sk_receive_queue); | ||
432 | skb_queue_purge(&pn->ctrlreq_queue); | ||
433 | } | ||
434 | |||
435 | static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) | ||
436 | { | ||
437 | struct sock *newsk; | ||
438 | struct pep_sock *newpn, *pn = pep_sk(sk); | ||
439 | struct pnpipehdr *hdr; | ||
440 | struct sockaddr_pn dst; | ||
441 | u16 peer_type; | ||
442 | u8 pipe_handle, enabled, n_sb; | ||
443 | |||
444 | if (!pskb_pull(skb, sizeof(*hdr) + 4)) | ||
445 | return -EINVAL; | ||
446 | |||
447 | hdr = pnp_hdr(skb); | ||
448 | pipe_handle = hdr->pipe_handle; | ||
449 | switch (hdr->state_after_connect) { | ||
450 | case PN_PIPE_DISABLE: | ||
451 | enabled = 0; | ||
452 | break; | ||
453 | case PN_PIPE_ENABLE: | ||
454 | enabled = 1; | ||
455 | break; | ||
456 | default: | ||
457 | pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); | ||
458 | return -EINVAL; | ||
459 | } | ||
460 | peer_type = hdr->other_pep_type << 8; | ||
461 | |||
462 | if (unlikely(sk->sk_state != TCP_LISTEN) || sk_acceptq_is_full(sk)) { | ||
463 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | ||
464 | return -ENOBUFS; | ||
465 | } | ||
466 | |||
467 | /* Parse sub-blocks (options) */ | ||
468 | n_sb = hdr->data[4]; | ||
469 | while (n_sb > 0) { | ||
470 | u8 type, buf[1], len = sizeof(buf); | ||
471 | const u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
472 | |||
473 | if (data == NULL) | ||
474 | return -EINVAL; | ||
475 | switch (type) { | ||
476 | case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: | ||
477 | if (len < 1) | ||
478 | return -EINVAL; | ||
479 | peer_type = (peer_type & 0xff00) | data[0]; | ||
480 | break; | ||
481 | } | ||
482 | n_sb--; | ||
483 | } | ||
484 | |||
485 | skb = skb_clone(skb, GFP_ATOMIC); | ||
486 | if (!skb) | ||
487 | return -ENOMEM; | ||
488 | |||
489 | /* Create a new to-be-accepted sock */ | ||
490 | newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); | ||
491 | if (!newsk) { | ||
492 | kfree_skb(skb); | ||
493 | return -ENOMEM; | ||
494 | } | ||
495 | sock_init_data(NULL, newsk); | ||
496 | newsk->sk_state = TCP_SYN_RECV; | ||
497 | newsk->sk_backlog_rcv = pipe_do_rcv; | ||
498 | newsk->sk_protocol = sk->sk_protocol; | ||
499 | newsk->sk_destruct = pipe_destruct; | ||
500 | |||
501 | newpn = pep_sk(newsk); | ||
502 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
503 | newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); | ||
504 | newpn->pn_sk.resource = pn->pn_sk.resource; | ||
505 | skb_queue_head_init(&newpn->ctrlreq_queue); | ||
506 | newpn->pipe_handle = pipe_handle; | ||
507 | newpn->peer_type = peer_type; | ||
508 | newpn->rx_credits = newpn->tx_credits = 0; | ||
509 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
510 | newpn->init_enable = enabled; | ||
511 | |||
512 | BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); | ||
513 | skb_queue_head(&newsk->sk_receive_queue, skb); | ||
514 | if (!sock_flag(sk, SOCK_DEAD)) | ||
515 | sk->sk_data_ready(sk, 0); | ||
516 | |||
517 | sk_acceptq_added(sk); | ||
518 | sk_add_node(newsk, &pn->ackq); | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /* Listening sock must be locked */ | ||
523 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, | ||
524 | const struct sockaddr_pn *dst, | ||
525 | u8 pipe_handle) | ||
526 | { | ||
527 | struct hlist_node *node; | ||
528 | struct sock *sknode; | ||
529 | u16 dobj = pn_sockaddr_get_object(dst); | ||
530 | |||
531 | sk_for_each(sknode, node, hlist) { | ||
532 | struct pep_sock *pnnode = pep_sk(sknode); | ||
533 | |||
534 | /* Ports match, but addresses might not: */ | ||
535 | if (pnnode->pn_sk.sobject != dobj) | ||
536 | continue; | ||
537 | if (pnnode->pipe_handle != pipe_handle) | ||
538 | continue; | ||
539 | if (sknode->sk_state == TCP_CLOSE_WAIT) | ||
540 | continue; | ||
541 | |||
542 | sock_hold(sknode); | ||
543 | return sknode; | ||
544 | } | ||
545 | return NULL; | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * Deliver an skb to a listening sock. | ||
550 | * Socket lock must be held. | ||
551 | * We then queue the skb to the right connected sock (if any). | ||
552 | */ | ||
553 | static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) | ||
554 | { | ||
555 | struct pep_sock *pn = pep_sk(sk); | ||
556 | struct sock *sknode; | ||
557 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
558 | struct sockaddr_pn dst; | ||
559 | int err = NET_RX_SUCCESS; | ||
560 | u8 pipe_handle; | ||
561 | |||
562 | if (!pskb_may_pull(skb, sizeof(*hdr))) | ||
563 | goto drop; | ||
564 | |||
565 | hdr = pnp_hdr(skb); | ||
566 | pipe_handle = hdr->pipe_handle; | ||
567 | if (pipe_handle == PN_PIPE_INVALID_HANDLE) | ||
568 | goto drop; | ||
569 | |||
570 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
571 | |||
572 | /* Look for an existing pipe handle */ | ||
573 | sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); | ||
574 | if (sknode) | ||
575 | return sk_receive_skb(sknode, skb, 1); | ||
576 | |||
577 | /* Look for a pipe handle pending accept */ | ||
578 | sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); | ||
579 | if (sknode) { | ||
580 | sock_put(sknode); | ||
581 | if (net_ratelimit()) | ||
582 | printk(KERN_WARNING"Phonet unconnected PEP ignored"); | ||
583 | err = NET_RX_DROP; | ||
584 | goto drop; | ||
585 | } | ||
586 | |||
587 | switch (hdr->message_id) { | ||
588 | case PNS_PEP_CONNECT_REQ: | ||
589 | err = pep_connreq_rcv(sk, skb); | ||
590 | break; | ||
591 | |||
592 | case PNS_PEP_DISCONNECT_REQ: | ||
593 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | ||
594 | break; | ||
595 | |||
596 | case PNS_PEP_CTRL_REQ: | ||
597 | pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC); | ||
598 | break; | ||
599 | |||
600 | case PNS_PEP_RESET_REQ: | ||
601 | case PNS_PEP_ENABLE_REQ: | ||
602 | case PNS_PEP_DISABLE_REQ: | ||
603 | /* invalid handle is not even allowed here! */ | ||
604 | default: | ||
605 | err = NET_RX_DROP; | ||
606 | } | ||
607 | drop: | ||
608 | kfree_skb(skb); | ||
609 | return err; | ||
610 | } | ||
611 | |||
612 | /* associated socket ceases to exist */ | ||
613 | static void pep_sock_close(struct sock *sk, long timeout) | ||
614 | { | ||
615 | struct pep_sock *pn = pep_sk(sk); | ||
616 | int ifindex = 0; | ||
617 | |||
618 | sk_common_release(sk); | ||
619 | |||
620 | lock_sock(sk); | ||
621 | if (sk->sk_state == TCP_LISTEN) { | ||
622 | /* Destroy the listen queue */ | ||
623 | struct sock *sknode; | ||
624 | struct hlist_node *p, *n; | ||
625 | |||
626 | sk_for_each_safe(sknode, p, n, &pn->ackq) | ||
627 | sk_del_node_init(sknode); | ||
628 | sk->sk_state = TCP_CLOSE; | ||
629 | } | ||
630 | ifindex = pn->ifindex; | ||
631 | pn->ifindex = 0; | ||
632 | release_sock(sk); | ||
633 | |||
634 | if (ifindex) | ||
635 | gprs_detach(sk); | ||
636 | } | ||
637 | |||
638 | static int pep_wait_connreq(struct sock *sk, int noblock) | ||
639 | { | ||
640 | struct task_struct *tsk = current; | ||
641 | struct pep_sock *pn = pep_sk(sk); | ||
642 | long timeo = sock_rcvtimeo(sk, noblock); | ||
643 | |||
644 | for (;;) { | ||
645 | DEFINE_WAIT(wait); | ||
646 | |||
647 | if (sk->sk_state != TCP_LISTEN) | ||
648 | return -EINVAL; | ||
649 | if (!hlist_empty(&pn->ackq)) | ||
650 | break; | ||
651 | if (!timeo) | ||
652 | return -EWOULDBLOCK; | ||
653 | if (signal_pending(tsk)) | ||
654 | return sock_intr_errno(timeo); | ||
655 | |||
656 | prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait, | ||
657 | TASK_INTERRUPTIBLE); | ||
658 | release_sock(sk); | ||
659 | timeo = schedule_timeout(timeo); | ||
660 | lock_sock(sk); | ||
661 | finish_wait(&sk->sk_socket->wait, &wait); | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) | ||
668 | { | ||
669 | struct pep_sock *pn = pep_sk(sk); | ||
670 | struct sock *newsk = NULL; | ||
671 | struct sk_buff *oskb; | ||
672 | int err; | ||
673 | |||
674 | lock_sock(sk); | ||
675 | err = pep_wait_connreq(sk, flags & O_NONBLOCK); | ||
676 | if (err) | ||
677 | goto out; | ||
678 | |||
679 | newsk = __sk_head(&pn->ackq); | ||
680 | |||
681 | oskb = skb_dequeue(&newsk->sk_receive_queue); | ||
682 | err = pep_accept_conn(newsk, oskb); | ||
683 | if (err) { | ||
684 | skb_queue_head(&newsk->sk_receive_queue, oskb); | ||
685 | newsk = NULL; | ||
686 | goto out; | ||
687 | } | ||
688 | |||
689 | sock_hold(sk); | ||
690 | pep_sk(newsk)->listener = sk; | ||
691 | |||
692 | sock_hold(newsk); | ||
693 | sk_del_node_init(newsk); | ||
694 | sk_acceptq_removed(sk); | ||
695 | sk_add_node(newsk, &pn->hlist); | ||
696 | __sock_put(newsk); | ||
697 | |||
698 | out: | ||
699 | release_sock(sk); | ||
700 | *errp = err; | ||
701 | return newsk; | ||
702 | } | ||
703 | |||
704 | static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) | ||
705 | { | ||
706 | struct pep_sock *pn = pep_sk(sk); | ||
707 | int answ; | ||
708 | |||
709 | switch (cmd) { | ||
710 | case SIOCINQ: | ||
711 | if (sk->sk_state == TCP_LISTEN) | ||
712 | return -EINVAL; | ||
713 | |||
714 | lock_sock(sk); | ||
715 | if (sock_flag(sk, SOCK_URGINLINE) | ||
716 | && !skb_queue_empty(&pn->ctrlreq_queue)) | ||
717 | answ = skb_peek(&pn->ctrlreq_queue)->len; | ||
718 | else if (!skb_queue_empty(&sk->sk_receive_queue)) | ||
719 | answ = skb_peek(&sk->sk_receive_queue)->len; | ||
720 | else | ||
721 | answ = 0; | ||
722 | release_sock(sk); | ||
723 | return put_user(answ, (int __user *)arg); | ||
724 | } | ||
725 | |||
726 | return -ENOIOCTLCMD; | ||
727 | } | ||
728 | |||
729 | static int pep_init(struct sock *sk) | ||
730 | { | ||
731 | struct pep_sock *pn = pep_sk(sk); | ||
732 | |||
733 | INIT_HLIST_HEAD(&pn->ackq); | ||
734 | INIT_HLIST_HEAD(&pn->hlist); | ||
735 | skb_queue_head_init(&pn->ctrlreq_queue); | ||
736 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; | ||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | static int pep_setsockopt(struct sock *sk, int level, int optname, | ||
741 | char __user *optval, int optlen) | ||
742 | { | ||
743 | struct pep_sock *pn = pep_sk(sk); | ||
744 | int val = 0, err = 0; | ||
745 | |||
746 | if (level != SOL_PNPIPE) | ||
747 | return -ENOPROTOOPT; | ||
748 | if (optlen >= sizeof(int)) { | ||
749 | if (get_user(val, (int __user *) optval)) | ||
750 | return -EFAULT; | ||
751 | } | ||
752 | |||
753 | lock_sock(sk); | ||
754 | switch (optname) { | ||
755 | case PNPIPE_ENCAP: | ||
756 | if (val && val != PNPIPE_ENCAP_IP) { | ||
757 | err = -EINVAL; | ||
758 | break; | ||
759 | } | ||
760 | if (!pn->ifindex == !val) | ||
761 | break; /* Nothing to do! */ | ||
762 | if (!capable(CAP_NET_ADMIN)) { | ||
763 | err = -EPERM; | ||
764 | break; | ||
765 | } | ||
766 | if (val) { | ||
767 | release_sock(sk); | ||
768 | err = gprs_attach(sk); | ||
769 | if (err > 0) { | ||
770 | pn->ifindex = err; | ||
771 | err = 0; | ||
772 | } | ||
773 | } else { | ||
774 | pn->ifindex = 0; | ||
775 | release_sock(sk); | ||
776 | gprs_detach(sk); | ||
777 | err = 0; | ||
778 | } | ||
779 | goto out_norel; | ||
780 | default: | ||
781 | err = -ENOPROTOOPT; | ||
782 | } | ||
783 | release_sock(sk); | ||
784 | |||
785 | out_norel: | ||
786 | return err; | ||
787 | } | ||
788 | |||
789 | static int pep_getsockopt(struct sock *sk, int level, int optname, | ||
790 | char __user *optval, int __user *optlen) | ||
791 | { | ||
792 | struct pep_sock *pn = pep_sk(sk); | ||
793 | int len, val; | ||
794 | |||
795 | if (level != SOL_PNPIPE) | ||
796 | return -ENOPROTOOPT; | ||
797 | if (get_user(len, optlen)) | ||
798 | return -EFAULT; | ||
799 | |||
800 | switch (optname) { | ||
801 | case PNPIPE_ENCAP: | ||
802 | val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; | ||
803 | break; | ||
804 | case PNPIPE_IFINDEX: | ||
805 | val = pn->ifindex; | ||
806 | break; | ||
807 | default: | ||
808 | return -ENOPROTOOPT; | ||
809 | } | ||
810 | |||
811 | len = min_t(unsigned int, sizeof(int), len); | ||
812 | if (put_user(len, optlen)) | ||
813 | return -EFAULT; | ||
814 | if (put_user(val, (int __user *) optval)) | ||
815 | return -EFAULT; | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) | ||
820 | { | ||
821 | struct pep_sock *pn = pep_sk(sk); | ||
822 | struct pnpipehdr *ph; | ||
823 | |||
824 | skb_push(skb, 3); | ||
825 | skb_reset_transport_header(skb); | ||
826 | ph = pnp_hdr(skb); | ||
827 | ph->utid = 0; | ||
828 | ph->message_id = PNS_PIPE_DATA; | ||
829 | ph->pipe_handle = pn->pipe_handle; | ||
830 | if (pn_flow_safe(pn->tx_fc) && pn->tx_credits) | ||
831 | pn->tx_credits--; | ||
832 | |||
833 | return pn_skb_send(sk, skb, &pipe_srv); | ||
834 | } | ||
835 | |||
836 | static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, | ||
837 | struct msghdr *msg, size_t len) | ||
838 | { | ||
839 | struct pep_sock *pn = pep_sk(sk); | ||
840 | struct sk_buff *skb = NULL; | ||
841 | long timeo; | ||
842 | int flags = msg->msg_flags; | ||
843 | int err, done; | ||
844 | |||
845 | if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR)) | ||
846 | return -EOPNOTSUPP; | ||
847 | |||
848 | lock_sock(sk); | ||
849 | timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); | ||
850 | if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) { | ||
851 | err = -ENOTCONN; | ||
852 | goto out; | ||
853 | } | ||
854 | if (sk->sk_state != TCP_ESTABLISHED) { | ||
855 | /* Wait until the pipe gets to enabled state */ | ||
856 | disabled: | ||
857 | err = sk_stream_wait_connect(sk, &timeo); | ||
858 | if (err) | ||
859 | goto out; | ||
860 | |||
861 | if (sk->sk_state == TCP_CLOSE_WAIT) { | ||
862 | err = -ECONNRESET; | ||
863 | goto out; | ||
864 | } | ||
865 | } | ||
866 | BUG_ON(sk->sk_state != TCP_ESTABLISHED); | ||
867 | |||
868 | /* Wait until flow control allows TX */ | ||
869 | done = pn->tx_credits > 0; | ||
870 | while (!done) { | ||
871 | DEFINE_WAIT(wait); | ||
872 | |||
873 | if (!timeo) { | ||
874 | err = -EAGAIN; | ||
875 | goto out; | ||
876 | } | ||
877 | if (signal_pending(current)) { | ||
878 | err = sock_intr_errno(timeo); | ||
879 | goto out; | ||
880 | } | ||
881 | |||
882 | prepare_to_wait(&sk->sk_socket->wait, &wait, | ||
883 | TASK_INTERRUPTIBLE); | ||
884 | done = sk_wait_event(sk, &timeo, pn->tx_credits > 0); | ||
885 | finish_wait(&sk->sk_socket->wait, &wait); | ||
886 | |||
887 | if (sk->sk_state != TCP_ESTABLISHED) | ||
888 | goto disabled; | ||
889 | } | ||
890 | |||
891 | if (!skb) { | ||
892 | skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len, | ||
893 | flags & MSG_DONTWAIT, &err); | ||
894 | if (skb == NULL) | ||
895 | goto out; | ||
896 | skb_reserve(skb, MAX_PHONET_HEADER + 3); | ||
897 | |||
898 | if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits) | ||
899 | goto disabled; /* sock_alloc_send_skb might sleep */ | ||
900 | } | ||
901 | |||
902 | err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); | ||
903 | if (err < 0) | ||
904 | goto out; | ||
905 | |||
906 | err = pipe_skb_send(sk, skb); | ||
907 | if (err >= 0) | ||
908 | err = len; /* success! */ | ||
909 | skb = NULL; | ||
910 | out: | ||
911 | release_sock(sk); | ||
912 | kfree_skb(skb); | ||
913 | return err; | ||
914 | } | ||
915 | |||
916 | int pep_writeable(struct sock *sk) | ||
917 | { | ||
918 | struct pep_sock *pn = pep_sk(sk); | ||
919 | |||
920 | return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0; | ||
921 | } | ||
922 | |||
923 | int pep_write(struct sock *sk, struct sk_buff *skb) | ||
924 | { | ||
925 | struct sk_buff *rskb, *fs; | ||
926 | int flen = 0; | ||
927 | |||
928 | rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); | ||
929 | if (!rskb) { | ||
930 | kfree_skb(skb); | ||
931 | return -ENOMEM; | ||
932 | } | ||
933 | skb_shinfo(rskb)->frag_list = skb; | ||
934 | rskb->len += skb->len; | ||
935 | rskb->data_len += rskb->len; | ||
936 | rskb->truesize += rskb->len; | ||
937 | |||
938 | /* Avoid nested fragments */ | ||
939 | for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) | ||
940 | flen += fs->len; | ||
941 | skb->next = skb_shinfo(skb)->frag_list; | ||
942 | skb_shinfo(skb)->frag_list = NULL; | ||
943 | skb->len -= flen; | ||
944 | skb->data_len -= flen; | ||
945 | skb->truesize -= flen; | ||
946 | |||
947 | skb_reserve(rskb, MAX_PHONET_HEADER + 3); | ||
948 | return pipe_skb_send(sk, rskb); | ||
949 | } | ||
950 | |||
951 | struct sk_buff *pep_read(struct sock *sk) | ||
952 | { | ||
953 | struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); | ||
954 | |||
955 | if (sk->sk_state == TCP_ESTABLISHED) | ||
956 | pipe_grant_credits(sk); | ||
957 | return skb; | ||
958 | } | ||
959 | |||
960 | static int pep_recvmsg(struct kiocb *iocb, struct sock *sk, | ||
961 | struct msghdr *msg, size_t len, int noblock, | ||
962 | int flags, int *addr_len) | ||
963 | { | ||
964 | struct sk_buff *skb; | ||
965 | int err; | ||
966 | |||
967 | if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE))) | ||
968 | return -ENOTCONN; | ||
969 | |||
970 | if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) { | ||
971 | /* Dequeue and acknowledge control request */ | ||
972 | struct pep_sock *pn = pep_sk(sk); | ||
973 | |||
974 | skb = skb_dequeue(&pn->ctrlreq_queue); | ||
975 | if (skb) { | ||
976 | pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR, | ||
977 | GFP_KERNEL); | ||
978 | msg->msg_flags |= MSG_OOB; | ||
979 | goto copy; | ||
980 | } | ||
981 | if (flags & MSG_OOB) | ||
982 | return -EINVAL; | ||
983 | } | ||
984 | |||
985 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
986 | lock_sock(sk); | ||
987 | if (skb == NULL) { | ||
988 | if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT) | ||
989 | err = -ECONNRESET; | ||
990 | release_sock(sk); | ||
991 | return err; | ||
992 | } | ||
993 | |||
994 | if (sk->sk_state == TCP_ESTABLISHED) | ||
995 | pipe_grant_credits(sk); | ||
996 | release_sock(sk); | ||
997 | copy: | ||
998 | msg->msg_flags |= MSG_EOR; | ||
999 | if (skb->len > len) | ||
1000 | msg->msg_flags |= MSG_TRUNC; | ||
1001 | else | ||
1002 | len = skb->len; | ||
1003 | |||
1004 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); | ||
1005 | if (!err) | ||
1006 | err = (flags & MSG_TRUNC) ? skb->len : len; | ||
1007 | |||
1008 | skb_free_datagram(sk, skb); | ||
1009 | return err; | ||
1010 | } | ||
1011 | |||
1012 | static void pep_sock_unhash(struct sock *sk) | ||
1013 | { | ||
1014 | struct pep_sock *pn = pep_sk(sk); | ||
1015 | struct sock *skparent = NULL; | ||
1016 | |||
1017 | lock_sock(sk); | ||
1018 | if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { | ||
1019 | skparent = pn->listener; | ||
1020 | sk_del_node_init(sk); | ||
1021 | release_sock(sk); | ||
1022 | |||
1023 | sk = skparent; | ||
1024 | pn = pep_sk(skparent); | ||
1025 | lock_sock(sk); | ||
1026 | } | ||
1027 | /* Unhash a listening sock only when it is closed | ||
1028 | * and all of its active connected pipes are closed. */ | ||
1029 | if (hlist_empty(&pn->hlist)) | ||
1030 | pn_sock_unhash(&pn->pn_sk.sk); | ||
1031 | release_sock(sk); | ||
1032 | |||
1033 | if (skparent) | ||
1034 | sock_put(skparent); | ||
1035 | } | ||
1036 | |||
1037 | static struct proto pep_proto = { | ||
1038 | .close = pep_sock_close, | ||
1039 | .accept = pep_sock_accept, | ||
1040 | .ioctl = pep_ioctl, | ||
1041 | .init = pep_init, | ||
1042 | .setsockopt = pep_setsockopt, | ||
1043 | .getsockopt = pep_getsockopt, | ||
1044 | .sendmsg = pep_sendmsg, | ||
1045 | .recvmsg = pep_recvmsg, | ||
1046 | .backlog_rcv = pep_do_rcv, | ||
1047 | .hash = pn_sock_hash, | ||
1048 | .unhash = pep_sock_unhash, | ||
1049 | .get_port = pn_sock_get_port, | ||
1050 | .obj_size = sizeof(struct pep_sock), | ||
1051 | .owner = THIS_MODULE, | ||
1052 | .name = "PNPIPE", | ||
1053 | }; | ||
1054 | |||
1055 | static struct phonet_protocol pep_pn_proto = { | ||
1056 | .ops = &phonet_stream_ops, | ||
1057 | .prot = &pep_proto, | ||
1058 | .sock_type = SOCK_SEQPACKET, | ||
1059 | }; | ||
1060 | |||
1061 | static int __init pep_register(void) | ||
1062 | { | ||
1063 | return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); | ||
1064 | } | ||
1065 | |||
1066 | static void __exit pep_unregister(void) | ||
1067 | { | ||
1068 | phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto); | ||
1069 | } | ||
1070 | |||
1071 | module_init(pep_register); | ||
1072 | module_exit(pep_unregister); | ||
1073 | MODULE_AUTHOR("Remi Denis-Courmont, Nokia"); | ||
1074 | MODULE_DESCRIPTION("Phonet pipe protocol"); | ||
1075 | MODULE_LICENSE("GPL"); | ||
1076 | MODULE_ALIAS_NET_PF_PROTO(PF_PHONET, PN_PROTO_PIPE); | ||