[PATCH 2/3] Handle family-based parsing of IFLA_AF_SPEC attribute
David Ahern
dsa at cumulusnetworks.com
Wed Nov 25 11:14:15 PST 2015
The encoding of the IFLA_AF_SPEC attribute varies depending on the family
used for the request (RTM_GETLINK) message. For AF_UNSPEC the encoding
has another level of nesting for each address family with the type encoded
first. i.e.,
af_spec = nla_nest_start(skb, IFLA_AF_SPEC)
for each family:
af = nla_nest_start(skb, af_ops->family)
af_ops->fill_link_af(skb, dev, ext_filter_mask)
nest_end
nest_end
This allows the parser to find the address family by looking at the first
type.
Whereas AF_BRIDGE encoding is just:
af_spec = nla_nest_start(skb, IFLA_AF_SPEC)
br_fill_ifvlaninfo{_compressed}(skb, vg)
nest_end
which means the parser can not use the attribute itself to know the family
to which the attribute belongs.
Signed-off-by: David Ahern <dsa at cumulusnetworks.com>
---
lib/route/link.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 74 insertions(+), 15 deletions(-)
diff --git a/lib/route/link.c b/lib/route/link.c
index 3cb62df95d6a..5ccf6296f64d 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -489,6 +489,63 @@ int rtnl_link_info_parse(struct rtnl_link *link, struct nlattr **tb)
return 0;
}
+static int parse_af_spec_unspec(struct rtnl_link *link, struct nlattr *attr)
+{
+ struct nlattr *af_attr;
+ int remaining;
+ int err = 0;
+
+ nla_for_each_nested(af_attr, attr, remaining) {
+ struct rtnl_link_af_ops *af_ops;
+
+ /* for AF_UNSPEC the attribute contains the family for
+ * the IFLA_AF_SPEC encoding
+ */
+ af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
+ if (af_ops && af_ops->ao_parse_af) {
+ char *af_data = link->l_af_data[nla_type(af_attr)];
+
+ err = af_ops->ao_parse_af(link, af_attr, af_data);
+ if (err < 0) {
+ rtnl_link_af_ops_put(af_ops);
+ goto errout;
+ }
+ }
+ if (af_ops)
+ rtnl_link_af_ops_put(af_ops);
+ }
+
+ link->ce_mask |= LINK_ATTR_AF_SPEC;
+errout:
+ return err;
+}
+
+static int parse_af_spec_bridge(struct rtnl_link *link, struct nlattr *attr)
+{
+ struct rtnl_link_af_ops *af_ops;
+ struct nlattr *af_attr;
+ char *af_data;
+ int remaining;
+ int err = 0;
+
+ af_ops = af_lookup_and_alloc(link, AF_BRIDGE);
+ af_data = link->l_af_data[AF_BRIDGE];
+
+ if (af_ops && af_ops->ao_parse_af) {
+ nla_for_each_nested(af_attr, attr, remaining) {
+ err = af_ops->ao_parse_af(link, af_attr, af_data);
+ if (err < 0)
+ goto errout;
+ }
+ }
+
+ link->ce_mask |= LINK_ATTR_AF_SPEC;
+errout:
+ if (af_ops)
+ rtnl_link_af_ops_put(af_ops);
+ return err;
+}
+
static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *n, struct nl_parser_param *pp)
{
@@ -570,7 +627,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
if (af_ops->ao_protinfo_policy) {
tb[IFLA_PROTINFO] = (struct nlattr *)af_ops->ao_protinfo_policy;
}
- link->l_family = family = af;
+ link->l_family = af;
link->l_af_ops = af_ops;
}
@@ -601,21 +658,23 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
}
if (tb[IFLA_AF_SPEC]) {
- struct nlattr *af_attr;
- int remaining;
-
- nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) {
- af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
- if (af_ops && af_ops->ao_parse_af) {
- char *af_data = link->l_af_data[nla_type(af_attr)];
-
- err = af_ops->ao_parse_af(link, af_attr, af_data);
- if (err < 0)
- goto errout;
- }
-
+ /* parsing of IFLA_AF_SPEC is dependent on the family used
+ * in the request message.
+ */
+ switch(family) {
+ case AF_BRIDGE:
+ err = parse_af_spec_bridge(link, tb[IFLA_AF_SPEC]);
+ break;
+ case AF_UNSPEC:
+ err = parse_af_spec_unspec(link, tb[IFLA_AF_SPEC]);
+ break;
+ default:
+ err = -EINVAL;
+ NL_DBG(1, "IFLA_AF_SPEC parsing not implemented for family %d\n",
+ family);
}
- link->ce_mask |= LINK_ATTR_AF_SPEC;
+ if (err)
+ goto errout;
}
if (tb[IFLA_PROMISCUITY]) {
--
2.1.4
More information about the libnl
mailing list