diff options
Diffstat (limited to 'net/phonet/af_phonet.c')
-rw-r--r-- | net/phonet/af_phonet.c | 83 |
1 files changed, 66 insertions, 17 deletions
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index f60c0c2aacba..526d0273991a 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c | |||
@@ -35,7 +35,6 @@ | |||
35 | 35 | ||
36 | /* Transport protocol registration */ | 36 | /* Transport protocol registration */ |
37 | static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; | 37 | static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; |
38 | static DEFINE_SPINLOCK(proto_tab_lock); | ||
39 | 38 | ||
40 | static struct phonet_protocol *phonet_proto_get(int protocol) | 39 | static struct phonet_protocol *phonet_proto_get(int protocol) |
41 | { | 40 | { |
@@ -44,11 +43,11 @@ static struct phonet_protocol *phonet_proto_get(int protocol) | |||
44 | if (protocol >= PHONET_NPROTO) | 43 | if (protocol >= PHONET_NPROTO) |
45 | return NULL; | 44 | return NULL; |
46 | 45 | ||
47 | spin_lock(&proto_tab_lock); | 46 | rcu_read_lock(); |
48 | pp = proto_tab[protocol]; | 47 | pp = rcu_dereference(proto_tab[protocol]); |
49 | if (pp && !try_module_get(pp->prot->owner)) | 48 | if (pp && !try_module_get(pp->prot->owner)) |
50 | pp = NULL; | 49 | pp = NULL; |
51 | spin_unlock(&proto_tab_lock); | 50 | rcu_read_unlock(); |
52 | 51 | ||
53 | return pp; | 52 | return pp; |
54 | } | 53 | } |
@@ -60,7 +59,8 @@ static inline void phonet_proto_put(struct phonet_protocol *pp) | |||
60 | 59 | ||
61 | /* protocol family functions */ | 60 | /* protocol family functions */ |
62 | 61 | ||
63 | static int pn_socket_create(struct net *net, struct socket *sock, int protocol) | 62 | static int pn_socket_create(struct net *net, struct socket *sock, int protocol, |
63 | int kern) | ||
64 | { | 64 | { |
65 | struct sock *sk; | 65 | struct sock *sk; |
66 | struct pn_sock *pn; | 66 | struct pn_sock *pn; |
@@ -118,7 +118,7 @@ out: | |||
118 | return err; | 118 | return err; |
119 | } | 119 | } |
120 | 120 | ||
121 | static struct net_proto_family phonet_proto_family = { | 121 | static const struct net_proto_family phonet_proto_family = { |
122 | .family = PF_PHONET, | 122 | .family = PF_PHONET, |
123 | .create = pn_socket_create, | 123 | .create = pn_socket_create, |
124 | .owner = THIS_MODULE, | 124 | .owner = THIS_MODULE, |
@@ -190,9 +190,8 @@ static int pn_send(struct sk_buff *skb, struct net_device *dev, | |||
190 | skb->priority = 0; | 190 | skb->priority = 0; |
191 | skb->dev = dev; | 191 | skb->dev = dev; |
192 | 192 | ||
193 | if (pn_addr(src) == pn_addr(dst)) { | 193 | if (skb->pkt_type == PACKET_LOOPBACK) { |
194 | skb_reset_mac_header(skb); | 194 | skb_reset_mac_header(skb); |
195 | skb->pkt_type = PACKET_LOOPBACK; | ||
196 | skb_orphan(skb); | 195 | skb_orphan(skb); |
197 | if (irq) | 196 | if (irq) |
198 | netif_rx(skb); | 197 | netif_rx(skb); |
@@ -222,6 +221,9 @@ static int pn_raw_send(const void *data, int len, struct net_device *dev, | |||
222 | if (skb == NULL) | 221 | if (skb == NULL) |
223 | return -ENOMEM; | 222 | return -ENOMEM; |
224 | 223 | ||
224 | if (phonet_address_lookup(dev_net(dev), pn_addr(dst)) == 0) | ||
225 | skb->pkt_type = PACKET_LOOPBACK; | ||
226 | |||
225 | skb_reserve(skb, MAX_PHONET_HEADER); | 227 | skb_reserve(skb, MAX_PHONET_HEADER); |
226 | __skb_put(skb, len); | 228 | __skb_put(skb, len); |
227 | skb_copy_to_linear_data(skb, data, len); | 229 | skb_copy_to_linear_data(skb, data, len); |
@@ -235,6 +237,7 @@ static int pn_raw_send(const void *data, int len, struct net_device *dev, | |||
235 | int pn_skb_send(struct sock *sk, struct sk_buff *skb, | 237 | int pn_skb_send(struct sock *sk, struct sk_buff *skb, |
236 | const struct sockaddr_pn *target) | 238 | const struct sockaddr_pn *target) |
237 | { | 239 | { |
240 | struct net *net = sock_net(sk); | ||
238 | struct net_device *dev; | 241 | struct net_device *dev; |
239 | struct pn_sock *pn = pn_sk(sk); | 242 | struct pn_sock *pn = pn_sk(sk); |
240 | int err; | 243 | int err; |
@@ -243,9 +246,13 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, | |||
243 | 246 | ||
244 | err = -EHOSTUNREACH; | 247 | err = -EHOSTUNREACH; |
245 | if (sk->sk_bound_dev_if) | 248 | if (sk->sk_bound_dev_if) |
246 | dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if); | 249 | dev = dev_get_by_index(net, sk->sk_bound_dev_if); |
247 | else | 250 | else if (phonet_address_lookup(net, daddr) == 0) { |
248 | dev = phonet_device_get(sock_net(sk)); | 251 | dev = phonet_device_get(net); |
252 | skb->pkt_type = PACKET_LOOPBACK; | ||
253 | } else | ||
254 | dev = phonet_route_output(net, daddr); | ||
255 | |||
249 | if (!dev || !(dev->flags & IFF_UP)) | 256 | if (!dev || !(dev->flags & IFF_UP)) |
250 | goto drop; | 257 | goto drop; |
251 | 258 | ||
@@ -369,6 +376,12 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, | |||
369 | 376 | ||
370 | pn_skb_get_dst_sockaddr(skb, &sa); | 377 | pn_skb_get_dst_sockaddr(skb, &sa); |
371 | 378 | ||
379 | /* check if this is broadcasted */ | ||
380 | if (pn_sockaddr_get_addr(&sa) == PNADDR_BROADCAST) { | ||
381 | pn_deliver_sock_broadcast(net, skb); | ||
382 | goto out; | ||
383 | } | ||
384 | |||
372 | /* check if we are the destination */ | 385 | /* check if we are the destination */ |
373 | if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { | 386 | if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { |
374 | /* Phonet packet input */ | 387 | /* Phonet packet input */ |
@@ -381,6 +394,38 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, | |||
381 | send_obj_unreachable(skb); | 394 | send_obj_unreachable(skb); |
382 | send_reset_indications(skb); | 395 | send_reset_indications(skb); |
383 | } | 396 | } |
397 | } else if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) | ||
398 | goto out; /* Race between address deletion and loopback */ | ||
399 | else { | ||
400 | /* Phonet packet routing */ | ||
401 | struct net_device *out_dev; | ||
402 | |||
403 | out_dev = phonet_route_output(net, pn_sockaddr_get_addr(&sa)); | ||
404 | if (!out_dev) { | ||
405 | LIMIT_NETDEBUG(KERN_WARNING"No Phonet route to %02X\n", | ||
406 | pn_sockaddr_get_addr(&sa)); | ||
407 | goto out; | ||
408 | } | ||
409 | |||
410 | __skb_push(skb, sizeof(struct phonethdr)); | ||
411 | skb->dev = out_dev; | ||
412 | if (out_dev == dev) { | ||
413 | LIMIT_NETDEBUG(KERN_ERR"Phonet loop to %02X on %s\n", | ||
414 | pn_sockaddr_get_addr(&sa), dev->name); | ||
415 | goto out_dev; | ||
416 | } | ||
417 | /* Some drivers (e.g. TUN) do not allocate HW header space */ | ||
418 | if (skb_cow_head(skb, out_dev->hard_header_len)) | ||
419 | goto out_dev; | ||
420 | |||
421 | if (dev_hard_header(skb, out_dev, ETH_P_PHONET, NULL, NULL, | ||
422 | skb->len) < 0) | ||
423 | goto out_dev; | ||
424 | dev_queue_xmit(skb); | ||
425 | dev_put(out_dev); | ||
426 | return NET_RX_SUCCESS; | ||
427 | out_dev: | ||
428 | dev_put(out_dev); | ||
384 | } | 429 | } |
385 | 430 | ||
386 | out: | 431 | out: |
@@ -393,6 +438,8 @@ static struct packet_type phonet_packet_type __read_mostly = { | |||
393 | .func = phonet_rcv, | 438 | .func = phonet_rcv, |
394 | }; | 439 | }; |
395 | 440 | ||
441 | static DEFINE_MUTEX(proto_tab_lock); | ||
442 | |||
396 | int __init_or_module phonet_proto_register(int protocol, | 443 | int __init_or_module phonet_proto_register(int protocol, |
397 | struct phonet_protocol *pp) | 444 | struct phonet_protocol *pp) |
398 | { | 445 | { |
@@ -405,12 +452,12 @@ int __init_or_module phonet_proto_register(int protocol, | |||
405 | if (err) | 452 | if (err) |
406 | return err; | 453 | return err; |
407 | 454 | ||
408 | spin_lock(&proto_tab_lock); | 455 | mutex_lock(&proto_tab_lock); |
409 | if (proto_tab[protocol]) | 456 | if (proto_tab[protocol]) |
410 | err = -EBUSY; | 457 | err = -EBUSY; |
411 | else | 458 | else |
412 | proto_tab[protocol] = pp; | 459 | rcu_assign_pointer(proto_tab[protocol], pp); |
413 | spin_unlock(&proto_tab_lock); | 460 | mutex_unlock(&proto_tab_lock); |
414 | 461 | ||
415 | return err; | 462 | return err; |
416 | } | 463 | } |
@@ -418,10 +465,11 @@ EXPORT_SYMBOL(phonet_proto_register); | |||
418 | 465 | ||
419 | void phonet_proto_unregister(int protocol, struct phonet_protocol *pp) | 466 | void phonet_proto_unregister(int protocol, struct phonet_protocol *pp) |
420 | { | 467 | { |
421 | spin_lock(&proto_tab_lock); | 468 | mutex_lock(&proto_tab_lock); |
422 | BUG_ON(proto_tab[protocol] != pp); | 469 | BUG_ON(proto_tab[protocol] != pp); |
423 | proto_tab[protocol] = NULL; | 470 | rcu_assign_pointer(proto_tab[protocol], NULL); |
424 | spin_unlock(&proto_tab_lock); | 471 | mutex_unlock(&proto_tab_lock); |
472 | synchronize_rcu(); | ||
425 | proto_unregister(pp->prot); | 473 | proto_unregister(pp->prot); |
426 | } | 474 | } |
427 | EXPORT_SYMBOL(phonet_proto_unregister); | 475 | EXPORT_SYMBOL(phonet_proto_unregister); |
@@ -435,6 +483,7 @@ static int __init phonet_init(void) | |||
435 | if (err) | 483 | if (err) |
436 | return err; | 484 | return err; |
437 | 485 | ||
486 | pn_sock_init(); | ||
438 | err = sock_register(&phonet_proto_family); | 487 | err = sock_register(&phonet_proto_family); |
439 | if (err) { | 488 | if (err) { |
440 | printk(KERN_ALERT | 489 | printk(KERN_ALERT |