]> granicus.if.org Git - libnl/commitdiff
route: Add support for MPLS address family
authorDavid Ahern <dsahern@gmail.com>
Thu, 17 Aug 2017 22:59:32 +0000 (15:59 -0700)
committerThomas Haller <thaller@redhat.com>
Fri, 18 Aug 2017 13:01:04 +0000 (15:01 +0200)
Add support for route in MPLS family. New attributes:
- RTA_NEWDST - label stack for a nexthop
- RTA_VIA - nexthop address (e.g., IPv4 or IPv6)

Other changes required:
- scope has to be universe for MPLS routes so fixup rtnl_route_guess_scope
- priority attribute can not be set for MPLS. Change rtnl_route_parse to
  not set the attribute by default for AF_MPLS.
- table attribute should not be set unless something other than the default
  table. For MPLS this attribute can not be set.

'/' is the separator in label stacks for consistency with iproute2.

Signed-off-by: David Ahern <dsahern@gmail.com>
include/netlink-private/types.h
include/netlink/route/nexthop.h
lib/route/nexthop.c
lib/route/route.c
lib/route/route_obj.c
libnl-route-3.sym

index b6c5cea06303699658f5749036dd19e2f6b7835e..ad4b1252132840fb1ef245432c123d89afc914f0 100644 (file)
@@ -311,6 +311,8 @@ struct rtnl_nexthop
        uint32_t                ce_mask; /* HACK to support attr macros */
        struct nl_list_head     rtnh_list;
        uint32_t                rtnh_realms;
+       struct nl_addr *        rtnh_newdst;
+       struct nl_addr *        rtnh_via;
 };
 
 struct rtnl_route
index 2aa44dce6fb0001ef44a2a562827b06c223a6a33..654b84df9f278ace956b72339bceb2ef9d280946 100644 (file)
@@ -55,6 +55,12 @@ extern void          rtnl_route_nh_set_realms(struct rtnl_nexthop *,
                                                 uint32_t);
 extern uint32_t                rtnl_route_nh_get_realms(struct rtnl_nexthop *);
 
+extern int             rtnl_route_nh_set_newdst(struct rtnl_nexthop *,
+                                                struct nl_addr *);
+extern struct nl_addr *        rtnl_route_nh_get_newdst(struct rtnl_nexthop *);
+extern int             rtnl_route_nh_set_via(struct rtnl_nexthop *,
+                                                struct nl_addr *);
+extern struct nl_addr *        rtnl_route_nh_get_via(struct rtnl_nexthop *);
 extern char *          rtnl_route_nh_flags2str(int, char *, size_t);
 extern int             rtnl_route_nh_str2flags(const char *);
 
index a7cda90ba208a32f1a1dcd7ebbc0599f69de0a9c..c69b23e1f0de4523a415434442b75519d11f3830 100644 (file)
@@ -27,6 +27,8 @@
 #define NH_ATTR_IFINDEX 0x000004
 #define NH_ATTR_GATEWAY 0x000008
 #define NH_ATTR_REALMS  0x000010
