diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 80 |
1 files changed, 59 insertions, 21 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 65aebd450027..f965dce6f20f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -60,7 +60,6 @@ struct rtnl_link { | |||
60 | }; | 60 | }; |
61 | 61 | ||
62 | static DEFINE_MUTEX(rtnl_mutex); | 62 | static DEFINE_MUTEX(rtnl_mutex); |
63 | static u16 min_ifinfo_dump_size; | ||
64 | 63 | ||
65 | void rtnl_lock(void) | 64 | void rtnl_lock(void) |
66 | { | 65 | { |
@@ -724,10 +723,11 @@ static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b) | |||
724 | } | 723 | } |
725 | 724 | ||
726 | /* All VF info */ | 725 | /* All VF info */ |
727 | static inline int rtnl_vfinfo_size(const struct net_device *dev) | 726 | static inline int rtnl_vfinfo_size(const struct net_device *dev, |
727 | u32 ext_filter_mask) | ||
728 | { | 728 | { |
729 | if (dev->dev.parent && dev_is_pci(dev->dev.parent)) { | 729 | if (dev->dev.parent && dev_is_pci(dev->dev.parent) && |
730 | 730 | (ext_filter_mask & RTEXT_FILTER_VF)) { | |
731 | int num_vfs = dev_num_vf(dev->dev.parent); | 731 | int num_vfs = dev_num_vf(dev->dev.parent); |
732 | size_t size = nla_total_size(sizeof(struct nlattr)); | 732 | size_t size = nla_total_size(sizeof(struct nlattr)); |
733 | size += nla_total_size(num_vfs * sizeof(struct nlattr)); | 733 | size += nla_total_size(num_vfs * sizeof(struct nlattr)); |
@@ -766,7 +766,8 @@ static size_t rtnl_port_size(const struct net_device *dev) | |||
766 | return port_self_size; | 766 | return port_self_size; |
767 | } | 767 | } |
768 | 768 | ||
769 | static noinline size_t if_nlmsg_size(const struct net_device *dev) | 769 | static noinline size_t if_nlmsg_size(const struct net_device *dev, |
770 | u32 ext_filter_mask) | ||
770 | { | 771 | { |
771 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 772 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
772 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ | 773 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ |
@@ -784,8 +785,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev) | |||
784 | + nla_total_size(4) /* IFLA_MASTER */ | 785 | + nla_total_size(4) /* IFLA_MASTER */ |
785 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 786 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
786 | + nla_total_size(1) /* IFLA_LINKMODE */ | 787 | + nla_total_size(1) /* IFLA_LINKMODE */ |
787 | + nla_total_size(4) /* IFLA_NUM_VF */ | 788 | + nla_total_size(ext_filter_mask |
788 | + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */ | 789 | & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ |
790 | + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ | ||
789 | + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ | 791 | + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ |
790 | + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ | 792 | + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ |
791 | + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */ | 793 | + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */ |
@@ -868,7 +870,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev) | |||
868 | 870 | ||
869 | static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | 871 | static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, |
870 | int type, u32 pid, u32 seq, u32 change, | 872 | int type, u32 pid, u32 seq, u32 change, |
871 | unsigned int flags) | 873 | unsigned int flags, u32 ext_filter_mask) |
872 | { | 874 | { |
873 | struct ifinfomsg *ifm; | 875 | struct ifinfomsg *ifm; |
874 | struct nlmsghdr *nlh; | 876 | struct nlmsghdr *nlh; |
@@ -941,10 +943,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
941 | goto nla_put_failure; | 943 | goto nla_put_failure; |
942 | copy_rtnl_link_stats64(nla_data(attr), stats); | 944 | copy_rtnl_link_stats64(nla_data(attr), stats); |
943 | 945 | ||
944 | if (dev->dev.parent) | 946 | if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF)) |
945 | NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent)); | 947 | NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent)); |
946 | 948 | ||
947 | if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) { | 949 | if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent |
950 | && (ext_filter_mask & RTEXT_FILTER_VF)) { | ||
948 | int i; | 951 | int i; |
949 | 952 | ||
950 | struct nlattr *vfinfo, *vf; | 953 | struct nlattr *vfinfo, *vf; |
@@ -1048,6 +1051,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1048 | struct net_device *dev; | 1051 | struct net_device *dev; |
1049 | struct hlist_head *head; | 1052 | struct hlist_head *head; |
1050 | struct hlist_node *node; | 1053 | struct hlist_node *node; |
1054 | struct nlattr *tb[IFLA_MAX+1]; | ||
1055 | u32 ext_filter_mask = 0; | ||
1051 | 1056 | ||
1052 | s_h = cb->args[0]; | 1057 | s_h = cb->args[0]; |
1053 | s_idx = cb->args[1]; | 1058 | s_idx = cb->args[1]; |
@@ -1055,6 +1060,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1055 | rcu_read_lock(); | 1060 | rcu_read_lock(); |
1056 | cb->seq = net->dev_base_seq; | 1061 | cb->seq = net->dev_base_seq; |
1057 | 1062 | ||
1063 | if (nlmsg_parse(cb->nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX, | ||
1064 | ifla_policy) >= 0) { | ||
1065 | |||
1066 | if (tb[IFLA_EXT_MASK]) | ||
1067 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); | ||
1068 | } | ||
1069 | |||
1058 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { | 1070 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
1059 | idx = 0; | 1071 | idx = 0; |
1060 | head = &net->dev_index_head[h]; | 1072 | head = &net->dev_index_head[h]; |
@@ -1064,7 +1076,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1064 | if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, | 1076 | if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, |
1065 | NETLINK_CB(cb->skb).pid, | 1077 | NETLINK_CB(cb->skb).pid, |
1066 | cb->nlh->nlmsg_seq, 0, | 1078 | cb->nlh->nlmsg_seq, 0, |
1067 | NLM_F_MULTI) <= 0) | 1079 | NLM_F_MULTI, |
1080 | ext_filter_mask) <= 0) | ||
1068 | goto out; | 1081 | goto out; |
1069 | 1082 | ||
1070 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); | 1083 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); |
@@ -1100,6 +1113,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
1100 | [IFLA_VF_PORTS] = { .type = NLA_NESTED }, | 1113 | [IFLA_VF_PORTS] = { .type = NLA_NESTED }, |
1101 | [IFLA_PORT_SELF] = { .type = NLA_NESTED }, | 1114 | [IFLA_PORT_SELF] = { .type = NLA_NESTED }, |
1102 | [IFLA_AF_SPEC] = { .type = NLA_NESTED }, | 1115 | [IFLA_AF_SPEC] = { .type = NLA_NESTED }, |
1116 | [IFLA_EXT_MASK] = { .type = NLA_U32 }, | ||
1103 | }; | 1117 | }; |
1104 | EXPORT_SYMBOL(ifla_policy); | 1118 | EXPORT_SYMBOL(ifla_policy); |
1105 | 1119 | ||
@@ -1509,8 +1523,6 @@ errout: | |||
1509 | 1523 | ||
1510 | if (send_addr_notify) | 1524 | if (send_addr_notify) |
1511 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); | 1525 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); |
1512 | min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev), | ||
1513 | min_ifinfo_dump_size); | ||
1514 | 1526 | ||
1515 | return err; | 1527 | return err; |
1516 | } | 1528 | } |
@@ -1842,6 +1854,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
1842 | struct net_device *dev = NULL; | 1854 | struct net_device *dev = NULL; |
1843 | struct sk_buff *nskb; | 1855 | struct sk_buff *nskb; |
1844 | int err; | 1856 | int err; |
1857 | u32 ext_filter_mask = 0; | ||
1845 | 1858 | ||
1846 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); | 1859 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); |
1847 | if (err < 0) | 1860 | if (err < 0) |
@@ -1850,6 +1863,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
1850 | if (tb[IFLA_IFNAME]) | 1863 | if (tb[IFLA_IFNAME]) |
1851 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | 1864 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); |
1852 | 1865 | ||
1866 | if (tb[IFLA_EXT_MASK]) | ||
1867 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); | ||
1868 | |||
1853 | ifm = nlmsg_data(nlh); | 1869 | ifm = nlmsg_data(nlh); |
1854 | if (ifm->ifi_index > 0) | 1870 | if (ifm->ifi_index > 0) |
1855 | dev = __dev_get_by_index(net, ifm->ifi_index); | 1871 | dev = __dev_get_by_index(net, ifm->ifi_index); |
@@ -1861,12 +1877,12 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
1861 | if (dev == NULL) | 1877 | if (dev == NULL) |
1862 | return -ENODEV; | 1878 | return -ENODEV; |
1863 | 1879 | ||
1864 | nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); | 1880 | nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL); |
1865 | if (nskb == NULL) | 1881 | if (nskb == NULL) |
1866 | return -ENOBUFS; | 1882 | return -ENOBUFS; |
1867 | 1883 | ||
1868 | err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid, | 1884 | err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid, |
1869 | nlh->nlmsg_seq, 0, 0); | 1885 | nlh->nlmsg_seq, 0, 0, ext_filter_mask); |
1870 | if (err < 0) { | 1886 | if (err < 0) { |
1871 | /* -EMSGSIZE implies BUG in if_nlmsg_size */ | 1887 | /* -EMSGSIZE implies BUG in if_nlmsg_size */ |
1872 | WARN_ON(err == -EMSGSIZE); | 1888 | WARN_ON(err == -EMSGSIZE); |
@@ -1877,8 +1893,32 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
1877 | return err; | 1893 | return err; |
1878 | } | 1894 | } |
1879 | 1895 | ||
1880 | static u16 rtnl_calcit(struct sk_buff *skb) | 1896 | static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh) |
1881 | { | 1897 | { |
1898 | struct net *net = sock_net(skb->sk); | ||
1899 | struct net_device *dev; | ||
1900 | struct nlattr *tb[IFLA_MAX+1]; | ||
1901 | u32 ext_filter_mask = 0; | ||
1902 | u16 min_ifinfo_dump_size = 0; | ||
1903 | |||
1904 | if (nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX, | ||
1905 | ifla_policy) >= 0) { | ||
1906 | if (tb[IFLA_EXT_MASK]) | ||
1907 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); | ||
1908 | } | ||
1909 | |||
1910 | if (!ext_filter_mask) | ||
1911 | return NLMSG_GOODSIZE; | ||
1912 | /* | ||
1913 | * traverse the list of net devices and compute the minimum | ||
1914 | * buffer size based upon the filter mask. | ||
1915 | */ | ||
1916 | list_for_each_entry(dev, &net->dev_base_head, dev_list) { | ||
1917 | min_ifinfo_dump_size = max_t(u16, min_ifinfo_dump_size, | ||
1918 | if_nlmsg_size(dev, | ||
1919 | ext_filter_mask)); | ||
1920 | } | ||
1921 | |||
1882 | return min_ifinfo_dump_size; | 1922 | return min_ifinfo_dump_size; |
1883 | } | 1923 | } |
1884 | 1924 | ||
@@ -1913,13 +1953,11 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) | |||
1913 | int err = -ENOBUFS; | 1953 | int err = -ENOBUFS; |
1914 | size_t if_info_size; | 1954 | size_t if_info_size; |
1915 | 1955 | ||
1916 | skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL); | 1956 | skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL); |
1917 | if (skb == NULL) | 1957 | if (skb == NULL) |
1918 | goto errout; | 1958 | goto errout; |
1919 | 1959 | ||
1920 | min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size); | 1960 | err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0); |
1921 | |||
1922 | err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0); | ||
1923 | if (err < 0) { | 1961 | if (err < 0) { |
1924 | /* -EMSGSIZE implies BUG in if_nlmsg_size() */ | 1962 | /* -EMSGSIZE implies BUG in if_nlmsg_size() */ |
1925 | WARN_ON(err == -EMSGSIZE); | 1963 | WARN_ON(err == -EMSGSIZE); |
@@ -1977,7 +2015,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
1977 | return -EOPNOTSUPP; | 2015 | return -EOPNOTSUPP; |
1978 | calcit = rtnl_get_calcit(family, type); | 2016 | calcit = rtnl_get_calcit(family, type); |
1979 | if (calcit) | 2017 | if (calcit) |
1980 | min_dump_alloc = calcit(skb); | 2018 | min_dump_alloc = calcit(skb, nlh); |
1981 | 2019 | ||
1982 | __rtnl_unlock(); | 2020 | __rtnl_unlock(); |
1983 | rtnl = net->rtnl; | 2021 | rtnl = net->rtnl; |