diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 147 |
1 files changed, 145 insertions, 2 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 841c287ef40a..bf69e5871b1a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -362,6 +362,95 @@ static size_t rtnl_link_get_size(const struct net_device *dev) | |||
362 | return size; | 362 | return size; |
363 | } | 363 | } |
364 | 364 | ||
365 | static LIST_HEAD(rtnl_af_ops); | ||
366 | |||
367 | static const struct rtnl_af_ops *rtnl_af_lookup(const int family) | ||
368 | { | ||
369 | const struct rtnl_af_ops *ops; | ||
370 | |||
371 | list_for_each_entry(ops, &rtnl_af_ops, list) { | ||
372 | if (ops->family == family) | ||
373 | return ops; | ||
374 | } | ||
375 | |||
376 | return NULL; | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * __rtnl_af_register - Register rtnl_af_ops with rtnetlink. | ||
381 | * @ops: struct rtnl_af_ops * to register | ||
382 | * | ||
383 | * The caller must hold the rtnl_mutex. | ||
384 | * | ||
385 | * Returns 0 on success or a negative error code. | ||
386 | */ | ||
387 | int __rtnl_af_register(struct rtnl_af_ops *ops) | ||
388 | { | ||
389 | list_add_tail(&ops->list, &rtnl_af_ops); | ||
390 | return 0; | ||
391 | } | ||
392 | EXPORT_SYMBOL_GPL(__rtnl_af_register); | ||
393 | |||
394 | /** | ||
395 | * rtnl_af_register - Register rtnl_af_ops with rtnetlink. | ||
396 | * @ops: struct rtnl_af_ops * to register | ||
397 | * | ||
398 | * Returns 0 on success or a negative error code. | ||
399 | */ | ||
400 | int rtnl_af_register(struct rtnl_af_ops *ops) | ||
401 | { | ||
402 | int err; | ||
403 | |||
404 | rtnl_lock(); | ||
405 | err = __rtnl_af_register(ops); | ||
406 | rtnl_unlock(); | ||
407 | return err; | ||
408 | } | ||
409 | EXPORT_SYMBOL_GPL(rtnl_af_register); | ||
410 | |||
411 | /** | ||
412 | * __rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. | ||
413 | * @ops: struct rtnl_af_ops * to unregister | ||
414 | * | ||
415 | * The caller must hold the rtnl_mutex. | ||
416 | */ | ||
417 | void __rtnl_af_unregister(struct rtnl_af_ops *ops) | ||
418 | { | ||
419 | list_del(&ops->list); | ||
420 | } | ||
421 | EXPORT_SYMBOL_GPL(__rtnl_af_unregister); | ||
422 | |||
423 | /** | ||
424 | * rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. | ||
425 | * @ops: struct rtnl_af_ops * to unregister | ||
426 | */ | ||
427 | void rtnl_af_unregister(struct rtnl_af_ops *ops) | ||
428 | { | ||
429 | rtnl_lock(); | ||
430 | __rtnl_af_unregister(ops); | ||
431 | rtnl_unlock(); | ||
432 | } | ||
433 | EXPORT_SYMBOL_GPL(rtnl_af_unregister); | ||
434 | |||
435 | static size_t rtnl_link_get_af_size(const struct net_device *dev) | ||
436 | { | ||
437 | struct rtnl_af_ops *af_ops; | ||
438 | size_t size; | ||
439 | |||
440 | /* IFLA_AF_SPEC */ | ||
441 | size = nla_total_size(sizeof(struct nlattr)); | ||
442 | |||
443 | list_for_each_entry(af_ops, &rtnl_af_ops, list) { | ||
444 | if (af_ops->get_link_af_size) { | ||
445 | /* AF_* + nested data */ | ||
446 | size += nla_total_size(sizeof(struct nlattr)) + | ||
447 | af_ops->get_link_af_size(dev); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | return size; | ||
452 | } | ||
453 | |||
365 | static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) | 454 | static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) |
366 | { | 455 | { |
367 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | 456 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; |
@@ -671,7 +760,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev) | |||
671 | + nla_total_size(4) /* IFLA_NUM_VF */ | 760 | + nla_total_size(4) /* IFLA_NUM_VF */ |
672 | + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */ | 761 | + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */ |
673 | + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ | 762 | + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ |
674 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ | 763 | + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ |
764 | + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */ | ||
675 | } | 765 | } |
676 | 766 | ||
677 | static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) | 767 | static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) |
@@ -757,7 +847,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
757 | struct nlmsghdr *nlh; | 847 | struct nlmsghdr *nlh; |
758 | struct rtnl_link_stats64 temp; | 848 | struct rtnl_link_stats64 temp; |
759 | const struct rtnl_link_stats64 *stats; | 849 | const struct rtnl_link_stats64 *stats; |
760 | struct nlattr *attr; | 850 | struct nlattr *attr, *af_spec; |
851 | struct rtnl_af_ops *af_ops; | ||
761 | 852 | ||
762 | nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); | 853 | nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); |
763 | if (nlh == NULL) | 854 | if (nlh == NULL) |
@@ -866,6 +957,36 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
866 | goto nla_put_failure; | 957 | goto nla_put_failure; |
867 | } | 958 | } |
868 | 959 | ||
960 | if (!(af_spec = nla_nest_start(skb, IFLA_AF_SPEC))) | ||
961 | goto nla_put_failure; | ||
962 | |||
963 | list_for_each_entry(af_ops, &rtnl_af_ops, list) { | ||
964 | if (af_ops->fill_link_af) { | ||
965 | struct nlattr *af; | ||
966 | int err; | ||
967 | |||
968 | if (!(af = nla_nest_start(skb, af_ops->family))) | ||
969 | goto nla_put_failure; | ||
970 | |||
971 | err = af_ops->fill_link_af(skb, dev); | ||
972 | |||
973 | /* | ||
974 | * Caller may return ENODATA to indicate that there | ||
975 | * was no data to be dumped. This is not an error, it | ||
976 | * means we should trim the attribute header and | ||
977 | * continue. | ||
978 | */ | ||
979 | if (err == -ENODATA) | ||
980 | nla_nest_cancel(skb, af); | ||
981 | else if (err < 0) | ||
982 | goto nla_put_failure; | ||
983 | |||
984 | nla_nest_end(skb, af); | ||
985 | } | ||
986 | } | ||
987 | |||
988 | nla_nest_end(skb, af_spec); | ||
989 | |||
869 | return nlmsg_end(skb, nlh); | 990 | return nlmsg_end(skb, nlh); |
870 | 991 | ||
871 | nla_put_failure: | 992 | nla_put_failure: |
@@ -924,6 +1045,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
924 | [IFLA_VFINFO_LIST] = {. type = NLA_NESTED }, | 1045 | [IFLA_VFINFO_LIST] = {. type = NLA_NESTED }, |
925 | [IFLA_VF_PORTS] = { .type = NLA_NESTED }, | 1046 | [IFLA_VF_PORTS] = { .type = NLA_NESTED }, |
926 | [IFLA_PORT_SELF] = { .type = NLA_NESTED }, | 1047 | [IFLA_PORT_SELF] = { .type = NLA_NESTED }, |
1048 | [IFLA_AF_SPEC] = { .type = NLA_NESTED }, | ||
927 | }; | 1049 | }; |
928 | EXPORT_SYMBOL(ifla_policy); | 1050 | EXPORT_SYMBOL(ifla_policy); |
929 | 1051 | ||
@@ -1225,6 +1347,27 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
1225 | goto errout; | 1347 | goto errout; |
1226 | modified = 1; | 1348 | modified = 1; |
1227 | } | 1349 | } |
1350 | |||
1351 | if (tb[IFLA_AF_SPEC]) { | ||
1352 | struct nlattr *af; | ||
1353 | int rem; | ||
1354 | |||
1355 | nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) { | ||
1356 | const struct rtnl_af_ops *af_ops; | ||
1357 | |||
1358 | if (!(af_ops = rtnl_af_lookup(nla_type(af)))) | ||
1359 | continue; | ||
1360 | |||
1361 | if (!af_ops->parse_link_af) | ||
1362 | continue; | ||
1363 | |||
1364 | err = af_ops->parse_link_af(dev, af); | ||
1365 | if (err < 0) | ||
1366 | goto errout; | ||
1367 | |||
1368 | modified = 1; | ||
1369 | } | ||
1370 | } | ||
1228 | err = 0; | 1371 | err = 0; |
1229 | 1372 | ||
1230 | errout: | 1373 | errout: |