+#define NH_ATTR_NEWDST  0x000020
+#define NH_ATTR_VIA     0x000040
 /** @endcond */
 
 /**
@@ -69,12 +71,33 @@ struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src)
                }
        }
 
+       if (src->rtnh_newdst) {
+               nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst);
+               if (!nh->rtnh_newdst) {
+                       nl_addr_put(nh->rtnh_gateway);
+                       free(nh);
+                       return NULL;
+               }
+       }
+
+       if (src->rtnh_via) {
+               nh->rtnh_via = nl_addr_clone(src->rtnh_via);
+               if (!nh->rtnh_via) {
+                       nl_addr_put(nh->rtnh_gateway);
+                       nl_addr_put(nh->rtnh_newdst);
+                       free(nh);
+                       return NULL;
+               }
+       }
+
        return nh;
 }
 
 void rtnl_route_nh_free(struct rtnl_nexthop *nh)
 {
        nl_addr_put(nh->rtnh_gateway);
+       nl_addr_put(nh->rtnh_newdst);
+       nl_addr_put(nh->rtnh_via);
        free(nh);
 }
 
@@ -92,6 +115,10 @@ int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
        diff |= NH_DIFF(REALMS,         a->rtnh_realms != b->rtnh_realms);
        diff |= NH_DIFF(GATEWAY,        nl_addr_cmp(a->rtnh_gateway,
                                                    b->rtnh_gateway));
+       diff |= NH_DIFF(NEWDST,         nl_addr_cmp(a->rtnh_newdst,
+                                                   b->rtnh_newdst));
+       diff |= NH_DIFF(VIA,            nl_addr_cmp(a->rtnh_via,
+                                                   b->rtnh_via));
 
        if (loose)
                diff |= NH_DIFF(FLAGS,
@@ -111,8 +138,16 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
 
        link_cache = nl_cache_mngt_require_safe("route/link");
 
+       if (nh->ce_mask & NH_ATTR_NEWDST)
+               nl_dump(dp, "as to %s ",
+                       nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
+
        nl_dump(dp, "via");
 
+       if (nh->ce_mask & NH_ATTR_VIA)
+               nl_dump(dp, " %s",
+                       nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
+
        if (nh->ce_mask & NH_ATTR_GATEWAY)
                nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
                                                   buf, sizeof(buf)));
@@ -142,6 +177,14 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
 
        nl_dump(dp, "nexthop");
 
+       if (nh->ce_mask & NH_ATTR_NEWDST)
+               nl_dump(dp, " as to %s",
+                       nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
+
+       if (nh->ce_mask & NH_ATTR_VIA)
+               nl_dump(dp, " via %s",
+                       nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
+
        if (nh->ce_mask & NH_ATTR_GATEWAY)
                nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
                                                   buf, sizeof(buf)));
@@ -269,6 +312,60 @@ uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
        return nh->rtnh_realms;
 }
 
+int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr)
+{
+       struct nl_addr *old = nh->rtnh_newdst;
+
+       if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
+                          nl_addr_get_len(addr)))
+               return -NLE_INVAL;
+
+       if (addr) {
+               nh->rtnh_newdst = nl_addr_get(addr);
+               nh->ce_mask |= NH_ATTR_NEWDST;
+       } else {
+               nh->ce_mask &= ~NH_ATTR_NEWDST;
+               nh->rtnh_newdst = NULL;
+       }
+
+       if (old)
+               nl_addr_put(old);
+
+       return 0;
+}
+
+struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh)
+{
+       return nh->rtnh_newdst;
+}
+
+int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr)
+{
+       struct nl_addr *old = nh->rtnh_via;
+
+       if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
+                          nl_addr_get_len(addr)))
+               return -NLE_INVAL;
+
+       if (addr) {
+               nh->rtnh_via = nl_addr_get(addr);
+               nh->ce_mask |= NH_ATTR_VIA;
+       } else {
+               nh->ce_mask &= ~NH_ATTR_VIA;
+               nh->rtnh_via= NULL;
+       }
+
+       if (old)
+               nl_addr_put(old);
+
+       return 0;
+}
+
+struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh)
+{
+       return nh->rtnh_via;
+}
+
 /** @} */
 
 /**
index 29851873c09b48b4c02e1918ea4aa0ed30684f22..6688749ae22732aff583dbec519f7bf9e0c1f199 100644 (file)
@@ -173,6 +173,7 @@ int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags)
 static struct nl_af_group route_groups[] = {
        { AF_INET,      RTNLGRP_IPV4_ROUTE },
        { AF_INET6,     RTNLGRP_IPV6_ROUTE },
+       { AF_MPLS,      RTNLGRP_MPLS_ROUTE },
        { AF_DECnet,    RTNLGRP_DECnet_ROUTE },
        { END_OF_GROUP_LIST },
 };
index b8e9f77c1868d777403f0c574cbf2b41a87d68fc..0f03f54a686db2850fd96a7f532b5baa313db4e4 100644 (file)
@@ -345,6 +345,19 @@ static void route_keygen(struct nl_object *obj, uint32_t *hashkey,
        return;
 }
 
+static uint32_t route_id_attrs_get(struct nl_object *obj)
+{
+       struct rtnl_route *route = (struct rtnl_route *)obj;
+       struct nl_object_ops *ops = obj->ce_ops;
+       uint32_t rv = ops->oo_id_attrs;
+
+       /* MPLS address family does not allow RTA_PRIORITY to be set */
+       if (route->rt_family == AF_MPLS)
+               rv &= ~ROUTE_ATTR_PRIO;
+
+       return rv;
+}
+
 static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b,
                              uint64_t attrs, int flags)
 {
@@ -657,13 +670,17 @@ uint32_t rtnl_route_get_priority(struct rtnl_route *route)
 
 int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
 {
-       if (family != AF_INET && family != AF_INET6 && family != AF_DECnet)
-               return -NLE_AF_NOSUPPORT;
-
-       route->rt_family = family;
-       route->ce_mask |= ROUTE_ATTR_FAMILY;
+       switch(family) {
+       case AF_INET:
+       case AF_INET6:
+       case AF_DECnet:
+       case AF_MPLS:
+               route->rt_family = family;
+               route->ce_mask |= ROUTE_ATTR_FAMILY;
+               return 0;
+       }
 
-       return 0;
+       return -NLE_AF_NOSUPPORT;
 }
 
 uint8_t rtnl_route_get_family(struct rtnl_route *route)
@@ -918,6 +935,9 @@ int rtnl_route_guess_scope(struct rtnl_route *route)
        if (route->rt_type == RTN_LOCAL)
                return RT_SCOPE_HOST;
 
+       if (route->rt_family == AF_MPLS)
+               return RT_SCOPE_UNIVERSE;
+
        if (!nl_list_empty(&route->rt_nexthops)) {
                struct rtnl_nexthop *nh;
 
@@ -936,6 +956,31 @@ int rtnl_route_guess_scope(struct rtnl_route *route)
 
 /** @} */
 
+static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla)
+{
+       int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
+       struct rtvia *via = nla_data(nla);
+
+       return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
+}
+
+static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr)
+{
+       unsigned int alen = nl_addr_get_len(addr);
+       struct nlattr *nla;
+       struct rtvia *via;
+
+       nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via));
+       if (!nla)
+               return -EMSGSIZE;
+
+       via = nla_data(nla);
+       via->rtvia_family = nl_addr_get_family(addr);
+       memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen);
+
+       return 0;
+}
+
 static struct nla_policy route_policy[RTA_MAX+1] = {
        [RTA_IIF]       = { .type = NLA_U32 },
        [RTA_OIF]       = { .type = NLA_U32 },
@@ -992,6 +1037,33 @@ static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
                                realms = nla_get_u32(ntb[RTA_FLOW]);
                                rtnl_route_nh_set_realms(nh, realms);
                        }
