forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
d67ef35fff
commit
f8ff182c71
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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[]);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue
Block a user