diff options
author | Thomas Graf <tgraf@infradead.org> | 2010-11-15 23:30:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-11-17 14:28:24 -0500 |
commit | f8ff182c716c6f11ca3061961f5722f26a14e101 (patch) | |
tree | 6ea721a7feb71850c61b9d02400d162674d91c21 | |
parent | d67ef35fff67845c64d806c033cc7c569ccebfff (diff) |
rtnetlink: Link address family API
Each net_device contains address family specific data such as
per device settings and statistics. We already expose this data
via procfs/sysfs and partially netlink.
The netlink method requires the requester to send one RTM_GETLINK
request for each address family it wishes to receive data of
and then merge this data itself.
This patch implements a new API which combines all address family
specific link data in a new netlink attribute IFLA_AF_SPEC.
IFLA_AF_SPEC contains a sequence of nested attributes, one for each
address family which in turn defines the structure of its own
attribute. Example:
[IFLA_AF_SPEC] = {
[AF_INET] = {
[IFLA_INET_CONF] = ...,
},
[AF_INET6] = {
[IFLA_INET6_FLAGS] = ...,
[IFLA_INET6_CONF] = ...,
}
}
The API also allows for address families to implement a function
which parses the IFLA_AF_SPEC attribute sent by userspace to
implement address family specific link options.
Signed-off-by: Thomas Graf <tgraf@infradead.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/if_link.h | 19 | ||||
-rw-r--r-- | include/net/rtnetlink.h | 31 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 147 |
3 files changed, 195 insertions, 2 deletions
diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 2fc66dd783ee..443d04a66a79 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h | |||
@@ -80,6 +80,24 @@ struct rtnl_link_ifmap { | |||
80 | __u8 port; | 80 | __u8 port; |
81 | }; | 81 | }; |
82 | 82 | ||
83 | /* | ||
84 | * IFLA_AF_SPEC | ||
85 | * Contains nested attributes for address family specific attributes. | ||
86 | * Each address family may create a attribute with the address family | ||
87 | * number as type and create its own attribute structure in it. | ||
88 | * | ||
89 | * Example: | ||
90 | * [IFLA_AF_SPEC] = { | ||
91 | * [AF_INET] = { | ||
92 | * [IFLA_INET_CONF] = ..., | ||
93 | * }, | ||
94 | * [AF_INET6] = { | ||
95 | * [IFLA_INET6_FLAGS] = ..., | ||
96 | * [IFLA_INET6_CONF] = ..., | ||
97 | * } | ||
98 | * } | ||
99 | */ | ||
100 | |||
83 | enum { | 101 | enum { |
84 | IFLA_UNSPEC, | 102 | IFLA_UNSPEC, |
85 | IFLA_ADDRESS, | 103 | IFLA_ADDRESS, |
@@ -116,6 +134,7 @@ enum { | |||
116 | IFLA_STATS64, | 134 | IFLA_STATS64, |
117 | IFLA_VF_PORTS, | 135 | IFLA_VF_PORTS, |
118 | IFLA_PORT_SELF, | 136 | IFLA_PORT_SELF, |
137 | IFLA_AF_SPEC, | ||
119 | __IFLA_MAX | 138 | __IFLA_MAX |
120 | }; | 139 | }; |
121 | 140 | ||
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index e013c68bfb00..35be0bbcd7da 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h | |||
@@ -83,6 +83,37 @@ extern void __rtnl_link_unregister(struct rtnl_link_ops *ops); | |||
83 | extern int rtnl_link_register(struct rtnl_link_ops *ops); | 83 | extern int rtnl_link_register(struct rtnl_link_ops *ops); |
84 | extern void rtnl_link_unregister(struct rtnl_link_ops *ops); | 84 | extern void rtnl_link_unregister(struct rtnl_link_ops *ops); |
85 | 85 | ||
86 | /** | ||
87 | * struct rtnl_af_ops - rtnetlink address family operations | ||
88 | * | ||
89 | * @list: Used internally | ||
90 | * @family: Address family | ||
91 | * @fill_link_af: Function to fill IFLA_AF_SPEC with address family | ||
92 | * specific netlink attributes. | ||
93 | * @get_link_af_size: Function to calculate size of address family specific | ||
94 | * netlink attributes exlusive the container attribute. | ||
95 | * @parse_link_af: Function to parse a IFLA_AF_SPEC attribute and modify | ||
96 | * net_device accordingly. | ||
97 | */ | ||
98 | struct rtnl_af_ops { | ||
99 | struct list_head list; | ||
100 | int family; | ||
101 | |||
102 | int (*fill_link_af)(struct sk_buff *skb, | ||
103 | const struct net_device *dev); | ||
104 | size_t (*get_link_af_size)(const struct net_device *dev); | ||
105 | |||
106 | int (*parse_link_af)(struct net_device *dev, | ||
107 | const struct nlattr *attr); | ||
108 | }; | ||
109 | |||
110 | extern int __rtnl_af_register(struct rtnl_af_ops *ops); | ||
111 | extern void __rtnl_af_unregister(struct rtnl_af_ops *ops); | ||
112 | |||
113 | extern int rtnl_af_register(struct rtnl_af_ops *ops); | ||
114 | extern void rtnl_af_unregister(struct rtnl_af_ops *ops); | ||
115 | |||
116 | |||
86 | extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); | 117 | extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); |
87 | extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | 118 | extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net, |
88 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]); | 119 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]); |
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: |