+
+                       if (ntb[RTA_NEWDST]) {
+                               struct nl_addr *addr;
+
+                               addr = nl_addr_alloc_attr(ntb[RTA_NEWDST],
+                                                         route->rt_family);
+                               if (!addr)
+                                       goto errout;
+
+                               err = rtnl_route_nh_set_newdst(nh, addr);
+                               nl_addr_put(addr);
+                               if (err)
+                                       goto errout;
+                       }
+
+                       if (ntb[RTA_VIA]) {
+                               struct nl_addr *addr;
+
+                               addr = rtnl_route_parse_via(ntb[RTA_VIA]);
+                               if (!addr)
+                                       goto errout;
+
+                               err = rtnl_route_nh_set_via(nh, addr);
+                               nl_addr_put(addr);
+                               if (err)
+                                       goto errout;
+                       }
                }
 
                rtnl_route_add_nexthop(route, nh);
@@ -1041,7 +1113,13 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
        route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
                          ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
                          ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
-                         ROUTE_ATTR_FLAGS | ROUTE_ATTR_PRIO;
+                         ROUTE_ATTR_FLAGS;
+
+       /* right now MPLS does not allow rt_prio to be set, so don't
+        * assume it is unless it comes from an attribute
+        */
+       if (family != AF_MPLS)
+               route->ce_mask |= ROUTE_ATTR_PRIO;
 
        if (tb[RTA_DST]) {
                if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
@@ -1140,6 +1218,33 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
                rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
        }
 
