diff options
| author | Thomas Graf <tgraf@infradead.org> | 2010-11-16 04:30:14 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-11-17 11:28:24 -0800 | 
| commit | f8ff182c716c6f11ca3061961f5722f26a14e101 (patch) | |
| tree | 6ea721a7feb71850c61b9d02400d162674d91c21 | |
| parent | d67ef35fff67845c64d806c033cc7c569ccebfff (diff) | |
| download | olio-linux-3.10-f8ff182c716c6f11ca3061961f5722f26a14e101.tar.xz olio-linux-3.10-f8ff182c716c6f11ca3061961f5722f26a14e101.zip  | |
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 2fc66dd783e..443d04a66a7 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -80,6 +80,24 @@ struct rtnl_link_ifmap {  	__u8	port;  }; +/* + * IFLA_AF_SPEC + *   Contains nested attributes for address family specific attributes. + *   Each address family may create a attribute with the address family + *   number as type and create its own attribute structure in it. + * + *   Example: + *   [IFLA_AF_SPEC] = { + *       [AF_INET] = { + *           [IFLA_INET_CONF] = ..., + *       }, + *       [AF_INET6] = { + *           [IFLA_INET6_FLAGS] = ..., + *           [IFLA_INET6_CONF] = ..., + *       } + *   } + */ +  enum {  	IFLA_UNSPEC,  	IFLA_ADDRESS, @@ -116,6 +134,7 @@ enum {  	IFLA_STATS64,  	IFLA_VF_PORTS,  	IFLA_PORT_SELF, +	IFLA_AF_SPEC,  	__IFLA_MAX  }; diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index e013c68bfb0..35be0bbcd7d 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);  extern int	rtnl_link_register(struct rtnl_link_ops *ops);  extern void	rtnl_link_unregister(struct rtnl_link_ops *ops); +/** + * 	struct rtnl_af_ops - rtnetlink address family operations + * + *	@list: Used internally + * 	@family: Address family + * 	@fill_link_af: Function to fill IFLA_AF_SPEC with address family + * 		       specific netlink attributes. + * 	@get_link_af_size: Function to calculate size of address family specific + * 			   netlink attributes exlusive the container attribute. + * 	@parse_link_af: Function to parse a IFLA_AF_SPEC attribute and modify + *			net_device accordingly. + */ +struct rtnl_af_ops { +	struct list_head	list; +	int			family; + +	int			(*fill_link_af)(struct sk_buff *skb, +						const struct net_device *dev); +	size_t			(*get_link_af_size)(const struct net_device *dev); + +	int			(*parse_link_af)(struct net_device *dev, +						 const struct nlattr *attr); +}; + +extern int	__rtnl_af_register(struct rtnl_af_ops *ops); +extern void	__rtnl_af_unregister(struct rtnl_af_ops *ops); + +extern int	rtnl_af_register(struct rtnl_af_ops *ops); +extern void	rtnl_af_unregister(struct rtnl_af_ops *ops); + +  extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);  extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net,  	char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 841c287ef40..bf69e5871b1 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)  	return size;  } +static LIST_HEAD(rtnl_af_ops); + +static const struct rtnl_af_ops *rtnl_af_lookup(const int family) +{ +	const struct rtnl_af_ops *ops; + +	list_for_each_entry(ops, &rtnl_af_ops, list) { +		if (ops->family == family) +			return ops; +	} + +	return NULL; +} + +/** + * __rtnl_af_register - Register rtnl_af_ops with rtnetlink. + * @ops: struct rtnl_af_ops * to register + * + * The caller must hold the rtnl_mutex. + * + * Returns 0 on success or a negative error code. + */ +int __rtnl_af_register(struct rtnl_af_ops *ops) +{ +	list_add_tail(&ops->list, &rtnl_af_ops); +	return 0; +} +EXPORT_SYMBOL_GPL(__rtnl_af_register); + +/** + * rtnl_af_register - Register rtnl_af_ops with rtnetlink. + * @ops: struct rtnl_af_ops * to register + * + * Returns 0 on success or a negative error code. + */ +int rtnl_af_register(struct rtnl_af_ops *ops) +{ +	int err; + +	rtnl_lock(); +	err = __rtnl_af_register(ops); +	rtnl_unlock(); +	return err; +} +EXPORT_SYMBOL_GPL(rtnl_af_register); + +/** + * __rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. + * @ops: struct rtnl_af_ops * to unregister + * + * The caller must hold the rtnl_mutex. + */ +void __rtnl_af_unregister(struct rtnl_af_ops *ops) +{ +	list_del(&ops->list); +} +EXPORT_SYMBOL_GPL(__rtnl_af_unregister); + +/** + * rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. + * @ops: struct rtnl_af_ops * to unregister + */ +void rtnl_af_unregister(struct rtnl_af_ops *ops) +{ +	rtnl_lock(); +	__rtnl_af_unregister(ops); +	rtnl_unlock(); +} +EXPORT_SYMBOL_GPL(rtnl_af_unregister); + +static size_t rtnl_link_get_af_size(const struct net_device *dev) +{ +	struct rtnl_af_ops *af_ops; +	size_t size; + +	/* IFLA_AF_SPEC */ +	size = nla_total_size(sizeof(struct nlattr)); + +	list_for_each_entry(af_ops, &rtnl_af_ops, list) { +		if (af_ops->get_link_af_size) { +			/* AF_* + nested data */ +			size += nla_total_size(sizeof(struct nlattr)) + +				af_ops->get_link_af_size(dev); +		} +	} + +	return size; +} +  static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev)  {  	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)  	       + nla_total_size(4) /* IFLA_NUM_VF */  	       + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */  	       + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ -	       + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ +	       + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ +	       + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */  }  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,  	struct nlmsghdr *nlh;  	struct rtnl_link_stats64 temp;  	const struct rtnl_link_stats64 *stats; -	struct nlattr *attr; +	struct nlattr *attr, *af_spec; +	struct rtnl_af_ops *af_ops;  	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);  	if (nlh == NULL) @@ -866,6 +957,36 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,  			goto nla_put_failure;  	} +	if (!(af_spec = nla_nest_start(skb, IFLA_AF_SPEC))) +		goto nla_put_failure; + +	list_for_each_entry(af_ops, &rtnl_af_ops, list) { +		if (af_ops->fill_link_af) { +			struct nlattr *af; +			int err; + +			if (!(af = nla_nest_start(skb, af_ops->family))) +				goto nla_put_failure; + +			err = af_ops->fill_link_af(skb, dev); + +			/* +			 * Caller may return ENODATA to indicate that there +			 * was no data to be dumped. This is not an error, it +			 * means we should trim the attribute header and +			 * continue. +			 */ +			if (err == -ENODATA) +				nla_nest_cancel(skb, af); +			else if (err < 0) +				goto nla_put_failure; + +			nla_nest_end(skb, af); +		} +	} + +	nla_nest_end(skb, af_spec); +  	return nlmsg_end(skb, nlh);  nla_put_failure: @@ -924,6 +1045,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {  	[IFLA_VFINFO_LIST]	= {. type = NLA_NESTED },  	[IFLA_VF_PORTS]		= { .type = NLA_NESTED },  	[IFLA_PORT_SELF]	= { .type = NLA_NESTED }, +	[IFLA_AF_SPEC]		= { .type = NLA_NESTED },  };  EXPORT_SYMBOL(ifla_policy); @@ -1225,6 +1347,27 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,  			goto errout;  		modified = 1;  	} + +	if (tb[IFLA_AF_SPEC]) { +		struct nlattr *af; +		int rem; + +		nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) { +			const struct rtnl_af_ops *af_ops; + +			if (!(af_ops = rtnl_af_lookup(nla_type(af)))) +				continue; + +			if (!af_ops->parse_link_af) +				continue; + +			err = af_ops->parse_link_af(dev, af); +			if (err < 0) +				goto errout; + +			modified = 1; +		} +	}  	err = 0;  errout:  |