diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 126 |
1 files changed, 111 insertions, 15 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4c7d3f635ba7..fe776c9ddeca 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> |
| @@ -556,6 +557,19 @@ static void set_operstate(struct net_device *dev, unsigned char transition) | |||
| 556 | } | 557 | } |
| 557 | } | 558 | } |
| 558 | 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 | |||
| 559 | static void copy_rtnl_link_stats(struct rtnl_link_stats *a, | 573 | static void copy_rtnl_link_stats(struct rtnl_link_stats *a, |
| 560 | const struct net_device_stats *b) | 574 | const struct net_device_stats *b) |
| 561 | { | 575 | { |
| @@ -588,6 +602,15 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, | |||
| 588 | a->tx_compressed = b->tx_compressed; | 602 | a->tx_compressed = b->tx_compressed; |
| 589 | }; | 603 | }; |
| 590 | 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 | |||
| 591 | 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) |
| 592 | { | 615 | { |
| 593 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 616 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
| @@ -605,6 +628,8 @@ static inline size_t if_nlmsg_size(const struct net_device *dev) | |||
| 605 | + nla_total_size(4) /* IFLA_MASTER */ | 628 | + nla_total_size(4) /* IFLA_MASTER */ |
| 606 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 629 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
| 607 | + 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 */ | ||
| 608 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ | 633 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ |
| 609 | } | 634 | } |
| 610 | 635 | ||
| @@ -673,6 +698,17 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
| 673 | stats = dev_get_stats(dev); | 698 | stats = dev_get_stats(dev); |
| 674 | copy_rtnl_link_stats(nla_data(attr), stats); | 699 | copy_rtnl_link_stats(nla_data(attr), stats); |
| 675 | 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 | } | ||
| 676 | if (dev->rtnl_link_ops) { | 712 | if (dev->rtnl_link_ops) { |
| 677 | if (rtnl_link_fill(skb, dev) < 0) | 713 | if (rtnl_link_fill(skb, dev) < 0) |
| 678 | goto nla_put_failure; | 714 | goto nla_put_failure; |
| @@ -733,6 +769,12 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
| 733 | [IFLA_LINKINFO] = { .type = NLA_NESTED }, | 769 | [IFLA_LINKINFO] = { .type = NLA_NESTED }, |
| 734 | [IFLA_NET_NS_PID] = { .type = NLA_U32 }, | 770 | [IFLA_NET_NS_PID] = { .type = NLA_U32 }, |
| 735 | [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) }, | ||
| 736 | }; | 778 | }; |
| 737 | EXPORT_SYMBOL(ifla_policy); | 779 | EXPORT_SYMBOL(ifla_policy); |
| 738 | 780 | ||
| @@ -883,13 +925,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
| 883 | } | 925 | } |
| 884 | 926 | ||
| 885 | if (ifm->ifi_flags || ifm->ifi_change) { | 927 | if (ifm->ifi_flags || ifm->ifi_change) { |
| 886 | unsigned int flags = ifm->ifi_flags; | 928 | err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm)); |
| 887 | |||
| 888 | /* bugwards compatibility: ifi_change == 0 is treated as ~0 */ | ||
| 889 | if (ifm->ifi_change) | ||
| 890 | flags = (flags & ifm->ifi_change) | | ||
| 891 | (dev->flags & ~ifm->ifi_change); | ||
| 892 | err = dev_change_flags(dev, flags); | ||
| 893 | if (err < 0) | 929 | if (err < 0) |
| 894 | goto errout; | 930 | goto errout; |
| 895 | } | 931 | } |
| @@ -906,6 +942,41 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
| 906 | write_unlock_bh(&dev_base_lock); | 942 | write_unlock_bh(&dev_base_lock); |
| 907 | } | 943 | } |
| 908 | 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 | } | ||
| 909 | err = 0; | 980 | err = 0; |
| 910 | 981 | ||
| 911 | errout: | 982 | errout: |
| @@ -997,6 +1068,26 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
| 997 | return 0; | 1068 | return 0; |
| 998 | } | 1069 | } |
| 999 | 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 | |||
| 1000 | 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, |
| 1001 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) | 1092 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) |
| 1002 | { | 1093 | { |
| @@ -1018,6 +1109,7 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | |||
| 1018 | 1109 | ||
| 1019 | dev_net_set(dev, net); | 1110 | dev_net_set(dev, net); |
| 1020 | dev->rtnl_link_ops = ops; | 1111 | dev->rtnl_link_ops = ops; |
| 1112 | dev->rtnl_link_state = RTNL_LINK_INITIALIZING; | ||
| 1021 | dev->real_num_tx_queues = real_num_queues; | 1113 | dev->real_num_tx_queues = real_num_queues; |
| 1022 | 1114 | ||
| 1023 | if (strchr(dev->name, '%')) { | 1115 | if (strchr(dev->name, '%')) { |
| @@ -1147,7 +1239,7 @@ replay: | |||
| 1147 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) | 1239 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) |
| 1148 | return -ENODEV; | 1240 | return -ENODEV; |
| 1149 | 1241 | ||
| 1150 | if (ifm->ifi_index || ifm->ifi_flags || ifm->ifi_change) | 1242 | if (ifm->ifi_index) |
| 1151 | return -EOPNOTSUPP; | 1243 | return -EOPNOTSUPP; |
| 1152 | if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) | 1244 | if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) |
| 1153 | return -EOPNOTSUPP; | 1245 | return -EOPNOTSUPP; |
| @@ -1178,9 +1270,16 @@ replay: | |||
| 1178 | err = ops->newlink(net, dev, tb, data); | 1270 | err = ops->newlink(net, dev, tb, data); |
| 1179 | else | 1271 | else |
| 1180 | err = register_netdevice(dev); | 1272 | err = register_netdevice(dev); |
| 1273 | |||
| 1181 | if (err < 0 && !IS_ERR(dev)) | 1274 | if (err < 0 && !IS_ERR(dev)) |
| 1182 | free_netdev(dev); | 1275 | free_netdev(dev); |
| 1276 | if (err < 0) | ||
| 1277 | goto out; | ||
| 1183 | 1278 | ||
| 1279 | err = rtnl_configure_link(dev, ifm); | ||
| 1280 | if (err < 0) | ||
| 1281 | unregister_netdevice(dev); | ||
| 1282 | out: | ||
| 1184 | put_net(dest_net); | 1283 | put_net(dest_net); |
| 1185 | return err; | 1284 | return err; |
| 1186 | } | 1285 | } |
| @@ -1369,17 +1468,14 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi | |||
| 1369 | struct net_device *dev = ptr; | 1468 | struct net_device *dev = ptr; |
| 1370 | 1469 | ||
| 1371 | switch (event) { | 1470 | switch (event) { |
| 1372 | case NETDEV_UNREGISTER: | ||
| 1373 | rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); | ||
| 1374 | break; | ||
| 1375 | case NETDEV_UP: | 1471 | case NETDEV_UP: |
| 1376 | case NETDEV_DOWN: | 1472 | case NETDEV_DOWN: |
| 1377 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); | 1473 | case NETDEV_PRE_UP: |
| 1378 | break; | ||
| 1379 | case NETDEV_POST_INIT: | 1474 | case NETDEV_POST_INIT: |
| 1380 | case NETDEV_REGISTER: | 1475 | case NETDEV_REGISTER: |
| 1381 | case NETDEV_CHANGE: | 1476 | case NETDEV_CHANGE: |
| 1382 | case NETDEV_GOING_DOWN: | 1477 | case NETDEV_GOING_DOWN: |
| 1478 | case NETDEV_UNREGISTER: | ||
| 1383 | case NETDEV_UNREGISTER_BATCH: | 1479 | case NETDEV_UNREGISTER_BATCH: |
| 1384 | break; | 1480 | break; |
| 1385 | default: | 1481 | default: |
| @@ -1394,7 +1490,7 @@ static struct notifier_block rtnetlink_dev_notifier = { | |||
| 1394 | }; | 1490 | }; |
| 1395 | 1491 | ||
| 1396 | 1492 | ||
| 1397 | static int rtnetlink_net_init(struct net *net) | 1493 | static int __net_init rtnetlink_net_init(struct net *net) |
| 1398 | { | 1494 | { |
| 1399 | struct sock *sk; | 1495 | struct sock *sk; |
| 1400 | sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, | 1496 | sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, |
| @@ -1405,7 +1501,7 @@ static int rtnetlink_net_init(struct net *net) | |||
| 1405 | return 0; | 1501 | return 0; |
| 1406 | } | 1502 | } |
| 1407 | 1503 | ||
| 1408 | static void rtnetlink_net_exit(struct net *net) | 1504 | static void __net_exit rtnetlink_net_exit(struct net *net) |
| 1409 | { | 1505 | { |
| 1410 | netlink_kernel_release(net->rtnl); | 1506 | netlink_kernel_release(net->rtnl); |
| 1411 | net->rtnl = NULL; | 1507 | net->rtnl = NULL; |