+       if (tb[RTA_NEWDST]) {
+               struct nl_addr *addr;
+
+               addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family);
+               if (!addr)
+                       goto errout_nomem;
+
+               err = rtnl_route_nh_set_newdst(old_nh, addr);
+               nl_addr_put(addr);
+               if (err)
+                       goto errout;
+       }
+
+       if (tb[RTA_VIA]) {
+               int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr);
+               struct rtvia *via = nla_data(tb[RTA_VIA]);
+
+               addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
+               if (!addr)
+                       goto errout_nomem;
+
+               err = rtnl_route_nh_set_via(old_nh, addr);
+               nl_addr_put(addr);
+               if (err)
+                       goto errout;
+       }
+
        if (old_nh) {
                rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
                if (route->rt_nr_nh == 0) {
@@ -1214,12 +1319,17 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
                goto nla_put_failure;
 
        /* Additional table attribute replacing the 8bit in the header, was
-        * required to allow more than 256 tables. */
-       NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
+        * required to allow more than 256 tables. MPLS does not allow the
+        * table attribute to be set
+        */
+       if (route->rt_family != AF_MPLS)
+               NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
 
        if (nl_addr_get_len(route->rt_dst))
                NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
-       NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
+
+       if (route->ce_mask & ROUTE_ATTR_PRIO)
+               NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
 
        if (route->ce_mask & ROUTE_ATTR_SRC)
                NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
@@ -1255,6 +1365,10 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
                        NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex);
                if (nh->rtnh_realms)
                        NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
+               if (nh->rtnh_newdst)
+                       NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
+               if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0)
+                       goto nla_put_failure;
        } else if (rtnl_route_get_nnexthops(route) > 1) {
                struct nlattr *multipath;
                struct rtnl_nexthop *nh;
@@ -1277,6 +1391,13 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
                                NLA_PUT_ADDR(msg, RTA_GATEWAY,
                                             nh->rtnh_gateway);
 
+                       if (nh->rtnh_newdst)
+                               NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
+
+                       if (nh->rtnh_via &&
+                           rtnl_route_put_via(msg, nh->rtnh_via) < 0)
+                               goto nla_put_failure;
+
                        if (nh->rtnh_realms)
                                NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
 
@@ -1312,6 +1433,7 @@ struct nl_object_ops route_obj_ops = {
        .oo_id_attrs            = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
                                   ROUTE_ATTR_TABLE | ROUTE_ATTR_DST |
                                   ROUTE_ATTR_PRIO),
+       .oo_id_attrs_get        = route_id_attrs_get,
 };
 /** @endcond */
 
index 2ba920b463cf04cff0ad369b7aeeda90407cf2f5..bb2d6583b8bcc34e5ca662fa257b8cf67de5d225 100644 (file)
@@ -1053,4 +1053,8 @@ global:
        rtnl_rule_get_l3mdev;
        rtnl_rule_set_l3mdev;
        rtnl_u32_get_action;
+       rtnl_route_nh_set_newdst;
+       rtnl_route_nh_get_newdst;
+       rtnl_route_nh_set_via;
+       rtnl_route_nh_get_via;
 } libnl_3_2_29;