[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