|
@@ -362,6 +362,95 @@ static size_t rtnl_link_get_size(const struct net_device *dev)
|
|
return size;
|
|
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)
|
|
static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev)
|
|
{
|
|
{
|
|
const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
|
|
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 */
|
|
+ nla_total_size(4) /* IFLA_NUM_VF */
|
|
+ rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */
|
|
+ rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */
|
|
+ rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
|
|
+ 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)
|
|
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 nlmsghdr *nlh;
|
|
struct rtnl_link_stats64 temp;
|
|
struct rtnl_link_stats64 temp;
|
|
const struct rtnl_link_stats64 *stats;
|
|
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);
|
|
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
|
|
if (nlh == NULL)
|
|
if (nlh == NULL)
|
|
@@ -866,6 +957,36 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
|
|
goto nla_put_failure;
|
|
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);
|
|
return nlmsg_end(skb, nlh);
|
|
|
|
|
|
nla_put_failure:
|
|
nla_put_failure:
|
|
@@ -924,6 +1045,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
|
|
[IFLA_VFINFO_LIST] = {. type = NLA_NESTED },
|
|
[IFLA_VFINFO_LIST] = {. type = NLA_NESTED },
|
|
[IFLA_VF_PORTS] = { .type = NLA_NESTED },
|
|
[IFLA_VF_PORTS] = { .type = NLA_NESTED },
|
|
[IFLA_PORT_SELF] = { .type = NLA_NESTED },
|
|
[IFLA_PORT_SELF] = { .type = NLA_NESTED },
|
|
|
|
+ [IFLA_AF_SPEC] = { .type = NLA_NESTED },
|
|
};
|
|
};
|
|
EXPORT_SYMBOL(ifla_policy);
|
|
EXPORT_SYMBOL(ifla_policy);
|
|
|
|
|
|
@@ -1225,6 +1347,27 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
|
|
goto errout;
|
|
goto errout;
|
|
modified = 1;
|
|
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;
|
|
err = 0;
|
|
|
|
|
|
errout:
|
|
errout:
|