[PATCH v2 05/11] route: Add support for MPLS address family
David Ahern
dsahern at gmail.com
Thu Aug 17 15:59:32 PDT 2017
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 at gmail.com>
---
include/netlink-private/types.h | 2 +
include/netlink/route/nexthop.h | 6 ++
lib/route/nexthop.c | 97 +++++++++++++++++++++++++++
lib/route/route.c | 1 +
lib/route/route_obj.c | 142 +++++++++++++++++++++++++++++++++++++---
libnl-route-3.sym | 4 ++
6 files changed, 242 insertions(+), 10 deletions(-)
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index b6c5cea06303..ad4b12521328 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -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
diff --git a/include/netlink/route/nexthop.h b/include/netlink/route/nexthop.h
index 2aa44dce6fb0..654b84df9f27 100644
--- a/include/netlink/route/nexthop.h
+++ b/include/netlink/route/nexthop.h
@@ -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 *);
diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c
index a7cda90ba208..c69b23e1f0de 100644
--- a/lib/route/nexthop.c
+++ b/lib/route/nexthop.c
@@ -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;
+}
+
/** @} */
/**
diff --git a/lib/route/route.c b/lib/route/route.c
index 29851873c09b..6688749ae227 100644
--- a/lib/route/route.c
+++ b/lib/route/route.c
@@ -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 },
};
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index b8e9f77c1868..0f03f54a686d 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -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 */
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 2ba920b463cf..bb2d6583b8bc 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1053,4 +1053,8 @@ libnl_3_4 {
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;
--
2.1.4
More information about the libnl
mailing list