diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 176 |
1 files changed, 164 insertions, 12 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index eca2976abb25..3fcfa9c59e1f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/skbuff.h> | 35 | #include <linux/skbuff.h> |
36 | #include <linux/init.h> | 36 | #include <linux/init.h> |
37 | #include <linux/security.h> | 37 | #include <linux/security.h> |
38 | #include <linux/mutex.h> | ||
38 | 39 | ||
39 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
40 | #include <asm/system.h> | 41 | #include <asm/system.h> |
@@ -50,26 +51,36 @@ | |||
50 | #include <net/sock.h> | 51 | #include <net/sock.h> |
51 | #include <net/pkt_sched.h> | 52 | #include <net/pkt_sched.h> |
52 | #include <net/netlink.h> | 53 | #include <net/netlink.h> |
54 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
55 | #include <linux/wireless.h> | ||
56 | #include <net/iw_handler.h> | ||
57 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
53 | 58 | ||
54 | DECLARE_MUTEX(rtnl_sem); | 59 | static DEFINE_MUTEX(rtnl_mutex); |
55 | 60 | ||
56 | void rtnl_lock(void) | 61 | void rtnl_lock(void) |
57 | { | 62 | { |
58 | rtnl_shlock(); | 63 | mutex_lock(&rtnl_mutex); |
59 | } | 64 | } |
60 | 65 | ||
61 | int rtnl_lock_interruptible(void) | 66 | void __rtnl_unlock(void) |
62 | { | 67 | { |
63 | return down_interruptible(&rtnl_sem); | 68 | mutex_unlock(&rtnl_mutex); |
64 | } | 69 | } |
65 | 70 | ||
66 | void rtnl_unlock(void) | 71 | void rtnl_unlock(void) |
67 | { | 72 | { |
68 | rtnl_shunlock(); | 73 | mutex_unlock(&rtnl_mutex); |
69 | 74 | if (rtnl && rtnl->sk_receive_queue.qlen) | |
75 | rtnl->sk_data_ready(rtnl, 0); | ||
70 | netdev_run_todo(); | 76 | netdev_run_todo(); |
71 | } | 77 | } |
72 | 78 | ||
79 | int rtnl_trylock(void) | ||
80 | { | ||
81 | return mutex_trylock(&rtnl_mutex); | ||
82 | } | ||
83 | |||
73 | int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) | 84 | int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) |
74 | { | 85 | { |
75 | memset(tb, 0, sizeof(struct rtattr*)*maxattr); | 86 | memset(tb, 0, sizeof(struct rtattr*)*maxattr); |
@@ -179,6 +190,33 @@ rtattr_failure: | |||
179 | } | 190 | } |
180 | 191 | ||
181 | 192 | ||
193 | static void set_operstate(struct net_device *dev, unsigned char transition) | ||
194 | { | ||
195 | unsigned char operstate = dev->operstate; | ||
196 | |||
197 | switch(transition) { | ||
198 | case IF_OPER_UP: | ||
199 | if ((operstate == IF_OPER_DORMANT || | ||
200 | operstate == IF_OPER_UNKNOWN) && | ||
201 | !netif_dormant(dev)) | ||
202 | operstate = IF_OPER_UP; | ||
203 | break; | ||
204 | |||
205 | case IF_OPER_DORMANT: | ||
206 | if (operstate == IF_OPER_UP || | ||
207 | operstate == IF_OPER_UNKNOWN) | ||
208 | operstate = IF_OPER_DORMANT; | ||
209 | break; | ||
210 | }; | ||
211 | |||
212 | if (dev->operstate != operstate) { | ||
213 | write_lock_bh(&dev_base_lock); | ||
214 | dev->operstate = operstate; | ||
215 | write_unlock_bh(&dev_base_lock); | ||
216 | netdev_state_change(dev); | ||
217 | } | ||
218 | } | ||
219 | |||
182 | static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | 220 | static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, |
183 | int type, u32 pid, u32 seq, u32 change, | 221 | int type, u32 pid, u32 seq, u32 change, |
184 | unsigned int flags) | 222 | unsigned int flags) |
@@ -209,6 +247,13 @@ static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
209 | } | 247 | } |
210 | 248 | ||
211 | if (1) { | 249 | if (1) { |
250 | u8 operstate = netif_running(dev)?dev->operstate:IF_OPER_DOWN; | ||
251 | u8 link_mode = dev->link_mode; | ||
252 | RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate); | ||
253 | RTA_PUT(skb, IFLA_LINKMODE, sizeof(link_mode), &link_mode); | ||
254 | } | ||
255 | |||
256 | if (1) { | ||
212 | struct rtnl_link_ifmap map = { | 257 | struct rtnl_link_ifmap map = { |
213 | .mem_start = dev->mem_start, | 258 | .mem_start = dev->mem_start, |
214 | .mem_end = dev->mem_end, | 259 | .mem_end = dev->mem_end, |
@@ -399,6 +444,22 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
399 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); | 444 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); |
400 | } | 445 | } |
401 | 446 | ||
447 | if (ida[IFLA_OPERSTATE - 1]) { | ||
448 | if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | ||
449 | goto out; | ||
450 | |||
451 | set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1]))); | ||
452 | } | ||
453 | |||
454 | if (ida[IFLA_LINKMODE - 1]) { | ||
455 | if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | ||
456 | goto out; | ||
457 | |||
458 | write_lock_bh(&dev_base_lock); | ||
459 | dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1])); | ||
460 | write_unlock_bh(&dev_base_lock); | ||
461 | } | ||
462 | |||
402 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { | 463 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { |
403 | char ifname[IFNAMSIZ]; | 464 | char ifname[IFNAMSIZ]; |
404 | 465 | ||
@@ -410,6 +471,17 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
410 | goto out; | 471 | goto out; |
411 | } | 472 | } |
412 | 473 | ||
474 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
475 | if (ida[IFLA_WIRELESS - 1]) { | ||
476 | |||
477 | /* Call Wireless Extensions. | ||
478 | * Various stuff checked in there... */ | ||
479 | err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len); | ||
480 | if (err) | ||
481 | goto out; | ||
482 | } | ||
483 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
484 | |||
413 | err = 0; | 485 | err = 0; |
414 | 486 | ||
415 | out: | 487 | out: |
@@ -420,6 +492,83 @@ out: | |||
420 | return err; | 492 | return err; |
421 | } | 493 | } |
422 | 494 | ||
495 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
496 | static int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg) | ||
497 | { | ||
498 | struct ifinfomsg *ifm = NLMSG_DATA(in_nlh); | ||
499 | struct rtattr **ida = arg; | ||
500 | struct net_device *dev; | ||
501 | struct ifinfomsg *r; | ||
502 | struct nlmsghdr *nlh; | ||
503 | int err = -ENOBUFS; | ||
504 | struct sk_buff *skb; | ||
505 | unsigned char *b; | ||
506 | char *iw_buf = NULL; | ||
507 | int iw_buf_len = 0; | ||
508 | |||
509 | if (ifm->ifi_index >= 0) | ||
510 | dev = dev_get_by_index(ifm->ifi_index); | ||
511 | else | ||
512 | return -EINVAL; | ||
513 | if (!dev) | ||
514 | return -ENODEV; | ||
515 | |||
516 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
517 | if (ida[IFLA_WIRELESS - 1]) { | ||
518 | |||
519 | /* Call Wireless Extensions. We need to know the size before | ||
520 | * we can alloc. Various stuff checked in there... */ | ||
521 | err = wireless_rtnetlink_get(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len, &iw_buf, &iw_buf_len); | ||
522 | if (err) | ||
523 | goto out; | ||
524 | } | ||
525 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
526 | |||
527 | /* Create a skb big enough to include all the data. | ||
528 | * Some requests are way bigger than 4k... Jean II */ | ||
529 | skb = alloc_skb((NLMSG_LENGTH(sizeof(*r))) + (RTA_SPACE(iw_buf_len)), | ||
530 | GFP_KERNEL); | ||
531 | if (!skb) | ||
532 | goto out; | ||
533 | b = skb->tail; | ||
534 | |||
535 | /* Put in the message the usual good stuff */ | ||
536 | nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, in_nlh->nlmsg_seq, | ||
537 | RTM_NEWLINK, sizeof(*r)); | ||
538 | r = NLMSG_DATA(nlh); | ||
539 | r->ifi_family = AF_UNSPEC; | ||
540 | r->__ifi_pad = 0; | ||
541 | r->ifi_type = dev->type; | ||
542 | r->ifi_index = dev->ifindex; | ||
543 | r->ifi_flags = dev->flags; | ||
544 | r->ifi_change = 0; | ||
545 | |||
546 | /* Put the wireless payload if it exist */ | ||
547 | if(iw_buf != NULL) | ||
548 | RTA_PUT(skb, IFLA_WIRELESS, iw_buf_len, | ||
549 | iw_buf + IW_EV_POINT_OFF); | ||
550 | |||
551 | nlh->nlmsg_len = skb->tail - b; | ||
552 | |||
553 | /* Needed ? */ | ||
554 | NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid; | ||
555 | |||
556 | err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | ||
557 | if (err > 0) | ||
558 | err = 0; | ||
559 | out: | ||
560 | if(iw_buf != NULL) | ||
561 | kfree(iw_buf); | ||
562 | dev_put(dev); | ||
563 | return err; | ||
564 | |||
565 | rtattr_failure: | ||
566 | nlmsg_failure: | ||
567 | kfree_skb(skb); | ||
568 | goto out; | ||
569 | } | ||
570 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
571 | |||
423 | static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | 572 | static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb) |
424 | { | 573 | { |
425 | int idx; | 574 | int idx; |
@@ -575,9 +724,9 @@ static void rtnetlink_rcv(struct sock *sk, int len) | |||
575 | unsigned int qlen = 0; | 724 | unsigned int qlen = 0; |
576 | 725 | ||
577 | do { | 726 | do { |
578 | rtnl_lock(); | 727 | mutex_lock(&rtnl_mutex); |
579 | netlink_run_queue(sk, &qlen, &rtnetlink_rcv_msg); | 728 | netlink_run_queue(sk, &qlen, &rtnetlink_rcv_msg); |
580 | up(&rtnl_sem); | 729 | mutex_unlock(&rtnl_mutex); |
581 | 730 | ||
582 | netdev_run_todo(); | 731 | netdev_run_todo(); |
583 | } while (qlen); | 732 | } while (qlen); |
@@ -585,7 +734,11 @@ static void rtnetlink_rcv(struct sock *sk, int len) | |||
585 | 734 | ||
586 | static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = | 735 | static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = |
587 | { | 736 | { |
588 | [RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo }, | 737 | [RTM_GETLINK - RTM_BASE] = { |
738 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
739 | .doit = do_getlink, | ||
740 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
741 | .dumpit = rtnetlink_dump_ifinfo }, | ||
589 | [RTM_SETLINK - RTM_BASE] = { .doit = do_setlink }, | 742 | [RTM_SETLINK - RTM_BASE] = { .doit = do_setlink }, |
590 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | 743 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, |
591 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | 744 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, |
@@ -654,6 +807,5 @@ EXPORT_SYMBOL(rtnetlink_links); | |||
654 | EXPORT_SYMBOL(rtnetlink_put_metrics); | 807 | EXPORT_SYMBOL(rtnetlink_put_metrics); |
655 | EXPORT_SYMBOL(rtnl); | 808 | EXPORT_SYMBOL(rtnl); |
656 | EXPORT_SYMBOL(rtnl_lock); | 809 | EXPORT_SYMBOL(rtnl_lock); |
657 | EXPORT_SYMBOL(rtnl_lock_interruptible); | 810 | EXPORT_SYMBOL(rtnl_trylock); |
658 | EXPORT_SYMBOL(rtnl_sem); | ||
659 | EXPORT_SYMBOL(rtnl_unlock); | 811 | EXPORT_SYMBOL(rtnl_unlock); |