diff options
Diffstat (limited to 'net/phonet')
-rw-r--r-- | net/phonet/Kconfig | 16 | ||||
-rw-r--r-- | net/phonet/Makefile | 11 | ||||
-rw-r--r-- | net/phonet/af_phonet.c | 476 | ||||
-rw-r--r-- | net/phonet/datagram.c | 197 | ||||
-rw-r--r-- | net/phonet/pep-gprs.c | 347 | ||||
-rw-r--r-- | net/phonet/pep.c | 1076 | ||||
-rw-r--r-- | net/phonet/pn_dev.c | 208 | ||||
-rw-r--r-- | net/phonet/pn_netlink.c | 165 | ||||
-rw-r--r-- | net/phonet/socket.c | 411 | ||||
-rw-r--r-- | net/phonet/sysctl.c | 113 |
10 files changed, 3020 insertions, 0 deletions
diff --git a/net/phonet/Kconfig b/net/phonet/Kconfig new file mode 100644 index 000000000000..51a5669573f2 --- /dev/null +++ b/net/phonet/Kconfig | |||
@@ -0,0 +1,16 @@ | |||
1 | # | ||
2 | # Phonet protocol | ||
3 | # | ||
4 | |||
5 | config PHONET | ||
6 | tristate "Phonet protocols family" | ||
7 | help | ||
8 | The Phone Network protocol (PhoNet) is a packet-oriented | ||
9 | communication protocol developped by Nokia for use with its modems. | ||
10 | |||
11 | This is required for Maemo to use cellular data connectivity (if | ||
12 | supported). It can also be used to control Nokia phones | ||
13 | from a Linux computer, although AT commands may be easier to use. | ||
14 | |||
15 | To compile this driver as a module, choose M here: the module | ||
16 | will be called phonet. If unsure, say N. | ||
diff --git a/net/phonet/Makefile b/net/phonet/Makefile new file mode 100644 index 000000000000..d62bbba649b3 --- /dev/null +++ b/net/phonet/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | obj-$(CONFIG_PHONET) += phonet.o pn_pep.o | ||
2 | |||
3 | phonet-objs := \ | ||
4 | pn_dev.o \ | ||
5 | pn_netlink.o \ | ||
6 | socket.o \ | ||
7 | datagram.o \ | ||
8 | sysctl.o \ | ||
9 | af_phonet.o | ||
10 | |||
11 | pn_pep-objs := pep.o pep-gprs.o | ||
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c new file mode 100644 index 000000000000..b9d97effebe3 --- /dev/null +++ b/net/phonet/af_phonet.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | * File: af_phonet.c | ||
3 | * | ||
4 | * Phonet protocols family | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <asm/unaligned.h> | ||
29 | #include <net/sock.h> | ||
30 | |||
31 | #include <linux/if_phonet.h> | ||
32 | #include <linux/phonet.h> | ||
33 | #include <net/phonet/phonet.h> | ||
34 | #include <net/phonet/pn_dev.h> | ||
35 | |||
36 | static struct net_proto_family phonet_proto_family; | ||
37 | static struct phonet_protocol *phonet_proto_get(int protocol); | ||
38 | static inline void phonet_proto_put(struct phonet_protocol *pp); | ||
39 | |||
40 | /* protocol family functions */ | ||
41 | |||
42 | static int pn_socket_create(struct net *net, struct socket *sock, int protocol) | ||
43 | { | ||
44 | struct sock *sk; | ||
45 | struct pn_sock *pn; | ||
46 | struct phonet_protocol *pnp; | ||
47 | int err; | ||
48 | |||
49 | if (net != &init_net) | ||
50 | return -EAFNOSUPPORT; | ||
51 | |||
52 | if (!capable(CAP_SYS_ADMIN)) | ||
53 | return -EPERM; | ||
54 | |||
55 | if (protocol == 0) { | ||
56 | /* Default protocol selection */ | ||
57 | switch (sock->type) { | ||
58 | case SOCK_DGRAM: | ||
59 | protocol = PN_PROTO_PHONET; | ||
60 | break; | ||
61 | case SOCK_SEQPACKET: | ||
62 | protocol = PN_PROTO_PIPE; | ||
63 | break; | ||
64 | default: | ||
65 | return -EPROTONOSUPPORT; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | pnp = phonet_proto_get(protocol); | ||
70 | if (pnp == NULL && | ||
71 | request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0) | ||
72 | pnp = phonet_proto_get(protocol); | ||
73 | |||
74 | if (pnp == NULL) | ||
75 | return -EPROTONOSUPPORT; | ||
76 | if (sock->type != pnp->sock_type) { | ||
77 | err = -EPROTONOSUPPORT; | ||
78 | goto out; | ||
79 | } | ||
80 | |||
81 | sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot); | ||
82 | if (sk == NULL) { | ||
83 | err = -ENOMEM; | ||
84 | goto out; | ||
85 | } | ||
86 | |||
87 | sock_init_data(sock, sk); | ||
88 | sock->state = SS_UNCONNECTED; | ||
89 | sock->ops = pnp->ops; | ||
90 | sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; | ||
91 | sk->sk_protocol = protocol; | ||
92 | pn = pn_sk(sk); | ||
93 | pn->sobject = 0; | ||
94 | pn->resource = 0; | ||
95 | sk->sk_prot->init(sk); | ||
96 | err = 0; | ||
97 | |||
98 | out: | ||
99 | phonet_proto_put(pnp); | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static struct net_proto_family phonet_proto_family = { | ||
104 | .family = PF_PHONET, | ||
105 | .create = pn_socket_create, | ||
106 | .owner = THIS_MODULE, | ||
107 | }; | ||
108 | |||
109 | /* Phonet device header operations */ | ||
110 | static int pn_header_create(struct sk_buff *skb, struct net_device *dev, | ||
111 | unsigned short type, const void *daddr, | ||
112 | const void *saddr, unsigned len) | ||
113 | { | ||
114 | u8 *media = skb_push(skb, 1); | ||
115 | |||
116 | if (type != ETH_P_PHONET) | ||
117 | return -1; | ||
118 | |||
119 | if (!saddr) | ||
120 | saddr = dev->dev_addr; | ||
121 | *media = *(const u8 *)saddr; | ||
122 | return 1; | ||
123 | } | ||
124 | |||
125 | static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr) | ||
126 | { | ||
127 | const u8 *media = skb_mac_header(skb); | ||
128 | *haddr = *media; | ||
129 | return 1; | ||
130 | } | ||
131 | |||
132 | struct header_ops phonet_header_ops = { | ||
133 | .create = pn_header_create, | ||
134 | .parse = pn_header_parse, | ||
135 | }; | ||
136 | EXPORT_SYMBOL(phonet_header_ops); | ||
137 | |||
138 | /* | ||
139 | * Prepends an ISI header and sends a datagram. | ||
140 | */ | ||
141 | static int pn_send(struct sk_buff *skb, struct net_device *dev, | ||
142 | u16 dst, u16 src, u8 res, u8 irq) | ||
143 | { | ||
144 | struct phonethdr *ph; | ||
145 | int err; | ||
146 | |||
147 | if (skb->len + 2 > 0xffff) { | ||
148 | /* Phonet length field would overflow */ | ||
149 | err = -EMSGSIZE; | ||
150 | goto drop; | ||
151 | } | ||
152 | |||
153 | skb_reset_transport_header(skb); | ||
154 | WARN_ON(skb_headroom(skb) & 1); /* HW assumes word alignment */ | ||
155 | skb_push(skb, sizeof(struct phonethdr)); | ||
156 | skb_reset_network_header(skb); | ||
157 | ph = pn_hdr(skb); | ||
158 | ph->pn_rdev = pn_dev(dst); | ||
159 | ph->pn_sdev = pn_dev(src); | ||
160 | ph->pn_res = res; | ||
161 | ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph)); | ||
162 | ph->pn_robj = pn_obj(dst); | ||
163 | ph->pn_sobj = pn_obj(src); | ||
164 | |||
165 | skb->protocol = htons(ETH_P_PHONET); | ||
166 | skb->priority = 0; | ||
167 | skb->dev = dev; | ||
168 | |||
169 | if (pn_addr(src) == pn_addr(dst)) { | ||
170 | skb_reset_mac_header(skb); | ||
171 | skb->pkt_type = PACKET_LOOPBACK; | ||
172 | skb_orphan(skb); | ||
173 | if (irq) | ||
174 | netif_rx(skb); | ||
175 | else | ||
176 | netif_rx_ni(skb); | ||
177 | err = 0; | ||
178 | } else { | ||
179 | err = dev_hard_header(skb, dev, ntohs(skb->protocol), | ||
180 | NULL, NULL, skb->len); | ||
181 | if (err < 0) { | ||
182 | err = -EHOSTUNREACH; | ||
183 | goto drop; | ||
184 | } | ||
185 | err = dev_queue_xmit(skb); | ||
186 | } | ||
187 | |||
188 | return err; | ||
189 | drop: | ||
190 | kfree_skb(skb); | ||
191 | return err; | ||
192 | } | ||
193 | |||
194 | static int pn_raw_send(const void *data, int len, struct net_device *dev, | ||
195 | u16 dst, u16 src, u8 res) | ||
196 | { | ||
197 | struct sk_buff *skb = alloc_skb(MAX_PHONET_HEADER + len, GFP_ATOMIC); | ||
198 | if (skb == NULL) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | skb_reserve(skb, MAX_PHONET_HEADER); | ||
202 | __skb_put(skb, len); | ||
203 | skb_copy_to_linear_data(skb, data, len); | ||
204 | return pn_send(skb, dev, dst, src, res, 1); | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Create a Phonet header for the skb and send it out. Returns | ||
209 | * non-zero error code if failed. The skb is freed then. | ||
210 | */ | ||
211 | int pn_skb_send(struct sock *sk, struct sk_buff *skb, | ||
212 | const struct sockaddr_pn *target) | ||
213 | { | ||
214 | struct net_device *dev; | ||
215 | struct pn_sock *pn = pn_sk(sk); | ||
216 | int err; | ||
217 | u16 src; | ||
218 | u8 daddr = pn_sockaddr_get_addr(target), saddr = PN_NO_ADDR; | ||
219 | |||
220 | err = -EHOSTUNREACH; | ||
221 | if (sk->sk_bound_dev_if) | ||
222 | dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); | ||
223 | else | ||
224 | dev = phonet_device_get(sock_net(sk)); | ||
225 | if (!dev || !(dev->flags & IFF_UP)) | ||
226 | goto drop; | ||
227 | |||
228 | saddr = phonet_address_get(dev, daddr); | ||
229 | if (saddr == PN_NO_ADDR) | ||
230 | goto drop; | ||
231 | |||
232 | src = pn->sobject; | ||
233 | if (!pn_addr(src)) | ||
234 | src = pn_object(saddr, pn_obj(src)); | ||
235 | |||
236 | err = pn_send(skb, dev, pn_sockaddr_get_object(target), | ||
237 | src, pn_sockaddr_get_resource(target), 0); | ||
238 | dev_put(dev); | ||
239 | return err; | ||
240 | |||
241 | drop: | ||
242 | kfree_skb(skb); | ||
243 | if (dev) | ||
244 | dev_put(dev); | ||
245 | return err; | ||
246 | } | ||
247 | EXPORT_SYMBOL(pn_skb_send); | ||
248 | |||
249 | /* Do not send an error message in response to an error message */ | ||
250 | static inline int can_respond(struct sk_buff *skb) | ||
251 | { | ||
252 | const struct phonethdr *ph; | ||
253 | const struct phonetmsg *pm; | ||
254 | u8 submsg_id; | ||
255 | |||
256 | if (!pskb_may_pull(skb, 3)) | ||
257 | return 0; | ||
258 | |||
259 | ph = pn_hdr(skb); | ||
260 | if (phonet_address_get(skb->dev, ph->pn_rdev) != ph->pn_rdev) | ||
261 | return 0; /* we are not the destination */ | ||
262 | if (ph->pn_res == PN_PREFIX && !pskb_may_pull(skb, 5)) | ||
263 | return 0; | ||
264 | |||
265 | ph = pn_hdr(skb); /* re-acquires the pointer */ | ||
266 | pm = pn_msg(skb); | ||
267 | if (pm->pn_msg_id != PN_COMMON_MESSAGE) | ||
268 | return 1; | ||
269 | submsg_id = (ph->pn_res == PN_PREFIX) | ||
270 | ? pm->pn_e_submsg_id : pm->pn_submsg_id; | ||
271 | if (submsg_id != PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP && | ||
272 | pm->pn_e_submsg_id != PN_COMM_SERVICE_NOT_IDENTIFIED_RESP) | ||
273 | return 1; | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int send_obj_unreachable(struct sk_buff *rskb) | ||
278 | { | ||
279 | const struct phonethdr *oph = pn_hdr(rskb); | ||
280 | const struct phonetmsg *opm = pn_msg(rskb); | ||
281 | struct phonetmsg resp; | ||
282 | |||
283 | memset(&resp, 0, sizeof(resp)); | ||
284 | resp.pn_trans_id = opm->pn_trans_id; | ||
285 | resp.pn_msg_id = PN_COMMON_MESSAGE; | ||
286 | if (oph->pn_res == PN_PREFIX) { | ||
287 | resp.pn_e_res_id = opm->pn_e_res_id; | ||
288 | resp.pn_e_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; | ||
289 | resp.pn_e_orig_msg_id = opm->pn_msg_id; | ||
290 | resp.pn_e_status = 0; | ||
291 | } else { | ||
292 | resp.pn_submsg_id = PN_COMM_ISA_ENTITY_NOT_REACHABLE_RESP; | ||
293 | resp.pn_orig_msg_id = opm->pn_msg_id; | ||
294 | resp.pn_status = 0; | ||
295 | } | ||
296 | return pn_raw_send(&resp, sizeof(resp), rskb->dev, | ||
297 | pn_object(oph->pn_sdev, oph->pn_sobj), | ||
298 | pn_object(oph->pn_rdev, oph->pn_robj), | ||
299 | oph->pn_res); | ||
300 | } | ||
301 | |||
302 | static int send_reset_indications(struct sk_buff *rskb) | ||
303 | { | ||
304 | struct phonethdr *oph = pn_hdr(rskb); | ||
305 | static const u8 data[4] = { | ||
306 | 0x00 /* trans ID */, 0x10 /* subscribe msg */, | ||
307 | 0x00 /* subscription count */, 0x00 /* dummy */ | ||
308 | }; | ||
309 | |||
310 | return pn_raw_send(data, sizeof(data), rskb->dev, | ||
311 | pn_object(oph->pn_sdev, 0x00), | ||
312 | pn_object(oph->pn_rdev, oph->pn_robj), 0x10); | ||
313 | } | ||
314 | |||
315 | |||
316 | /* packet type functions */ | ||
317 | |||
318 | /* | ||
319 | * Stuff received packets to associated sockets. | ||
320 | * On error, returns non-zero and releases the skb. | ||
321 | */ | ||
322 | static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, | ||
323 | struct packet_type *pkttype, | ||
324 | struct net_device *orig_dev) | ||
325 | { | ||
326 | struct phonethdr *ph; | ||
327 | struct sock *sk; | ||
328 | struct sockaddr_pn sa; | ||
329 | u16 len; | ||
330 | |||
331 | if (dev_net(dev) != &init_net) | ||
332 | goto out; | ||
333 | |||
334 | /* check we have at least a full Phonet header */ | ||
335 | if (!pskb_pull(skb, sizeof(struct phonethdr))) | ||
336 | goto out; | ||
337 | |||
338 | /* check that the advertised length is correct */ | ||
339 | ph = pn_hdr(skb); | ||
340 | len = get_unaligned_be16(&ph->pn_length); | ||
341 | if (len < 2) | ||
342 | goto out; | ||
343 | len -= 2; | ||
344 | if ((len > skb->len) || pskb_trim(skb, len)) | ||
345 | goto out; | ||
346 | skb_reset_transport_header(skb); | ||
347 | |||
348 | pn_skb_get_dst_sockaddr(skb, &sa); | ||
349 | if (pn_sockaddr_get_addr(&sa) == 0) | ||
350 | goto out; /* currently, we cannot be device 0 */ | ||
351 | |||
352 | sk = pn_find_sock_by_sa(&sa); | ||
353 | if (sk == NULL) { | ||
354 | if (can_respond(skb)) { | ||
355 | send_obj_unreachable(skb); | ||
356 | send_reset_indications(skb); | ||
357 | } | ||
358 | goto out; | ||
359 | } | ||
360 | |||
361 | /* Push data to the socket (or other sockets connected to it). */ | ||
362 | return sk_receive_skb(sk, skb, 0); | ||
363 | |||
364 | out: | ||
365 | kfree_skb(skb); | ||
366 | return NET_RX_DROP; | ||
367 | } | ||
368 | |||
369 | static struct packet_type phonet_packet_type = { | ||
370 | .type = __constant_htons(ETH_P_PHONET), | ||
371 | .dev = NULL, | ||
372 | .func = phonet_rcv, | ||
373 | }; | ||
374 | |||
375 | /* Transport protocol registration */ | ||
376 | static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; | ||
377 | static DEFINE_SPINLOCK(proto_tab_lock); | ||
378 | |||
379 | int __init_or_module phonet_proto_register(int protocol, | ||
380 | struct phonet_protocol *pp) | ||
381 | { | ||
382 | int err = 0; | ||
383 | |||
384 | if (protocol >= PHONET_NPROTO) | ||
385 | return -EINVAL; | ||
386 | |||
387 | err = proto_register(pp->prot, 1); | ||
388 | if (err) | ||
389 | return err; | ||
390 | |||
391 | spin_lock(&proto_tab_lock); | ||
392 | if (proto_tab[protocol]) | ||
393 | err = -EBUSY; | ||
394 | else | ||
395 | proto_tab[protocol] = pp; | ||
396 | spin_unlock(&proto_tab_lock); | ||
397 | |||
398 | return err; | ||
399 | } | ||
400 | EXPORT_SYMBOL(phonet_proto_register); | ||
401 | |||
402 | void phonet_proto_unregister(int protocol, struct phonet_protocol *pp) | ||
403 | { | ||
404 | spin_lock(&proto_tab_lock); | ||
405 | BUG_ON(proto_tab[protocol] != pp); | ||
406 | proto_tab[protocol] = NULL; | ||
407 | spin_unlock(&proto_tab_lock); | ||
408 | proto_unregister(pp->prot); | ||
409 | } | ||
410 | EXPORT_SYMBOL(phonet_proto_unregister); | ||
411 | |||
412 | static struct phonet_protocol *phonet_proto_get(int protocol) | ||
413 | { | ||
414 | struct phonet_protocol *pp; | ||
415 | |||
416 | if (protocol >= PHONET_NPROTO) | ||
417 | return NULL; | ||
418 | |||
419 | spin_lock(&proto_tab_lock); | ||
420 | pp = proto_tab[protocol]; | ||
421 | if (pp && !try_module_get(pp->prot->owner)) | ||
422 | pp = NULL; | ||
423 | spin_unlock(&proto_tab_lock); | ||
424 | |||
425 | return pp; | ||
426 | } | ||
427 | |||
428 | static inline void phonet_proto_put(struct phonet_protocol *pp) | ||
429 | { | ||
430 | module_put(pp->prot->owner); | ||
431 | } | ||
432 | |||
433 | /* Module registration */ | ||
434 | static int __init phonet_init(void) | ||
435 | { | ||
436 | int err; | ||
437 | |||
438 | err = sock_register(&phonet_proto_family); | ||
439 | if (err) { | ||
440 | printk(KERN_ALERT | ||
441 | "phonet protocol family initialization failed\n"); | ||
442 | return err; | ||
443 | } | ||
444 | |||
445 | phonet_device_init(); | ||
446 | dev_add_pack(&phonet_packet_type); | ||
447 | phonet_netlink_register(); | ||
448 | phonet_sysctl_init(); | ||
449 | |||
450 | err = isi_register(); | ||
451 | if (err) | ||
452 | goto err; | ||
453 | return 0; | ||
454 | |||
455 | err: | ||
456 | phonet_sysctl_exit(); | ||
457 | sock_unregister(PF_PHONET); | ||
458 | dev_remove_pack(&phonet_packet_type); | ||
459 | phonet_device_exit(); | ||
460 | return err; | ||
461 | } | ||
462 | |||
463 | static void __exit phonet_exit(void) | ||
464 | { | ||
465 | isi_unregister(); | ||
466 | phonet_sysctl_exit(); | ||
467 | sock_unregister(PF_PHONET); | ||
468 | dev_remove_pack(&phonet_packet_type); | ||
469 | phonet_device_exit(); | ||
470 | } | ||
471 | |||
472 | module_init(phonet_init); | ||
473 | module_exit(phonet_exit); | ||
474 | MODULE_DESCRIPTION("Phonet protocol stack for Linux"); | ||
475 | MODULE_LICENSE("GPL"); | ||
476 | MODULE_ALIAS_NETPROTO(PF_PHONET); | ||
diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c new file mode 100644 index 000000000000..e087862ed7e4 --- /dev/null +++ b/net/phonet/datagram.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * File: datagram.c | ||
3 | * | ||
4 | * Datagram (ISI) Phonet sockets | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/socket.h> | ||
28 | #include <asm/ioctls.h> | ||
29 | #include <net/sock.h> | ||
30 | |||
31 | #include <linux/phonet.h> | ||
32 | #include <net/phonet/phonet.h> | ||
33 | |||
34 | static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb); | ||
35 | |||
36 | /* associated socket ceases to exist */ | ||
37 | static void pn_sock_close(struct sock *sk, long timeout) | ||
38 | { | ||
39 | sk_common_release(sk); | ||
40 | } | ||
41 | |||
42 | static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg) | ||
43 | { | ||
44 | struct sk_buff *skb; | ||
45 | int answ; | ||
46 | |||
47 | switch (cmd) { | ||
48 | case SIOCINQ: | ||
49 | lock_sock(sk); | ||
50 | skb = skb_peek(&sk->sk_receive_queue); | ||
51 | answ = skb ? skb->len : 0; | ||
52 | release_sock(sk); | ||
53 | return put_user(answ, (int __user *)arg); | ||
54 | } | ||
55 | |||
56 | return -ENOIOCTLCMD; | ||
57 | } | ||
58 | |||
59 | /* Destroy socket. All references are gone. */ | ||
60 | static void pn_destruct(struct sock *sk) | ||
61 | { | ||
62 | skb_queue_purge(&sk->sk_receive_queue); | ||
63 | } | ||
64 | |||
65 | static int pn_init(struct sock *sk) | ||
66 | { | ||
67 | sk->sk_destruct = pn_destruct; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int pn_sendmsg(struct kiocb *iocb, struct sock *sk, | ||
72 | struct msghdr *msg, size_t len) | ||
73 | { | ||
74 | struct sockaddr_pn *target; | ||
75 | struct sk_buff *skb; | ||
76 | int err; | ||
77 | |||
78 | if (msg->msg_flags & MSG_OOB) | ||
79 | return -EOPNOTSUPP; | ||
80 | |||
81 | if (msg->msg_name == NULL) | ||
82 | return -EDESTADDRREQ; | ||
83 | |||
84 | if (msg->msg_namelen < sizeof(struct sockaddr_pn)) | ||
85 | return -EINVAL; | ||
86 | |||
87 | target = (struct sockaddr_pn *)msg->msg_name; | ||
88 | if (target->spn_family != AF_PHONET) | ||
89 | return -EAFNOSUPPORT; | ||
90 | |||
91 | skb = sock_alloc_send_skb(sk, MAX_PHONET_HEADER + len, | ||
92 | msg->msg_flags & MSG_DONTWAIT, &err); | ||
93 | if (skb == NULL) | ||
94 | return err; | ||
95 | skb_reserve(skb, MAX_PHONET_HEADER); | ||
96 | |||
97 | err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len); | ||
98 | if (err < 0) { | ||
99 | kfree_skb(skb); | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Fill in the Phonet header and | ||
105 | * finally pass the packet forwards. | ||
106 | */ | ||
107 | err = pn_skb_send(sk, skb, target); | ||
108 | |||
109 | /* If ok, return len. */ | ||
110 | return (err >= 0) ? len : err; | ||
111 | } | ||
112 | |||
113 | static int pn_recvmsg(struct kiocb *iocb, struct sock *sk, | ||
114 | struct msghdr *msg, size_t len, int noblock, | ||
115 | int flags, int *addr_len) | ||
116 | { | ||
117 | struct sk_buff *skb = NULL; | ||
118 | struct sockaddr_pn sa; | ||
119 | int rval = -EOPNOTSUPP; | ||
120 | int copylen; | ||
121 | |||
122 | if (flags & MSG_OOB) | ||
123 | goto out_nofree; | ||
124 | |||
125 | if (addr_len) | ||
126 | *addr_len = sizeof(sa); | ||
127 | |||
128 | skb = skb_recv_datagram(sk, flags, noblock, &rval); | ||
129 | if (skb == NULL) | ||
130 | goto out_nofree; | ||
131 | |||
132 | pn_skb_get_src_sockaddr(skb, &sa); | ||
133 | |||
134 | copylen = skb->len; | ||
135 | if (len < copylen) { | ||
136 | msg->msg_flags |= MSG_TRUNC; | ||
137 | copylen = len; | ||
138 | } | ||
139 | |||
140 | rval = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copylen); | ||
141 | if (rval) { | ||
142 | rval = -EFAULT; | ||
143 | goto out; | ||
144 | } | ||
145 | |||
146 | rval = (flags & MSG_TRUNC) ? skb->len : copylen; | ||
147 | |||
148 | if (msg->msg_name != NULL) | ||
149 | memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn)); | ||
150 | |||
151 | out: | ||
152 | skb_free_datagram(sk, skb); | ||
153 | |||
154 | out_nofree: | ||
155 | return rval; | ||
156 | } | ||
157 | |||
158 | /* Queue an skb for a sock. */ | ||
159 | static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb) | ||
160 | { | ||
161 | int err = sock_queue_rcv_skb(sk, skb); | ||
162 | if (err < 0) | ||
163 | kfree_skb(skb); | ||
164 | return err ? NET_RX_DROP : NET_RX_SUCCESS; | ||
165 | } | ||
166 | |||
167 | /* Module registration */ | ||
168 | static struct proto pn_proto = { | ||
169 | .close = pn_sock_close, | ||
170 | .ioctl = pn_ioctl, | ||
171 | .init = pn_init, | ||
172 | .sendmsg = pn_sendmsg, | ||
173 | .recvmsg = pn_recvmsg, | ||
174 | .backlog_rcv = pn_backlog_rcv, | ||
175 | .hash = pn_sock_hash, | ||
176 | .unhash = pn_sock_unhash, | ||
177 | .get_port = pn_sock_get_port, | ||
178 | .obj_size = sizeof(struct pn_sock), | ||
179 | .owner = THIS_MODULE, | ||
180 | .name = "PHONET", | ||
181 | }; | ||
182 | |||
183 | static struct phonet_protocol pn_dgram_proto = { | ||
184 | .ops = &phonet_dgram_ops, | ||
185 | .prot = &pn_proto, | ||
186 | .sock_type = SOCK_DGRAM, | ||
187 | }; | ||
188 | |||
189 | int __init isi_register(void) | ||
190 | { | ||
191 | return phonet_proto_register(PN_PROTO_PHONET, &pn_dgram_proto); | ||
192 | } | ||
193 | |||
194 | void __exit isi_unregister(void) | ||
195 | { | ||
196 | phonet_proto_unregister(PN_PROTO_PHONET, &pn_dgram_proto); | ||
197 | } | ||
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c new file mode 100644 index 000000000000..9978afbd9f2a --- /dev/null +++ b/net/phonet/pep-gprs.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * File: pep-gprs.c | ||
3 | * | ||
4 | * GPRS over Phonet pipe 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/netdevice.h> | ||
27 | #include <linux/if_ether.h> | ||
28 | #include <linux/if_arp.h> | ||
29 | #include <net/sock.h> | ||
30 | |||
31 | #include <linux/if_phonet.h> | ||
32 | #include <net/tcp_states.h> | ||
33 | #include <net/phonet/gprs.h> | ||
34 | |||
35 | #define GPRS_DEFAULT_MTU 1400 | ||
36 | |||
37 | struct gprs_dev { | ||
38 | struct sock *sk; | ||
39 | void (*old_state_change)(struct sock *); | ||
40 | void (*old_data_ready)(struct sock *, int); | ||
41 | void (*old_write_space)(struct sock *); | ||
42 | |||
43 | struct net_device *net; | ||
44 | struct net_device_stats stats; | ||
45 | |||
46 | struct sk_buff_head tx_queue; | ||
47 | struct work_struct tx_work; | ||
48 | spinlock_t tx_lock; | ||
49 | unsigned tx_max; | ||
50 | }; | ||
51 | |||
52 | static int gprs_type_trans(struct sk_buff *skb) | ||
53 | { | ||
54 | const u8 *pvfc; | ||
55 | u8 buf; | ||
56 | |||
57 | pvfc = skb_header_pointer(skb, 0, 1, &buf); | ||
58 | if (!pvfc) | ||
59 | return 0; | ||
60 | /* Look at IP version field */ | ||
61 | switch (*pvfc >> 4) { | ||
62 | case 4: | ||
63 | return htons(ETH_P_IP); | ||
64 | case 6: | ||
65 | return htons(ETH_P_IPV6); | ||
66 | } | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Socket callbacks | ||
72 | */ | ||
73 | |||
74 | static void gprs_state_change(struct sock *sk) | ||
75 | { | ||
76 | struct gprs_dev *dev = sk->sk_user_data; | ||
77 | |||
78 | if (sk->sk_state == TCP_CLOSE_WAIT) { | ||
79 | netif_stop_queue(dev->net); | ||
80 | netif_carrier_off(dev->net); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb) | ||
85 | { | ||
86 | int err = 0; | ||
87 | u16 protocol = gprs_type_trans(skb); | ||
88 | |||
89 | if (!protocol) { | ||
90 | err = -EINVAL; | ||
91 | goto drop; | ||
92 | } | ||
93 | |||
94 | if (likely(skb_headroom(skb) & 3)) { | ||
95 | struct sk_buff *rskb, *fs; | ||
96 | int flen = 0; | ||
97 | |||
98 | /* Phonet Pipe data header is misaligned (3 bytes), | ||
99 | * so wrap the IP packet as a single fragment of an head-less | ||
100 | * socket buffer. The network stack will pull what it needs, | ||
101 | * but at least, the whole IP payload is not memcpy'd. */ | ||
102 | rskb = netdev_alloc_skb(dev->net, 0); | ||
103 | if (!rskb) { | ||
104 | err = -ENOBUFS; | ||
105 | goto drop; | ||
106 | } | ||
107 | skb_shinfo(rskb)->frag_list = skb; | ||
108 | rskb->len += skb->len; | ||
109 | rskb->data_len += rskb->len; | ||
110 | rskb->truesize += rskb->len; | ||
111 | |||
112 | /* Avoid nested fragments */ | ||
113 | for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next) | ||
114 | flen += fs->len; | ||
115 | skb->next = skb_shinfo(skb)->frag_list; | ||
116 | skb_shinfo(skb)->frag_list = NULL; | ||
117 | skb->len -= flen; | ||
118 | skb->data_len -= flen; | ||
119 | skb->truesize -= flen; | ||
120 | |||
121 | skb = rskb; | ||
122 | } | ||
123 | |||
124 | skb->protocol = protocol; | ||
125 | skb_reset_mac_header(skb); | ||
126 | skb->dev = dev->net; | ||
127 | |||
128 | if (likely(dev->net->flags & IFF_UP)) { | ||
129 | dev->stats.rx_packets++; | ||
130 | dev->stats.rx_bytes += skb->len; | ||
131 | netif_rx(skb); | ||
132 | skb = NULL; | ||
133 | } else | ||
134 | err = -ENODEV; | ||
135 | |||
136 | drop: | ||
137 | if (skb) { | ||
138 | dev_kfree_skb(skb); | ||
139 | dev->stats.rx_dropped++; | ||
140 | } | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | static void gprs_data_ready(struct sock *sk, int len) | ||
145 | { | ||
146 | struct gprs_dev *dev = sk->sk_user_data; | ||
147 | struct sk_buff *skb; | ||
148 | |||
149 | while ((skb = pep_read(sk)) != NULL) { | ||
150 | skb_orphan(skb); | ||
151 | gprs_recv(dev, skb); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static void gprs_write_space(struct sock *sk) | ||
156 | { | ||
157 | struct gprs_dev *dev = sk->sk_user_data; | ||
158 | unsigned credits = pep_writeable(sk); | ||
159 | |||
160 | spin_lock_bh(&dev->tx_lock); | ||
161 | dev->tx_max = credits; | ||
162 | if (credits > skb_queue_len(&dev->tx_queue)) | ||
163 | netif_wake_queue(dev->net); | ||
164 | spin_unlock_bh(&dev->tx_lock); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Network device callbacks | ||
169 | */ | ||
170 | |||
171 | static int gprs_xmit(struct sk_buff *skb, struct net_device *net) | ||
172 | { | ||
173 | struct gprs_dev *dev = netdev_priv(net); | ||
174 | |||
175 | switch (skb->protocol) { | ||
176 | case htons(ETH_P_IP): | ||
177 | case htons(ETH_P_IPV6): | ||
178 | break; | ||
179 | default: | ||
180 | dev_kfree_skb(skb); | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | spin_lock(&dev->tx_lock); | ||
185 | if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) { | ||
186 | skb_queue_tail(&dev->tx_queue, skb); | ||
187 | skb = NULL; | ||
188 | } | ||
189 | if (skb_queue_len(&dev->tx_queue) >= dev->tx_max) | ||
190 | netif_stop_queue(net); | ||
191 | spin_unlock(&dev->tx_lock); | ||
192 | |||
193 | schedule_work(&dev->tx_work); | ||
194 | if (unlikely(skb)) | ||
195 | dev_kfree_skb(skb); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void gprs_tx(struct work_struct *work) | ||
200 | { | ||
201 | struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work); | ||
202 | struct sock *sk = dev->sk; | ||
203 | struct sk_buff *skb; | ||
204 | |||
205 | while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) { | ||
206 | int err; | ||
207 | |||
208 | dev->stats.tx_bytes += skb->len; | ||
209 | dev->stats.tx_packets++; | ||
210 | |||
211 | skb_orphan(skb); | ||
212 | skb_set_owner_w(skb, sk); | ||
213 | |||
214 | lock_sock(sk); | ||
215 | err = pep_write(sk, skb); | ||
216 | if (err) { | ||
217 | LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n", | ||
218 | dev->net->name, err); | ||
219 | dev->stats.tx_aborted_errors++; | ||
220 | dev->stats.tx_errors++; | ||
221 | } | ||
222 | release_sock(sk); | ||
223 | } | ||
224 | |||
225 | lock_sock(sk); | ||
226 | gprs_write_space(sk); | ||
227 | release_sock(sk); | ||
228 | } | ||
229 | |||
230 | static int gprs_set_mtu(struct net_device *net, int new_mtu) | ||
231 | { | ||
232 | if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) | ||
233 | return -EINVAL; | ||
234 | |||
235 | net->mtu = new_mtu; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static struct net_device_stats *gprs_get_stats(struct net_device *net) | ||
240 | { | ||
241 | struct gprs_dev *dev = netdev_priv(net); | ||
242 | |||
243 | return &dev->stats; | ||
244 | } | ||
245 | |||
246 | static void gprs_setup(struct net_device *net) | ||
247 | { | ||
248 | net->features = NETIF_F_FRAGLIST; | ||
249 | net->type = ARPHRD_NONE; | ||
250 | net->flags = IFF_POINTOPOINT | IFF_NOARP; | ||
251 | net->mtu = GPRS_DEFAULT_MTU; | ||
252 | net->hard_header_len = 0; | ||
253 | net->addr_len = 0; | ||
254 | net->tx_queue_len = 10; | ||
255 | |||
256 | net->destructor = free_netdev; | ||
257 | net->hard_start_xmit = gprs_xmit; /* mandatory */ | ||
258 | net->change_mtu = gprs_set_mtu; | ||
259 | net->get_stats = gprs_get_stats; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * External interface | ||
264 | */ | ||
265 | |||
266 | /* | ||
267 | * Attach a GPRS interface to a datagram socket. | ||
268 | * Returns the interface index on success, negative error code on error. | ||
269 | */ | ||
270 | int gprs_attach(struct sock *sk) | ||
271 | { | ||
272 | static const char ifname[] = "gprs%d"; | ||
273 | struct gprs_dev *dev; | ||
274 | struct net_device *net; | ||
275 | int err; | ||
276 | |||
277 | if (unlikely(sk->sk_type == SOCK_STREAM)) | ||
278 | return -EINVAL; /* need packet boundaries */ | ||
279 | |||
280 | /* Create net device */ | ||
281 | net = alloc_netdev(sizeof(*dev), ifname, gprs_setup); | ||
282 | if (!net) | ||
283 | return -ENOMEM; | ||
284 | dev = netdev_priv(net); | ||
285 | dev->net = net; | ||
286 | dev->tx_max = 0; | ||
287 | spin_lock_init(&dev->tx_lock); | ||
288 | skb_queue_head_init(&dev->tx_queue); | ||
289 | INIT_WORK(&dev->tx_work, gprs_tx); | ||
290 | |||
291 | netif_stop_queue(net); | ||
292 | err = register_netdev(net); | ||
293 | if (err) { | ||
294 | free_netdev(net); | ||
295 | return err; | ||
296 | } | ||
297 | |||
298 | lock_sock(sk); | ||
299 | if (unlikely(sk->sk_user_data)) { | ||
300 | err = -EBUSY; | ||
301 | goto out_rel; | ||
302 | } | ||
303 | if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) || | ||
304 | sock_flag(sk, SOCK_DEAD))) { | ||
305 | err = -EINVAL; | ||
306 | goto out_rel; | ||
307 | } | ||
308 | sk->sk_user_data = dev; | ||
309 | dev->old_state_change = sk->sk_state_change; | ||
310 | dev->old_data_ready = sk->sk_data_ready; | ||
311 | dev->old_write_space = sk->sk_write_space; | ||
312 | sk->sk_state_change = gprs_state_change; | ||
313 | sk->sk_data_ready = gprs_data_ready; | ||
314 | sk->sk_write_space = gprs_write_space; | ||
315 | release_sock(sk); | ||
316 | |||
317 | sock_hold(sk); | ||
318 | dev->sk = sk; | ||
319 | |||
320 | printk(KERN_DEBUG"%s: attached\n", net->name); | ||
321 | gprs_write_space(sk); /* kick off TX */ | ||
322 | return net->ifindex; | ||
323 | |||
324 | out_rel: | ||
325 | release_sock(sk); | ||
326 | unregister_netdev(net); | ||
327 | return err; | ||
328 | } | ||
329 | |||
330 | void gprs_detach(struct sock *sk) | ||
331 | { | ||
332 | struct gprs_dev *dev = sk->sk_user_data; | ||
333 | struct net_device *net = dev->net; | ||
334 | |||
335 | lock_sock(sk); | ||
336 | sk->sk_user_data = NULL; | ||
337 | sk->sk_state_change = dev->old_state_change; | ||
338 | sk->sk_data_ready = dev->old_data_ready; | ||
339 | sk->sk_write_space = dev->old_write_space; | ||
340 | release_sock(sk); | ||
341 | |||
342 | printk(KERN_DEBUG"%s: detached\n", net->name); | ||
343 | unregister_netdev(net); | ||
344 | flush_scheduled_work(); | ||
345 | sock_put(sk); | ||
346 | skb_queue_purge(&dev->tx_queue); | ||
347 | } | ||
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); | ||
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c new file mode 100644 index 000000000000..53be9fc82aaa --- /dev/null +++ b/net/phonet/pn_dev.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * File: pn_dev.c | ||
3 | * | ||
4 | * Phonet network device | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/net.h> | ||
28 | #include <linux/netdevice.h> | ||
29 | #include <linux/phonet.h> | ||
30 | #include <net/sock.h> | ||
31 | #include <net/phonet/pn_dev.h> | ||
32 | |||
33 | /* when accessing, remember to lock with spin_lock(&pndevs.lock); */ | ||
34 | struct phonet_device_list pndevs = { | ||
35 | .list = LIST_HEAD_INIT(pndevs.list), | ||
36 | .lock = __SPIN_LOCK_UNLOCKED(pndevs.lock), | ||
37 | }; | ||
38 | |||
39 | /* Allocate new Phonet device. */ | ||
40 | static struct phonet_device *__phonet_device_alloc(struct net_device *dev) | ||
41 | { | ||
42 | struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); | ||
43 | if (pnd == NULL) | ||
44 | return NULL; | ||
45 | pnd->netdev = dev; | ||
46 | bitmap_zero(pnd->addrs, 64); | ||
47 | |||
48 | list_add(&pnd->list, &pndevs.list); | ||
49 | return pnd; | ||
50 | } | ||
51 | |||
52 | static struct phonet_device *__phonet_get(struct net_device *dev) | ||
53 | { | ||
54 | struct phonet_device *pnd; | ||
55 | |||
56 | list_for_each_entry(pnd, &pndevs.list, list) { | ||
57 | if (pnd->netdev == dev) | ||
58 | return pnd; | ||
59 | } | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | static void __phonet_device_free(struct phonet_device *pnd) | ||
64 | { | ||
65 | list_del(&pnd->list); | ||
66 | kfree(pnd); | ||
67 | } | ||
68 | |||
69 | struct net_device *phonet_device_get(struct net *net) | ||
70 | { | ||
71 | struct phonet_device *pnd; | ||
72 | struct net_device *dev; | ||
73 | |||
74 | spin_lock_bh(&pndevs.lock); | ||
75 | list_for_each_entry(pnd, &pndevs.list, list) { | ||
76 | dev = pnd->netdev; | ||
77 | BUG_ON(!dev); | ||
78 | |||
79 | if (dev_net(dev) == net && | ||
80 | (dev->reg_state == NETREG_REGISTERED) && | ||
81 | ((pnd->netdev->flags & IFF_UP)) == IFF_UP) | ||
82 | break; | ||
83 | dev = NULL; | ||
84 | } | ||
85 | if (dev) | ||
86 | dev_hold(dev); | ||
87 | spin_unlock_bh(&pndevs.lock); | ||
88 | return dev; | ||
89 | } | ||
90 | |||
91 | int phonet_address_add(struct net_device *dev, u8 addr) | ||
92 | { | ||
93 | struct phonet_device *pnd; | ||
94 | int err = 0; | ||
95 | |||
96 | spin_lock_bh(&pndevs.lock); | ||
97 | /* Find or create Phonet-specific device data */ | ||
98 | pnd = __phonet_get(dev); | ||
99 | if (pnd == NULL) | ||
100 | pnd = __phonet_device_alloc(dev); | ||
101 | if (unlikely(pnd == NULL)) | ||
102 | err = -ENOMEM; | ||
103 | else if (test_and_set_bit(addr >> 2, pnd->addrs)) | ||
104 | err = -EEXIST; | ||
105 | spin_unlock_bh(&pndevs.lock); | ||
106 | return err; | ||
107 | } | ||
108 | |||
109 | int phonet_address_del(struct net_device *dev, u8 addr) | ||
110 | { | ||
111 | struct phonet_device *pnd; | ||
112 | int err = 0; | ||
113 | |||
114 | spin_lock_bh(&pndevs.lock); | ||
115 | pnd = __phonet_get(dev); | ||
116 | if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) | ||
117 | err = -EADDRNOTAVAIL; | ||
118 | if (bitmap_empty(pnd->addrs, 64)) | ||
119 | __phonet_device_free(pnd); | ||
120 | spin_unlock_bh(&pndevs.lock); | ||
121 | return err; | ||
122 | } | ||
123 | |||
124 | /* Gets a source address toward a destination, through a interface. */ | ||
125 | u8 phonet_address_get(struct net_device *dev, u8 addr) | ||
126 | { | ||
127 | struct phonet_device *pnd; | ||
128 | |||
129 | spin_lock_bh(&pndevs.lock); | ||
130 | pnd = __phonet_get(dev); | ||
131 | if (pnd) { | ||
132 | BUG_ON(bitmap_empty(pnd->addrs, 64)); | ||
133 | |||
134 | /* Use same source address as destination, if possible */ | ||
135 | if (!test_bit(addr >> 2, pnd->addrs)) | ||
136 | addr = find_first_bit(pnd->addrs, 64) << 2; | ||
137 | } else | ||
138 | addr = PN_NO_ADDR; | ||
139 | spin_unlock_bh(&pndevs.lock); | ||
140 | return addr; | ||
141 | } | ||
142 | |||
143 | int phonet_address_lookup(u8 addr) | ||
144 | { | ||
145 | struct phonet_device *pnd; | ||
146 | |||
147 | spin_lock_bh(&pndevs.lock); | ||
148 | list_for_each_entry(pnd, &pndevs.list, list) { | ||
149 | /* Don't allow unregistering devices! */ | ||
150 | if ((pnd->netdev->reg_state != NETREG_REGISTERED) || | ||
151 | ((pnd->netdev->flags & IFF_UP)) != IFF_UP) | ||
152 | continue; | ||
153 | |||
154 | if (test_bit(addr >> 2, pnd->addrs)) { | ||
155 | spin_unlock_bh(&pndevs.lock); | ||
156 | return 0; | ||
157 | } | ||
158 | } | ||
159 | spin_unlock_bh(&pndevs.lock); | ||
160 | return -EADDRNOTAVAIL; | ||
161 | } | ||
162 | |||
163 | /* notify Phonet of device events */ | ||
164 | static int phonet_device_notify(struct notifier_block *me, unsigned long what, | ||
165 | void *arg) | ||
166 | { | ||
167 | struct net_device *dev = arg; | ||
168 | |||
169 | if (what == NETDEV_UNREGISTER) { | ||
170 | struct phonet_device *pnd; | ||
171 | |||
172 | /* Destroy phonet-specific device data */ | ||
173 | spin_lock_bh(&pndevs.lock); | ||
174 | pnd = __phonet_get(dev); | ||
175 | if (pnd) | ||
176 | __phonet_device_free(pnd); | ||
177 | spin_unlock_bh(&pndevs.lock); | ||
178 | } | ||
179 | return 0; | ||
180 | |||
181 | } | ||
182 | |||
183 | static struct notifier_block phonet_device_notifier = { | ||
184 | .notifier_call = phonet_device_notify, | ||
185 | .priority = 0, | ||
186 | }; | ||
187 | |||
188 | /* Initialize Phonet devices list */ | ||
189 | void phonet_device_init(void) | ||
190 | { | ||
191 | register_netdevice_notifier(&phonet_device_notifier); | ||
192 | } | ||
193 | |||
194 | void phonet_device_exit(void) | ||
195 | { | ||
196 | struct phonet_device *pnd, *n; | ||
197 | |||
198 | rtnl_unregister_all(PF_PHONET); | ||
199 | rtnl_lock(); | ||
200 | spin_lock_bh(&pndevs.lock); | ||
201 | |||
202 | list_for_each_entry_safe(pnd, n, &pndevs.list, list) | ||
203 | __phonet_device_free(pnd); | ||
204 | |||
205 | spin_unlock_bh(&pndevs.lock); | ||
206 | rtnl_unlock(); | ||
207 | unregister_netdevice_notifier(&phonet_device_notifier); | ||
208 | } | ||
diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c new file mode 100644 index 000000000000..b1770d66bc8d --- /dev/null +++ b/net/phonet/pn_netlink.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * File: pn_netlink.c | ||
3 | * | ||
4 | * Phonet netlink interface | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/netlink.h> | ||
28 | #include <linux/phonet.h> | ||
29 | #include <net/sock.h> | ||
30 | #include <net/phonet/pn_dev.h> | ||
31 | |||
32 | static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, | ||
33 | u32 pid, u32 seq, int event); | ||
34 | |||
35 | static void rtmsg_notify(int event, struct net_device *dev, u8 addr) | ||
36 | { | ||
37 | struct sk_buff *skb; | ||
38 | int err = -ENOBUFS; | ||
39 | |||
40 | skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + | ||
41 | nla_total_size(1), GFP_KERNEL); | ||
42 | if (skb == NULL) | ||
43 | goto errout; | ||
44 | err = fill_addr(skb, dev, addr, 0, 0, event); | ||
45 | if (err < 0) { | ||
46 | WARN_ON(err == -EMSGSIZE); | ||
47 | kfree_skb(skb); | ||
48 | goto errout; | ||
49 | } | ||
50 | err = rtnl_notify(skb, dev_net(dev), 0, | ||
51 | RTNLGRP_PHONET_IFADDR, NULL, GFP_KERNEL); | ||
52 | errout: | ||
53 | if (err < 0) | ||
54 | rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_IFADDR, err); | ||
55 | } | ||
56 | |||
57 | static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = { | ||
58 | [IFA_LOCAL] = { .type = NLA_U8 }, | ||
59 | }; | ||
60 | |||
61 | static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr) | ||
62 | { | ||
63 | struct net *net = sock_net(skb->sk); | ||
64 | struct nlattr *tb[IFA_MAX+1]; | ||
65 | struct net_device *dev; | ||
66 | struct ifaddrmsg *ifm; | ||
67 | int err; | ||
68 | u8 pnaddr; | ||
69 | |||
70 | if (!capable(CAP_SYS_ADMIN)) | ||
71 | return -EPERM; | ||
72 | |||
73 | ASSERT_RTNL(); | ||
74 | |||
75 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy); | ||
76 | if (err < 0) | ||
77 | return err; | ||
78 | |||
79 | ifm = nlmsg_data(nlh); | ||
80 | if (tb[IFA_LOCAL] == NULL) | ||
81 | return -EINVAL; | ||
82 | pnaddr = nla_get_u8(tb[IFA_LOCAL]); | ||
83 | if (pnaddr & 3) | ||
84 | /* Phonet addresses only have 6 high-order bits */ | ||
85 | return -EINVAL; | ||
86 | |||
87 | dev = __dev_get_by_index(net, ifm->ifa_index); | ||
88 | if (dev == NULL) | ||
89 | return -ENODEV; | ||
90 | |||
91 | if (nlh->nlmsg_type == RTM_NEWADDR) | ||
92 | err = phonet_address_add(dev, pnaddr); | ||
93 | else | ||
94 | err = phonet_address_del(dev, pnaddr); | ||
95 | if (!err) | ||
96 | rtmsg_notify(nlh->nlmsg_type, dev, pnaddr); | ||
97 | return err; | ||
98 | } | ||
99 | |||
100 | static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, | ||
101 | u32 pid, u32 seq, int event) | ||
102 | { | ||
103 | struct ifaddrmsg *ifm; | ||
104 | struct nlmsghdr *nlh; | ||
105 | |||
106 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), 0); | ||
107 | if (nlh == NULL) | ||
108 | return -EMSGSIZE; | ||
109 | |||
110 | ifm = nlmsg_data(nlh); | ||
111 | ifm->ifa_family = AF_PHONET; | ||
112 | ifm->ifa_prefixlen = 0; | ||
113 | ifm->ifa_flags = IFA_F_PERMANENT; | ||
114 | ifm->ifa_scope = RT_SCOPE_LINK; | ||
115 | ifm->ifa_index = dev->ifindex; | ||
116 | NLA_PUT_U8(skb, IFA_LOCAL, addr); | ||
117 | return nlmsg_end(skb, nlh); | ||
118 | |||
119 | nla_put_failure: | ||
120 | nlmsg_cancel(skb, nlh); | ||
121 | return -EMSGSIZE; | ||
122 | } | ||
123 | |||
124 | static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) | ||
125 | { | ||
126 | struct phonet_device *pnd; | ||
127 | int dev_idx = 0, dev_start_idx = cb->args[0]; | ||
128 | int addr_idx = 0, addr_start_idx = cb->args[1]; | ||
129 | |||
130 | spin_lock_bh(&pndevs.lock); | ||
131 | list_for_each_entry(pnd, &pndevs.list, list) { | ||
132 | u8 addr; | ||
133 | |||
134 | if (dev_idx > dev_start_idx) | ||
135 | addr_start_idx = 0; | ||
136 | if (dev_idx++ < dev_start_idx) | ||
137 | continue; | ||
138 | |||
139 | addr_idx = 0; | ||
140 | for (addr = find_first_bit(pnd->addrs, 64); addr < 64; | ||
141 | addr = find_next_bit(pnd->addrs, 64, 1+addr)) { | ||
142 | if (addr_idx++ < addr_start_idx) | ||
143 | continue; | ||
144 | |||
145 | if (fill_addr(skb, pnd->netdev, addr << 2, | ||
146 | NETLINK_CB(cb->skb).pid, | ||
147 | cb->nlh->nlmsg_seq, RTM_NEWADDR)) | ||
148 | goto out; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | out: | ||
153 | spin_unlock_bh(&pndevs.lock); | ||
154 | cb->args[0] = dev_idx; | ||
155 | cb->args[1] = addr_idx; | ||
156 | |||
157 | return skb->len; | ||
158 | } | ||
159 | |||
160 | void __init phonet_netlink_register(void) | ||
161 | { | ||
162 | rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); | ||
163 | rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); | ||
164 | rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); | ||
165 | } | ||
diff --git a/net/phonet/socket.c b/net/phonet/socket.c new file mode 100644 index 000000000000..d81740187fb4 --- /dev/null +++ b/net/phonet/socket.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * File: socket.c | ||
3 | * | ||
4 | * Phonet sockets | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | ||
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
23 | * 02110-1301 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/net.h> | ||
28 | #include <linux/poll.h> | ||
29 | #include <net/sock.h> | ||
30 | #include <net/tcp_states.h> | ||
31 | |||
32 | #include <linux/phonet.h> | ||
33 | #include <net/phonet/phonet.h> | ||
34 | #include <net/phonet/pep.h> | ||
35 | #include <net/phonet/pn_dev.h> | ||
36 | |||
37 | static int pn_socket_release(struct socket *sock) | ||
38 | { | ||
39 | struct sock *sk = sock->sk; | ||
40 | |||
41 | if (sk) { | ||
42 | sock->sk = NULL; | ||
43 | sk->sk_prot->close(sk, 0); | ||
44 | } | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static struct { | ||
49 | struct hlist_head hlist; | ||
50 | spinlock_t lock; | ||
51 | } pnsocks = { | ||
52 | .hlist = HLIST_HEAD_INIT, | ||
53 | .lock = __SPIN_LOCK_UNLOCKED(pnsocks.lock), | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Find address based on socket address, match only certain fields. | ||
58 | * Also grab sock if it was found. Remember to sock_put it later. | ||
59 | */ | ||
60 | struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn) | ||
61 | { | ||
62 | struct hlist_node *node; | ||
63 | struct sock *sknode; | ||
64 | struct sock *rval = NULL; | ||
65 | u16 obj = pn_sockaddr_get_object(spn); | ||
66 | u8 res = spn->spn_resource; | ||
67 | |||
68 | spin_lock_bh(&pnsocks.lock); | ||
69 | |||
70 | sk_for_each(sknode, node, &pnsocks.hlist) { | ||
71 | struct pn_sock *pn = pn_sk(sknode); | ||
72 | BUG_ON(!pn->sobject); /* unbound socket */ | ||
73 | |||
74 | if (pn_port(obj)) { | ||
75 | /* Look up socket by port */ | ||
76 | if (pn_port(pn->sobject) != pn_port(obj)) | ||
77 | continue; | ||
78 | } else { | ||
79 | /* If port is zero, look up by resource */ | ||
80 | if (pn->resource != res) | ||
81 | continue; | ||
82 | } | ||
83 | if (pn_addr(pn->sobject) | ||
84 | && pn_addr(pn->sobject) != pn_addr(obj)) | ||
85 | continue; | ||
86 | |||
87 | rval = sknode; | ||
88 | sock_hold(sknode); | ||
89 | break; | ||
90 | } | ||
91 | |||
92 | spin_unlock_bh(&pnsocks.lock); | ||
93 | |||
94 | return rval; | ||
95 | |||
96 | } | ||
97 | |||
98 | void pn_sock_hash(struct sock *sk) | ||
99 | { | ||
100 | spin_lock_bh(&pnsocks.lock); | ||
101 | sk_add_node(sk, &pnsocks.hlist); | ||
102 | spin_unlock_bh(&pnsocks.lock); | ||
103 | } | ||
104 | EXPORT_SYMBOL(pn_sock_hash); | ||
105 | |||
106 | void pn_sock_unhash(struct sock *sk) | ||
107 | { | ||
108 | spin_lock_bh(&pnsocks.lock); | ||
109 | sk_del_node_init(sk); | ||
110 | spin_unlock_bh(&pnsocks.lock); | ||
111 | } | ||
112 | EXPORT_SYMBOL(pn_sock_unhash); | ||
113 | |||
114 | static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len) | ||
115 | { | ||
116 | struct sock *sk = sock->sk; | ||
117 | struct pn_sock *pn = pn_sk(sk); | ||
118 | struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; | ||
119 | int err; | ||
120 | u16 handle; | ||
121 | u8 saddr; | ||
122 | |||
123 | if (sk->sk_prot->bind) | ||
124 | return sk->sk_prot->bind(sk, addr, len); | ||
125 | |||
126 | if (len < sizeof(struct sockaddr_pn)) | ||
127 | return -EINVAL; | ||
128 | if (spn->spn_family != AF_PHONET) | ||
129 | return -EAFNOSUPPORT; | ||
130 | |||
131 | handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr); | ||
132 | saddr = pn_addr(handle); | ||
133 | if (saddr && phonet_address_lookup(saddr)) | ||
134 | return -EADDRNOTAVAIL; | ||
135 | |||
136 | lock_sock(sk); | ||
137 | if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) { | ||
138 | err = -EINVAL; /* attempt to rebind */ | ||
139 | goto out; | ||
140 | } | ||
141 | err = sk->sk_prot->get_port(sk, pn_port(handle)); | ||
142 | if (err) | ||
143 | goto out; | ||
144 | |||
145 | /* get_port() sets the port, bind() sets the address if applicable */ | ||
146 | pn->sobject = pn_object(saddr, pn_port(pn->sobject)); | ||
147 | pn->resource = spn->spn_resource; | ||
148 | |||
149 | /* Enable RX on the socket */ | ||
150 | sk->sk_prot->hash(sk); | ||
151 | out: | ||
152 | release_sock(sk); | ||
153 | return err; | ||
154 | } | ||
155 | |||
156 | static int pn_socket_autobind(struct socket *sock) | ||
157 | { | ||
158 | struct sockaddr_pn sa; | ||
159 | int err; | ||
160 | |||
161 | memset(&sa, 0, sizeof(sa)); | ||
162 | sa.spn_family = AF_PHONET; | ||
163 | err = pn_socket_bind(sock, (struct sockaddr *)&sa, | ||
164 | sizeof(struct sockaddr_pn)); | ||
165 | if (err != -EINVAL) | ||
166 | return err; | ||
167 | BUG_ON(!pn_port(pn_sk(sock->sk)->sobject)); | ||
168 | return 0; /* socket was already bound */ | ||
169 | } | ||
170 | |||
171 | static int pn_socket_accept(struct socket *sock, struct socket *newsock, | ||
172 | int flags) | ||
173 | { | ||
174 | struct sock *sk = sock->sk; | ||
175 | struct sock *newsk; | ||
176 | int err; | ||
177 | |||
178 | newsk = sk->sk_prot->accept(sk, flags, &err); | ||
179 | if (!newsk) | ||
180 | return err; | ||
181 | |||
182 | lock_sock(newsk); | ||
183 | sock_graft(newsk, newsock); | ||
184 | newsock->state = SS_CONNECTED; | ||
185 | release_sock(newsk); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int pn_socket_getname(struct socket *sock, struct sockaddr *addr, | ||
190 | int *sockaddr_len, int peer) | ||
191 | { | ||
192 | struct sock *sk = sock->sk; | ||
193 | struct pn_sock *pn = pn_sk(sk); | ||
194 | |||
195 | memset(addr, 0, sizeof(struct sockaddr_pn)); | ||
196 | addr->sa_family = AF_PHONET; | ||
197 | if (!peer) /* Race with bind() here is userland's problem. */ | ||
198 | pn_sockaddr_set_object((struct sockaddr_pn *)addr, | ||
199 | pn->sobject); | ||
200 | |||
201 | *sockaddr_len = sizeof(struct sockaddr_pn); | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static unsigned int pn_socket_poll(struct file *file, struct socket *sock, | ||
206 | poll_table *wait) | ||
207 | { | ||
208 | struct sock *sk = sock->sk; | ||
209 | struct pep_sock *pn = pep_sk(sk); | ||
210 | unsigned int mask = 0; | ||
211 | |||
212 | poll_wait(file, &sock->wait, wait); | ||
213 | |||
214 | switch (sk->sk_state) { | ||
215 | case TCP_LISTEN: | ||
216 | return hlist_empty(&pn->ackq) ? 0 : POLLIN; | ||
217 | case TCP_CLOSE: | ||
218 | return POLLERR; | ||
219 | } | ||
220 | |||
221 | if (!skb_queue_empty(&sk->sk_receive_queue)) | ||
222 | mask |= POLLIN | POLLRDNORM; | ||
223 | if (!skb_queue_empty(&pn->ctrlreq_queue)) | ||
224 | mask |= POLLPRI; | ||
225 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) | ||
226 | return POLLHUP; | ||
227 | |||
228 | if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits) | ||
229 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; | ||
230 | |||
231 | return mask; | ||
232 | } | ||
233 | |||
234 | static int pn_socket_ioctl(struct socket *sock, unsigned int cmd, | ||
235 | unsigned long arg) | ||
236 | { | ||
237 | struct sock *sk = sock->sk; | ||
238 | struct pn_sock *pn = pn_sk(sk); | ||
239 | |||
240 | if (cmd == SIOCPNGETOBJECT) { | ||
241 | struct net_device *dev; | ||
242 | u16 handle; | ||
243 | u8 saddr; | ||
244 | |||
245 | if (get_user(handle, (__u16 __user *)arg)) | ||
246 | return -EFAULT; | ||
247 | |||
248 | lock_sock(sk); | ||
249 | if (sk->sk_bound_dev_if) | ||
250 | dev = dev_get_by_index(sock_net(sk), | ||
251 | sk->sk_bound_dev_if); | ||
252 | else | ||
253 | dev = phonet_device_get(sock_net(sk)); | ||
254 | if (dev && (dev->flags & IFF_UP)) | ||
255 | saddr = phonet_address_get(dev, pn_addr(handle)); | ||
256 | else | ||
257 | saddr = PN_NO_ADDR; | ||
258 | release_sock(sk); | ||
259 | |||
260 | if (dev) | ||
261 | dev_put(dev); | ||
262 | if (saddr == PN_NO_ADDR) | ||
263 | return -EHOSTUNREACH; | ||
264 | |||
265 | handle = pn_object(saddr, pn_port(pn->sobject)); | ||
266 | return put_user(handle, (__u16 __user *)arg); | ||
267 | } | ||
268 | |||
269 | return sk->sk_prot->ioctl(sk, cmd, arg); | ||
270 | } | ||
271 | |||
272 | static int pn_socket_listen(struct socket *sock, int backlog) | ||
273 | { | ||
274 | struct sock *sk = sock->sk; | ||
275 | int err = 0; | ||
276 | |||
277 | if (sock->state != SS_UNCONNECTED) | ||
278 | return -EINVAL; | ||
279 | if (pn_socket_autobind(sock)) | ||
280 | return -ENOBUFS; | ||
281 | |||
282 | lock_sock(sk); | ||
283 | if (sk->sk_state != TCP_CLOSE) { | ||
284 | err = -EINVAL; | ||
285 | goto out; | ||
286 | } | ||
287 | |||
288 | sk->sk_state = TCP_LISTEN; | ||
289 | sk->sk_ack_backlog = 0; | ||
290 | sk->sk_max_ack_backlog = backlog; | ||
291 | out: | ||
292 | release_sock(sk); | ||
293 | return err; | ||
294 | } | ||
295 | |||
296 | static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
297 | struct msghdr *m, size_t total_len) | ||
298 | { | ||
299 | struct sock *sk = sock->sk; | ||
300 | |||
301 | if (pn_socket_autobind(sock)) | ||
302 | return -EAGAIN; | ||
303 | |||
304 | return sk->sk_prot->sendmsg(iocb, sk, m, total_len); | ||
305 | } | ||
306 | |||
307 | const struct proto_ops phonet_dgram_ops = { | ||
308 | .family = AF_PHONET, | ||
309 | .owner = THIS_MODULE, | ||
310 | .release = pn_socket_release, | ||
311 | .bind = pn_socket_bind, | ||
312 | .connect = sock_no_connect, | ||
313 | .socketpair = sock_no_socketpair, | ||
314 | .accept = sock_no_accept, | ||
315 | .getname = pn_socket_getname, | ||
316 | .poll = datagram_poll, | ||
317 | .ioctl = pn_socket_ioctl, | ||
318 | .listen = sock_no_listen, | ||
319 | .shutdown = sock_no_shutdown, | ||
320 | .setsockopt = sock_no_setsockopt, | ||
321 | .getsockopt = sock_no_getsockopt, | ||
322 | #ifdef CONFIG_COMPAT | ||
323 | .compat_setsockopt = sock_no_setsockopt, | ||
324 | .compat_getsockopt = sock_no_getsockopt, | ||
325 | #endif | ||
326 | .sendmsg = pn_socket_sendmsg, | ||
327 | .recvmsg = sock_common_recvmsg, | ||
328 | .mmap = sock_no_mmap, | ||
329 | .sendpage = sock_no_sendpage, | ||
330 | }; | ||
331 | |||
332 | const struct proto_ops phonet_stream_ops = { | ||
333 | .family = AF_PHONET, | ||
334 | .owner = THIS_MODULE, | ||
335 | .release = pn_socket_release, | ||
336 | .bind = pn_socket_bind, | ||
337 | .connect = sock_no_connect, | ||
338 | .socketpair = sock_no_socketpair, | ||
339 | .accept = pn_socket_accept, | ||
340 | .getname = pn_socket_getname, | ||
341 | .poll = pn_socket_poll, | ||
342 | .ioctl = pn_socket_ioctl, | ||
343 | .listen = pn_socket_listen, | ||
344 | .shutdown = sock_no_shutdown, | ||
345 | .setsockopt = sock_common_setsockopt, | ||
346 | .getsockopt = sock_common_getsockopt, | ||
347 | #ifdef CONFIG_COMPAT | ||
348 | .compat_setsockopt = compat_sock_common_setsockopt, | ||
349 | .compat_getsockopt = compat_sock_common_getsockopt, | ||
350 | #endif | ||
351 | .sendmsg = pn_socket_sendmsg, | ||
352 | .recvmsg = sock_common_recvmsg, | ||
353 | .mmap = sock_no_mmap, | ||
354 | .sendpage = sock_no_sendpage, | ||
355 | }; | ||
356 | EXPORT_SYMBOL(phonet_stream_ops); | ||
357 | |||
358 | static DEFINE_MUTEX(port_mutex); | ||
359 | |||
360 | /* allocate port for a socket */ | ||
361 | int pn_sock_get_port(struct sock *sk, unsigned short sport) | ||
362 | { | ||
363 | static int port_cur; | ||
364 | struct pn_sock *pn = pn_sk(sk); | ||
365 | struct sockaddr_pn try_sa; | ||
366 | struct sock *tmpsk; | ||
367 | |||
368 | memset(&try_sa, 0, sizeof(struct sockaddr_pn)); | ||
369 | try_sa.spn_family = AF_PHONET; | ||
370 | |||
371 | mutex_lock(&port_mutex); | ||
372 | |||
373 | if (!sport) { | ||
374 | /* search free port */ | ||
375 | int port, pmin, pmax; | ||
376 | |||
377 | phonet_get_local_port_range(&pmin, &pmax); | ||
378 | for (port = pmin; port <= pmax; port++) { | ||
379 | port_cur++; | ||
380 | if (port_cur < pmin || port_cur > pmax) | ||
381 | port_cur = pmin; | ||
382 | |||
383 | pn_sockaddr_set_port(&try_sa, port_cur); | ||
384 | tmpsk = pn_find_sock_by_sa(&try_sa); | ||
385 | if (tmpsk == NULL) { | ||
386 | sport = port_cur; | ||
387 | goto found; | ||
388 | } else | ||
389 | sock_put(tmpsk); | ||
390 | } | ||
391 | } else { | ||
392 | /* try to find specific port */ | ||
393 | pn_sockaddr_set_port(&try_sa, sport); | ||
394 | tmpsk = pn_find_sock_by_sa(&try_sa); | ||
395 | if (tmpsk == NULL) | ||
396 | /* No sock there! We can use that port... */ | ||
397 | goto found; | ||
398 | else | ||
399 | sock_put(tmpsk); | ||
400 | } | ||
401 | mutex_unlock(&port_mutex); | ||
402 | |||
403 | /* the port must be in use already */ | ||
404 | return -EADDRINUSE; | ||
405 | |||
406 | found: | ||
407 | mutex_unlock(&port_mutex); | ||
408 | pn->sobject = pn_object(pn_addr(pn->sobject), sport); | ||
409 | return 0; | ||
410 | } | ||
411 | EXPORT_SYMBOL(pn_sock_get_port); | ||
diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c new file mode 100644 index 000000000000..600a4309b8c8 --- /dev/null +++ b/net/phonet/sysctl.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * File: sysctl.c | ||
3 | * | ||
4 | * Phonet /proc/sys/net/phonet interface implementation | ||
5 | * | ||
6 | * Copyright (C) 2008 Nokia Corporation. | ||
7 | * | ||
8 | * Contact: Remi 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/seqlock.h> | ||
26 | #include <linux/sysctl.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/init.h> | ||
29 | |||
30 | #define DYNAMIC_PORT_MIN 0x40 | ||
31 | #define DYNAMIC_PORT_MAX 0x7f | ||
32 | |||
33 | static DEFINE_SEQLOCK(local_port_range_lock); | ||
34 | static int local_port_range_min[2] = {0, 0}; | ||
35 | static int local_port_range_max[2] = {1023, 1023}; | ||
36 | static int local_port_range[2] = {DYNAMIC_PORT_MIN, DYNAMIC_PORT_MAX}; | ||
37 | static struct ctl_table_header *phonet_table_hrd; | ||
38 | |||
39 | static void set_local_port_range(int range[2]) | ||
40 | { | ||
41 | write_seqlock(&local_port_range_lock); | ||
42 | local_port_range[0] = range[0]; | ||
43 | local_port_range[1] = range[1]; | ||
44 | write_sequnlock(&local_port_range_lock); | ||
45 | } | ||
46 | |||
47 | void phonet_get_local_port_range(int *min, int *max) | ||
48 | { | ||
49 | unsigned seq; | ||
50 | do { | ||
51 | seq = read_seqbegin(&local_port_range_lock); | ||
52 | if (min) | ||
53 | *min = local_port_range[0]; | ||
54 | if (max) | ||
55 | *max = local_port_range[1]; | ||
56 | } while (read_seqretry(&local_port_range_lock, seq)); | ||
57 | } | ||
58 | |||
59 | static int proc_local_port_range(ctl_table *table, int write, struct file *filp, | ||
60 | void __user *buffer, | ||
61 | size_t *lenp, loff_t *ppos) | ||
62 | { | ||
63 | int ret; | ||
64 | int range[2] = {local_port_range[0], local_port_range[1]}; | ||
65 | ctl_table tmp = { | ||
66 | .data = &range, | ||
67 | .maxlen = sizeof(range), | ||
68 | .mode = table->mode, | ||
69 | .extra1 = &local_port_range_min, | ||
70 | .extra2 = &local_port_range_max, | ||
71 | }; | ||
72 | |||
73 | ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos); | ||
74 | |||
75 | if (write && ret == 0) { | ||
76 | if (range[1] < range[0]) | ||
77 | ret = -EINVAL; | ||
78 | else | ||
79 | set_local_port_range(range); | ||
80 | } | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static struct ctl_table phonet_table[] = { | ||
86 | { | ||
87 | .ctl_name = CTL_UNNUMBERED, | ||
88 | .procname = "local_port_range", | ||
89 | .data = &local_port_range, | ||
90 | .maxlen = sizeof(local_port_range), | ||
91 | .mode = 0644, | ||
92 | .proc_handler = &proc_local_port_range, | ||
93 | .strategy = NULL, | ||
94 | }, | ||
95 | { .ctl_name = 0 } | ||
96 | }; | ||
97 | |||
98 | struct ctl_path phonet_ctl_path[] = { | ||
99 | { .procname = "net", .ctl_name = CTL_NET, }, | ||
100 | { .procname = "phonet", .ctl_name = CTL_UNNUMBERED, }, | ||
101 | { }, | ||
102 | }; | ||
103 | |||
104 | int __init phonet_sysctl_init(void) | ||
105 | { | ||
106 | phonet_table_hrd = register_sysctl_paths(phonet_ctl_path, phonet_table); | ||
107 | return phonet_table_hrd == NULL ? -ENOMEM : 0; | ||
108 | } | ||
109 | |||
110 | void phonet_sysctl_exit(void) | ||
111 | { | ||
112 | unregister_sysctl_table(phonet_table_hrd); | ||
113 | } | ||