diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 135 |
1 files changed, 119 insertions, 16 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 794bcb897ff0..4568120d8533 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include <linux/security.h> | 35 | #include <linux/security.h> |
| 36 | #include <linux/mutex.h> | 36 | #include <linux/mutex.h> |
| 37 | #include <linux/if_addr.h> | 37 | #include <linux/if_addr.h> |
| 38 | #include <linux/pci.h> | ||
| 38 | 39 | ||
| 39 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
| 40 | #include <asm/system.h> | 41 | #include <asm/system.h> |
| @@ -89,6 +90,14 @@ int rtnl_is_locked(void) | |||
| 89 | } | 90 | } |
| 90 | EXPORT_SYMBOL(rtnl_is_locked); | 91 | EXPORT_SYMBOL(rtnl_is_locked); |
| 91 | 92 | ||
| 93 | #ifdef CONFIG_PROVE_LOCKING | ||
| 94 | int lockdep_rtnl_is_held(void) | ||
| 95 | { | ||
| 96 | return lockdep_is_held(&rtnl_mutex); | ||
| 97 | } | ||
| 98 | EXPORT_SYMBOL(lockdep_rtnl_is_held); | ||
| 99 | #endif /* #ifdef CONFIG_PROVE_LOCKING */ | ||
| 100 | |||
| 92 | static struct rtnl_link *rtnl_msg_handlers[NPROTO]; | 101 | static struct rtnl_link *rtnl_msg_handlers[NPROTO]; |
| 93 | 102 | ||
| 94 | static inline int rtm_msgindex(int msgtype) | 103 | static inline int rtm_msgindex(int msgtype) |
| @@ -548,6 +557,19 @@ static void set_operstate(struct net_device *dev, unsigned char transition) | |||
| 548 | } | 557 | } |
| 549 | } | 558 | } |
| 550 | 559 | ||
| 560 | static unsigned int rtnl_dev_combine_flags(const struct net_device *dev, | ||
| 561 | const struct ifinfomsg *ifm) | ||
| 562 | { | ||
| 563 | unsigned int flags = ifm->ifi_flags; | ||
| 564 | |||
| 565 | /* bugwards compatibility: ifi_change == 0 is treated as ~0 */ | ||
| 566 | if (ifm->ifi_change) | ||
| 567 | flags = (flags & ifm->ifi_change) | | ||
| 568 | (dev->flags & ~ifm->ifi_change); | ||
| 569 | |||
| 570 | return flags; | ||
| 571 | } | ||
| 572 | |||
| 551 | static void copy_rtnl_link_stats(struct rtnl_link_stats *a, | 573 | static void copy_rtnl_link_stats(struct rtnl_link_stats *a, |
| 552 | const struct net_device_stats *b) | 574 | const struct net_device_stats *b) |
| 553 | { | 575 | { |
| @@ -580,6 +602,15 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, | |||
| 580 | a->tx_compressed = b->tx_compressed; | 602 | a->tx_compressed = b->tx_compressed; |
| 581 | }; | 603 | }; |
| 582 | 604 | ||
| 605 | static inline int rtnl_vfinfo_size(const struct net_device *dev) | ||
| 606 | { | ||
| 607 | if (dev->dev.parent && dev_is_pci(dev->dev.parent)) | ||
| 608 | return dev_num_vf(dev->dev.parent) * | ||
| 609 | sizeof(struct ifla_vf_info); | ||
| 610 | else | ||
| 611 | return 0; | ||
| 612 | } | ||
| 613 | |||
| 583 | static inline size_t if_nlmsg_size(const struct net_device *dev) | 614 | static inline size_t if_nlmsg_size(const struct net_device *dev) |
| 584 | { | 615 | { |
| 585 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 616 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
| @@ -597,6 +628,8 @@ static inline size_t if_nlmsg_size(const struct net_device *dev) | |||
| 597 | + nla_total_size(4) /* IFLA_MASTER */ | 628 | + nla_total_size(4) /* IFLA_MASTER */ |
| 598 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 629 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
| 599 | + nla_total_size(1) /* IFLA_LINKMODE */ | 630 | + nla_total_size(1) /* IFLA_LINKMODE */ |
| 631 | + nla_total_size(4) /* IFLA_NUM_VF */ | ||
| 632 | + nla_total_size(rtnl_vfinfo_size(dev)) /* IFLA_VFINFO */ | ||
| 600 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ | 633 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ |
| 601 | } | 634 | } |
| 602 | 635 | ||
| @@ -665,6 +698,17 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
| 665 | stats = dev_get_stats(dev); | 698 | stats = dev_get_stats(dev); |
| 666 | copy_rtnl_link_stats(nla_data(attr), stats); | 699 | copy_rtnl_link_stats(nla_data(attr), stats); |
| 667 | 700 | ||
| 701 | if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) { | ||
| 702 | int i; | ||
| 703 | struct ifla_vf_info ivi; | ||
| 704 | |||
| 705 | NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent)); | ||
| 706 | for (i = 0; i < dev_num_vf(dev->dev.parent); i++) { | ||
| 707 | if (dev->netdev_ops->ndo_get_vf_config(dev, i, &ivi)) | ||
| 708 | break; | ||
| 709 | NLA_PUT(skb, IFLA_VFINFO, sizeof(ivi), &ivi); | ||
| 710 | } | ||
| 711 | } | ||
| 668 | if (dev->rtnl_link_ops) { | 712 | if (dev->rtnl_link_ops) { |
| 669 | if (rtnl_link_fill(skb, dev) < 0) | 713 | if (rtnl_link_fill(skb, dev) < 0) |
| 670 | goto nla_put_failure; | 714 | goto nla_put_failure; |
| @@ -725,6 +769,12 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
| 725 | [IFLA_LINKINFO] = { .type = NLA_NESTED }, | 769 | [IFLA_LINKINFO] = { .type = NLA_NESTED }, |
| 726 | [IFLA_NET_NS_PID] = { .type = NLA_U32 }, | 770 | [IFLA_NET_NS_PID] = { .type = NLA_U32 }, |
| 727 | [IFLA_IFALIAS] = { .type = NLA_STRING, .len = IFALIASZ-1 }, | 771 | [IFLA_IFALIAS] = { .type = NLA_STRING, .len = IFALIASZ-1 }, |
| 772 | [IFLA_VF_MAC] = { .type = NLA_BINARY, | ||
| 773 | .len = sizeof(struct ifla_vf_mac) }, | ||
| 774 | [IFLA_VF_VLAN] = { .type = NLA_BINARY, | ||
| 775 | .len = sizeof(struct ifla_vf_vlan) }, | ||
| 776 | [IFLA_VF_TX_RATE] = { .type = NLA_BINARY, | ||
| 777 | .len = sizeof(struct ifla_vf_tx_rate) }, | ||
| 728 | }; | 778 | }; |
| 729 | EXPORT_SYMBOL(ifla_policy); | 779 | EXPORT_SYMBOL(ifla_policy); |
| 730 | 780 | ||
| @@ -875,13 +925,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
| 875 | } | 925 | } |
| 876 | 926 | ||
| 877 | if (ifm->ifi_flags || ifm->ifi_change) { | 927 | if (ifm->ifi_flags || ifm->ifi_change) { |
| 878 | unsigned int flags = ifm->ifi_flags; | 928 | err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm)); |
| 879 | |||
| 880 | /* bugwards compatibility: ifi_change == 0 is treated as ~0 */ | ||
| 881 | if (ifm->ifi_change) | ||
| 882 | flags = (flags & ifm->ifi_change) | | ||
| 883 | (dev->flags & ~ifm->ifi_change); | ||
| 884 | err = dev_change_flags(dev, flags); | ||
| 885 | if (err < 0) | 929 | if (err < 0) |
| 886 | goto errout; | 930 | goto errout; |
| 887 | } | 931 | } |
| @@ -898,6 +942,41 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
| 898 | write_unlock_bh(&dev_base_lock); | 942 | write_unlock_bh(&dev_base_lock); |
| 899 | } | 943 | } |
| 900 | 944 | ||
| 945 | if (tb[IFLA_VF_MAC]) { | ||
| 946 | struct ifla_vf_mac *ivm; | ||
| 947 | ivm = nla_data(tb[IFLA_VF_MAC]); | ||
| 948 | err = -EOPNOTSUPP; | ||
| 949 | if (ops->ndo_set_vf_mac) | ||
| 950 | err = ops->ndo_set_vf_mac(dev, ivm->vf, ivm->mac); | ||
| 951 | if (err < 0) | ||
| 952 | goto errout; | ||
| 953 | modified = 1; | ||
| 954 | } | ||
| 955 | |||
| 956 | if (tb[IFLA_VF_VLAN]) { | ||
| 957 | struct ifla_vf_vlan *ivv; | ||
| 958 | ivv = nla_data(tb[IFLA_VF_VLAN]); | ||
| 959 | err = -EOPNOTSUPP; | ||
| 960 | if (ops->ndo_set_vf_vlan) | ||
| 961 | err = ops->ndo_set_vf_vlan(dev, ivv->vf, | ||
| 962 | ivv->vlan, | ||
| 963 | ivv->qos); | ||
| 964 | if (err < 0) | ||
| 965 | goto errout; | ||
| 966 | modified = 1; | ||
| 967 | } | ||
| 968 | err = 0; | ||
| 969 | |||
| 970 | if (tb[IFLA_VF_TX_RATE]) { | ||
| 971 | struct ifla_vf_tx_rate *ivt; | ||
| 972 | ivt = nla_data(tb[IFLA_VF_TX_RATE]); | ||
| 973 | err = -EOPNOTSUPP; | ||
| 974 | if (ops->ndo_set_vf_tx_rate) | ||
| 975 | err = ops->ndo_set_vf_tx_rate(dev, ivt->vf, ivt->rate); | ||
| 976 | if (err < 0) | ||
| 977 | goto errout; | ||
| 978 | modified = 1; | ||
| 979 | } | ||
| 901 | err = 0; | 980 | err = 0; |
| 902 | 981 | ||
| 903 | errout: | 982 | errout: |
| @@ -989,6 +1068,26 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
| 989 | return 0; | 1068 | return 0; |
| 990 | } | 1069 | } |
| 991 | 1070 | ||
| 1071 | int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) | ||
| 1072 | { | ||
| 1073 | unsigned int old_flags; | ||
| 1074 | int err; | ||
| 1075 | |||
| 1076 | old_flags = dev->flags; | ||
| 1077 | if (ifm && (ifm->ifi_flags || ifm->ifi_change)) { | ||
| 1078 | err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm)); | ||
| 1079 | if (err < 0) | ||
| 1080 | return err; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | dev->rtnl_link_state = RTNL_LINK_INITIALIZED; | ||
| 1084 | rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U); | ||
| 1085 | |||
| 1086 | __dev_notify_flags(dev, old_flags); | ||
| 1087 | return 0; | ||
| 1088 | } | ||
| 1089 | EXPORT_SYMBOL(rtnl_configure_link); | ||
| 1090 | |||
| 992 | struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | 1091 | struct net_device *rtnl_create_link(struct net *src_net, struct net *net, |
| 993 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) | 1092 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) |
| 994 | { | 1093 | { |
| @@ -1010,6 +1109,7 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | |||
| 1010 | 1109 | ||
| 1011 | dev_net_set(dev, net); | 1110 | dev_net_set(dev, net); |
| 1012 | dev->rtnl_link_ops = ops; | 1111 | dev->rtnl_link_ops = ops; |
| 1112 | dev->rtnl_link_state = RTNL_LINK_INITIALIZING; | ||
| 1013 | dev->real_num_tx_queues = real_num_queues; | 1113 | dev->real_num_tx_queues = real_num_queues; |
| 1014 | 1114 | ||
| 1015 | if (strchr(dev->name, '%')) { | 1115 | if (strchr(dev->name, '%')) { |
| @@ -1139,7 +1239,7 @@ replay: | |||
| 1139 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) | 1239 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) |
| 1140 | return -ENODEV; | 1240 | return -ENODEV; |
| 1141 | 1241 | ||
| 1142 | if (ifm->ifi_index || ifm->ifi_flags || ifm->ifi_change) | 1242 | if (ifm->ifi_index) |
| 1143 | return -EOPNOTSUPP; | 1243 | return -EOPNOTSUPP; |
| 1144 | if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) | 1244 | if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) |
| 1145 | return -EOPNOTSUPP; | 1245 | return -EOPNOTSUPP; |
| @@ -1170,9 +1270,15 @@ replay: | |||
| 1170 | err = ops->newlink(net, dev, tb, data); | 1270 | err = ops->newlink(net, dev, tb, data); |
| 1171 | else | 1271 | else |
| 1172 | err = register_netdevice(dev); | 1272 | err = register_netdevice(dev); |
| 1173 | if (err < 0 && !IS_ERR(dev)) | 1273 | if (err < 0 && !IS_ERR(dev)) { |
| 1174 | free_netdev(dev); | 1274 | free_netdev(dev); |
| 1275 | goto out; | ||
| 1276 | } | ||
| 1175 | 1277 | ||
| 1278 | err = rtnl_configure_link(dev, ifm); | ||
| 1279 | if (err < 0) | ||
| 1280 | unregister_netdevice(dev); | ||
| 1281 | out: | ||
| 1176 | put_net(dest_net); | 1282 | put_net(dest_net); |
| 1177 | return err; | 1283 | return err; |
| 1178 | } | 1284 | } |
| @@ -1361,17 +1467,14 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi | |||
| 1361 | struct net_device *dev = ptr; | 1467 | struct net_device *dev = ptr; |
| 1362 | 1468 | ||
| 1363 | switch (event) { | 1469 | switch (event) { |
| 1364 | case NETDEV_UNREGISTER: | ||
| 1365 | rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); | ||
| 1366 | break; | ||
| 1367 | case NETDEV_UP: | 1470 | case NETDEV_UP: |
| 1368 | case NETDEV_DOWN: | 1471 | case NETDEV_DOWN: |
| 1369 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); | 1472 | case NETDEV_PRE_UP: |
| 1370 | break; | ||
| 1371 | case NETDEV_POST_INIT: | 1473 | case NETDEV_POST_INIT: |
| 1372 | case NETDEV_REGISTER: | 1474 | case NETDEV_REGISTER: |
| 1373 | case NETDEV_CHANGE: | 1475 | case NETDEV_CHANGE: |
| 1374 | case NETDEV_GOING_DOWN: | 1476 | case NETDEV_GOING_DOWN: |
| 1477 | case NETDEV_UNREGISTER: | ||
| 1375 | case NETDEV_UNREGISTER_BATCH: | 1478 | case NETDEV_UNREGISTER_BATCH: |
| 1376 | break; | 1479 | break; |
| 1377 | default: | 1480 | default: |
| @@ -1386,7 +1489,7 @@ static struct notifier_block rtnetlink_dev_notifier = { | |||
| 1386 | }; | 1489 | }; |
| 1387 | 1490 | ||
| 1388 | 1491 | ||
| 1389 | static int rtnetlink_net_init(struct net *net) | 1492 | static int __net_init rtnetlink_net_init(struct net *net) |
| 1390 | { | 1493 | { |
| 1391 | struct sock *sk; | 1494 | struct sock *sk; |
| 1392 | sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, | 1495 | sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, |
| @@ -1397,7 +1500,7 @@ static int rtnetlink_net_init(struct net *net) | |||
| 1397 | return 0; | 1500 | return 0; |
| 1398 | } | 1501 | } |
| 1399 | 1502 | ||
| 1400 | static void rtnetlink_net_exit(struct net *net) | 1503 | static void __net_exit rtnetlink_net_exit(struct net *net) |
| 1401 | { | 1504 | { |
| 1402 | netlink_kernel_release(net->rtnl); | 1505 | netlink_kernel_release(net->rtnl); |
| 1403 | net->rtnl = NULL; | 1506 | net->rtnl = NULL; |
