diff options
-rw-r--r-- | include/net/rtnetlink.h | 14 | ||||
-rw-r--r-- | include/uapi/linux/if_link.h | 2 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 158 |
3 files changed, 154 insertions, 20 deletions
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 8fb42070a2c1..661e45d38051 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h | |||
@@ -79,6 +79,20 @@ struct rtnl_link_ops { | |||
79 | const struct net_device *dev); | 79 | const struct net_device *dev); |
80 | unsigned int (*get_num_tx_queues)(void); | 80 | unsigned int (*get_num_tx_queues)(void); |
81 | unsigned int (*get_num_rx_queues)(void); | 81 | unsigned int (*get_num_rx_queues)(void); |
82 | |||
83 | int slave_maxtype; | ||
84 | const struct nla_policy *slave_policy; | ||
85 | int (*slave_validate)(struct nlattr *tb[], | ||
86 | struct nlattr *data[]); | ||
87 | int (*slave_changelink)(struct net_device *dev, | ||
88 | struct net_device *slave_dev, | ||
89 | struct nlattr *tb[], | ||
90 | struct nlattr *data[]); | ||
91 | size_t (*get_slave_size)(const struct net_device *dev, | ||
92 | const struct net_device *slave_dev); | ||
93 | int (*fill_slave_info)(struct sk_buff *skb, | ||
94 | const struct net_device *dev, | ||
95 | const struct net_device *slave_dev); | ||
82 | }; | 96 | }; |
83 | 97 | ||
84 | int __rtnl_link_register(struct rtnl_link_ops *ops); | 98 | int __rtnl_link_register(struct rtnl_link_ops *ops); |
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 1f30b85ebf9d..b8fb352b58f6 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h | |||
@@ -241,6 +241,8 @@ enum { | |||
241 | IFLA_INFO_KIND, | 241 | IFLA_INFO_KIND, |
242 | IFLA_INFO_DATA, | 242 | IFLA_INFO_DATA, |
243 | IFLA_INFO_XSTATS, | 243 | IFLA_INFO_XSTATS, |
244 | IFLA_INFO_SLAVE_KIND, | ||
245 | IFLA_INFO_SLAVE_DATA, | ||
244 | __IFLA_INFO_MAX, | 246 | __IFLA_INFO_MAX, |
245 | }; | 247 | }; |
246 | 248 | ||
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index cace14962234..a56bccf6629e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -365,6 +365,22 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops) | |||
365 | } | 365 | } |
366 | EXPORT_SYMBOL_GPL(rtnl_link_unregister); | 366 | EXPORT_SYMBOL_GPL(rtnl_link_unregister); |
367 | 367 | ||
368 | static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) | ||
369 | { | ||
370 | struct net_device *master_dev; | ||
371 | const struct rtnl_link_ops *ops; | ||
372 | |||
373 | master_dev = netdev_master_upper_dev_get((struct net_device *) dev); | ||
374 | if (!master_dev) | ||
375 | return 0; | ||
376 | ops = master_dev->rtnl_link_ops; | ||
377 | if (!ops->get_slave_size) | ||
378 | return 0; | ||
379 | /* IFLA_INFO_SLAVE_DATA + nested data */ | ||
380 | return nla_total_size(sizeof(struct nlattr)) + | ||
381 | ops->get_slave_size(master_dev, dev); | ||
382 | } | ||
383 | |||
368 | static size_t rtnl_link_get_size(const struct net_device *dev) | 384 | static size_t rtnl_link_get_size(const struct net_device *dev) |
369 | { | 385 | { |
370 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | 386 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; |
@@ -385,6 +401,8 @@ static size_t rtnl_link_get_size(const struct net_device *dev) | |||
385 | /* IFLA_INFO_XSTATS */ | 401 | /* IFLA_INFO_XSTATS */ |
386 | size += nla_total_size(ops->get_xstats_size(dev)); | 402 | size += nla_total_size(ops->get_xstats_size(dev)); |
387 | 403 | ||
404 | size += rtnl_link_get_slave_info_data_size(dev); | ||
405 | |||
388 | return size; | 406 | return size; |
389 | } | 407 | } |
390 | 408 | ||
@@ -459,40 +477,101 @@ static size_t rtnl_link_get_af_size(const struct net_device *dev) | |||
459 | return size; | 477 | return size; |
460 | } | 478 | } |
461 | 479 | ||
462 | static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) | 480 | static bool rtnl_have_link_slave_info(const struct net_device *dev) |
463 | { | 481 | { |
464 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | 482 | struct net_device *master_dev; |
465 | struct nlattr *linkinfo, *data; | ||
466 | int err = -EMSGSIZE; | ||
467 | 483 | ||
468 | linkinfo = nla_nest_start(skb, IFLA_LINKINFO); | 484 | master_dev = netdev_master_upper_dev_get((struct net_device *) dev); |
469 | if (linkinfo == NULL) | 485 | if (master_dev && master_dev->rtnl_link_ops && |
470 | goto out; | 486 | master_dev->rtnl_link_ops->fill_slave_info) |
487 | return true; | ||
488 | return false; | ||
489 | } | ||
490 | |||
491 | static int rtnl_link_slave_info_fill(struct sk_buff *skb, | ||
492 | const struct net_device *dev) | ||
493 | { | ||
494 | struct net_device *master_dev; | ||
495 | const struct rtnl_link_ops *ops; | ||
496 | struct nlattr *slave_data; | ||
497 | int err; | ||
471 | 498 | ||
499 | master_dev = netdev_master_upper_dev_get((struct net_device *) dev); | ||
500 | if (!master_dev) | ||
501 | return 0; | ||
502 | ops = master_dev->rtnl_link_ops; | ||
503 | if (!ops) | ||
504 | return 0; | ||
505 | if (nla_put_string(skb, IFLA_INFO_SLAVE_KIND, ops->kind) < 0) | ||
506 | return -EMSGSIZE; | ||
507 | if (ops->fill_slave_info) { | ||
508 | slave_data = nla_nest_start(skb, IFLA_INFO_SLAVE_DATA); | ||
509 | if (!slave_data) | ||
510 | return -EMSGSIZE; | ||
511 | err = ops->fill_slave_info(skb, master_dev, dev); | ||
512 | if (err < 0) | ||
513 | goto err_cancel_slave_data; | ||
514 | nla_nest_end(skb, slave_data); | ||
515 | } | ||
516 | return 0; | ||
517 | |||
518 | err_cancel_slave_data: | ||
519 | nla_nest_cancel(skb, slave_data); | ||
520 | return err; | ||
521 | } | ||
522 | |||
523 | static int rtnl_link_info_fill(struct sk_buff *skb, | ||
524 | const struct net_device *dev) | ||
525 | { | ||
526 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | ||
527 | struct nlattr *data; | ||
528 | int err; | ||
529 | |||
530 | if (!ops) | ||
531 | return 0; | ||
472 | if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0) | 532 | if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0) |
473 | goto err_cancel_link; | 533 | return -EMSGSIZE; |
474 | if (ops->fill_xstats) { | 534 | if (ops->fill_xstats) { |
475 | err = ops->fill_xstats(skb, dev); | 535 | err = ops->fill_xstats(skb, dev); |
476 | if (err < 0) | 536 | if (err < 0) |
477 | goto err_cancel_link; | 537 | return err; |
478 | } | 538 | } |
479 | if (ops->fill_info) { | 539 | if (ops->fill_info) { |
480 | data = nla_nest_start(skb, IFLA_INFO_DATA); | 540 | data = nla_nest_start(skb, IFLA_INFO_DATA); |
481 | if (data == NULL) { | 541 | if (data == NULL) |
482 | err = -EMSGSIZE; | 542 | return -EMSGSIZE; |
483 | goto err_cancel_link; | ||
484 | } | ||
485 | err = ops->fill_info(skb, dev); | 543 | err = ops->fill_info(skb, dev); |
486 | if (err < 0) | 544 | if (err < 0) |
487 | goto err_cancel_data; | 545 | goto err_cancel_data; |
488 | nla_nest_end(skb, data); | 546 | nla_nest_end(skb, data); |
489 | } | 547 | } |
490 | |||
491 | nla_nest_end(skb, linkinfo); | ||
492 | return 0; | 548 | return 0; |
493 | 549 | ||
494 | err_cancel_data: | 550 | err_cancel_data: |
495 | nla_nest_cancel(skb, data); | 551 | nla_nest_cancel(skb, data); |
552 | return err; | ||
553 | } | ||
554 | |||
555 | static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) | ||
556 | { | ||
557 | struct nlattr *linkinfo; | ||
558 | int err = -EMSGSIZE; | ||
559 | |||
560 | linkinfo = nla_nest_start(skb, IFLA_LINKINFO); | ||
561 | if (linkinfo == NULL) | ||
562 | goto out; | ||
563 | |||
564 | err = rtnl_link_info_fill(skb, dev); | ||
565 | if (err < 0) | ||
566 | goto err_cancel_link; | ||
567 | |||
568 | err = rtnl_link_slave_info_fill(skb, dev); | ||
569 | if (err < 0) | ||
570 | goto err_cancel_link; | ||
571 | |||
572 | nla_nest_end(skb, linkinfo); | ||
573 | return 0; | ||
574 | |||
496 | err_cancel_link: | 575 | err_cancel_link: |
497 | nla_nest_cancel(skb, linkinfo); | 576 | nla_nest_cancel(skb, linkinfo); |
498 | out: | 577 | out: |
@@ -1052,10 +1131,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
1052 | if (rtnl_port_fill(skb, dev)) | 1131 | if (rtnl_port_fill(skb, dev)) |
1053 | goto nla_put_failure; | 1132 | goto nla_put_failure; |
1054 | 1133 | ||
1055 | if (rtnl_bond_slave_fill(skb, dev)) | 1134 | if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) { |
1056 | goto nla_put_failure; | ||
1057 | |||
1058 | if (dev->rtnl_link_ops) { | ||
1059 | if (rtnl_link_fill(skb, dev) < 0) | 1135 | if (rtnl_link_fill(skb, dev) < 0) |
1060 | goto nla_put_failure; | 1136 | goto nla_put_failure; |
1061 | } | 1137 | } |
@@ -1178,6 +1254,8 @@ EXPORT_SYMBOL(ifla_policy); | |||
1178 | static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { | 1254 | static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { |
1179 | [IFLA_INFO_KIND] = { .type = NLA_STRING }, | 1255 | [IFLA_INFO_KIND] = { .type = NLA_STRING }, |
1180 | [IFLA_INFO_DATA] = { .type = NLA_NESTED }, | 1256 | [IFLA_INFO_DATA] = { .type = NLA_NESTED }, |
1257 | [IFLA_INFO_SLAVE_KIND] = { .type = NLA_STRING }, | ||
1258 | [IFLA_INFO_SLAVE_DATA] = { .type = NLA_NESTED }, | ||
1181 | }; | 1259 | }; |
1182 | 1260 | ||
1183 | static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = { | 1261 | static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = { |
@@ -1765,7 +1843,9 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
1765 | { | 1843 | { |
1766 | struct net *net = sock_net(skb->sk); | 1844 | struct net *net = sock_net(skb->sk); |
1767 | const struct rtnl_link_ops *ops; | 1845 | const struct rtnl_link_ops *ops; |
1846 | const struct rtnl_link_ops *m_ops = NULL; | ||
1768 | struct net_device *dev; | 1847 | struct net_device *dev; |
1848 | struct net_device *master_dev = NULL; | ||
1769 | struct ifinfomsg *ifm; | 1849 | struct ifinfomsg *ifm; |
1770 | char kind[MODULE_NAME_LEN]; | 1850 | char kind[MODULE_NAME_LEN]; |
1771 | char ifname[IFNAMSIZ]; | 1851 | char ifname[IFNAMSIZ]; |
@@ -1795,6 +1875,12 @@ replay: | |||
1795 | dev = NULL; | 1875 | dev = NULL; |
1796 | } | 1876 | } |
1797 | 1877 | ||
1878 | if (dev) { | ||
1879 | master_dev = netdev_master_upper_dev_get(dev); | ||
1880 | if (master_dev) | ||
1881 | m_ops = master_dev->rtnl_link_ops; | ||
1882 | } | ||
1883 | |||
1798 | err = validate_linkmsg(dev, tb); | 1884 | err = validate_linkmsg(dev, tb); |
1799 | if (err < 0) | 1885 | if (err < 0) |
1800 | return err; | 1886 | return err; |
@@ -1816,7 +1902,10 @@ replay: | |||
1816 | } | 1902 | } |
1817 | 1903 | ||
1818 | if (1) { | 1904 | if (1) { |
1819 | struct nlattr *attr[ops ? ops->maxtype + 1 : 0], **data = NULL; | 1905 | struct nlattr *attr[ops ? ops->maxtype + 1 : 0]; |
1906 | struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0]; | ||
1907 | struct nlattr **data = NULL; | ||
1908 | struct nlattr **slave_data = NULL; | ||
1820 | struct net *dest_net; | 1909 | struct net *dest_net; |
1821 | 1910 | ||
1822 | if (ops) { | 1911 | if (ops) { |
@@ -1835,6 +1924,24 @@ replay: | |||
1835 | } | 1924 | } |
1836 | } | 1925 | } |
1837 | 1926 | ||
1927 | if (m_ops) { | ||
1928 | if (m_ops->slave_maxtype && | ||
1929 | linkinfo[IFLA_INFO_SLAVE_DATA]) { | ||
1930 | err = nla_parse_nested(slave_attr, | ||
1931 | m_ops->slave_maxtype, | ||
1932 | linkinfo[IFLA_INFO_SLAVE_DATA], | ||
1933 | m_ops->slave_policy); | ||
1934 | if (err < 0) | ||
1935 | return err; | ||
1936 | slave_data = slave_attr; | ||
1937 | } | ||
1938 | if (m_ops->slave_validate) { | ||
1939 | err = m_ops->slave_validate(tb, slave_data); | ||
1940 | if (err < 0) | ||
1941 | return err; | ||
1942 | } | ||
1943 | } | ||
1944 | |||
1838 | if (dev) { | 1945 | if (dev) { |
1839 | int modified = 0; | 1946 | int modified = 0; |
1840 | 1947 | ||
@@ -1854,6 +1961,17 @@ replay: | |||
1854 | modified = 1; | 1961 | modified = 1; |
1855 | } | 1962 | } |
1856 | 1963 | ||
1964 | if (linkinfo[IFLA_INFO_SLAVE_DATA]) { | ||
1965 | if (!m_ops || !m_ops->slave_changelink) | ||
1966 | return -EOPNOTSUPP; | ||
1967 | |||
1968 | err = m_ops->slave_changelink(master_dev, dev, | ||
1969 | tb, slave_data); | ||
1970 | if (err < 0) | ||
1971 | return err; | ||
1972 | modified = 1; | ||
1973 | } | ||
1974 | |||
1857 | return do_setlink(dev, ifm, tb, ifname, modified); | 1975 | return do_setlink(dev, ifm, tb, ifname, modified); |
1858 | } | 1976 | } |
1859 | 1977 | ||