[PATCH v2 09/11] route: Add support for lwtunnel encapsulations

David Ahern dsahern at gmail.com
Thu Aug 17 15:59:36 PDT 2017


Add framework to support lwtunnel encapsulations per nexthop.

Encapsulations types are expected to fill in the nh_encap_ops for
building and parsing messages, compare encapsulations in routes,
dumping the encapsulations and freeing memory.

Signed-off-by: David Ahern <dsahern at gmail.com>
---
 Makefile.am                                   |  1 +
 include/netlink-private/route/nexthop-encap.h | 30 ++++++++
 include/netlink-private/types.h               |  8 +++
 lib/route/nexthop.c                           | 34 +++++++++
 lib/route/nexthop_encap.c                     | 99 +++++++++++++++++++++++++++
 lib/route/route_obj.c                         | 25 +++++++
 6 files changed, 197 insertions(+)
 create mode 100644 include/netlink-private/route/nexthop-encap.h
 create mode 100644 lib/route/nexthop_encap.c

diff --git a/Makefile.am b/Makefile.am
index 3fd6ba83ad6c..26e0f479afba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -384,6 +384,7 @@ lib_libnl_route_3_la_SOURCES = \
 	lib/route/neightbl.c \
 	lib/route/netconf.c \
 	lib/route/nexthop.c \
+	lib/route/nexthop_encap.c \
 	lib/route/pktloc.c \
 	lib/route/qdisc/blackhole.c \
 	lib/route/qdisc.c \
diff --git a/include/netlink-private/route/nexthop-encap.h b/include/netlink-private/route/nexthop-encap.h
new file mode 100644
index 000000000000..ba96b0d634a9
--- /dev/null
+++ b/include/netlink-private/route/nexthop-encap.h
@@ -0,0 +1,30 @@
+#ifndef NETLINK_NEXTHOP_ENCAP_H_
+#define	NETLINK_NEXTHOP_ENCAP_H_
+
+struct nh_encap_ops {
+	uint16_t encap_type;
+
+	int	(*build_msg)(struct nl_msg *msg, void *priv);
+	int	(*parse_msg)(struct nlattr *nla, struct rtnl_nexthop *rtnh);
+
+	int	(*compare)(void *a, void *b);
+
+	void	(*dump)(void *priv, struct nl_dump_params *dp);
+	void	(*destructor)(void *priv);
+};
+
+struct rtnl_nh_encap;
+
+/*
+ * generic nexthop encap
+ */
+void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap);
+
+int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type,
+		       struct rtnl_nexthop *rtnh);
+int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap);
+
+void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp);
+
+int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b);
+#endif
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 5d165b68b0ae..35c96899f742 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -23,6 +23,7 @@
 #include <netlink-private/object-api.h>
 #include <netlink-private/route/tc-api.h>
 #include <netlink-private/route/link/sriov.h>
+#include <netlink-private/route/nexthop-encap.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/genetlink.h>
@@ -300,6 +301,12 @@ struct rtnl_addr
 	struct rtnl_link *a_link;
 };
 
+struct rtnl_nh_encap
+{
+	struct nh_encap_ops *ops;
+	void *priv;    /* private data for encap type */
+};
+
 struct rtnl_nexthop
 {
 	uint8_t			rtnh_flags;
@@ -313,6 +320,7 @@ struct rtnl_nexthop
 	uint32_t		rtnh_realms;
 	struct nl_addr *	rtnh_newdst;
 	struct nl_addr *	rtnh_via;
+	struct rtnl_nh_encap *	rtnh_encap;
 };
 
 struct rtnl_route
diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c
index c69b23e1f0de..b38a31590359 100644
--- a/lib/route/nexthop.c
+++ b/lib/route/nexthop.c
@@ -16,6 +16,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/route/nexthop-encap.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/route/rtnl.h>
@@ -29,6 +30,7 @@
 #define NH_ATTR_REALMS  0x000010
 #define NH_ATTR_NEWDST  0x000020
 #define NH_ATTR_VIA     0x000040
+#define NH_ATTR_ENCAP   0x000080
 /** @endcond */
 
 /**
@@ -98,6 +100,12 @@ 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);
+	if (nh->rtnh_encap) {
+		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
+			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap);
+	}
 	free(nh);
 }
 
@@ -119,6 +127,8 @@ int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
 						    b->rtnh_newdst));
 	diff |= NH_DIFF(VIA,		nl_addr_cmp(a->rtnh_via,
 						    b->rtnh_via));
+	diff |= NH_DIFF(ENCAP,		nh_encap_compare(a->rtnh_encap,
+							 b->rtnh_encap));
 
 	if (loose)
 		diff |= NH_DIFF(FLAGS,
@@ -138,6 +148,9 @@ 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_ENCAP)
+		nh_encap_dump(nh->rtnh_encap, dp);
+
 	if (nh->ce_mask & NH_ATTR_NEWDST)
 		nl_dump(dp, "as to %s ",
 			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
@@ -177,6 +190,9 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
 
 	nl_dump(dp, "nexthop");
 
+	if (nh->ce_mask & NH_ATTR_ENCAP)
+		nh_encap_dump(nh->rtnh_encap, dp);
+
 	if (nh->ce_mask & NH_ATTR_NEWDST)
 		nl_dump(dp, " as to %s",
 			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
@@ -233,6 +249,24 @@ void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
 	}
 }
 
+void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap)
+{
+	if (nh->rtnh_encap) {
+		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
+			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap);
+	}
+
+	if (rtnh_encap) {
+		nh->rtnh_encap = rtnh_encap;
+		nh->ce_mask |= NH_ATTR_ENCAP;
+	} else {
+		nh->rtnh_encap = NULL;
+		nh->ce_mask &= ~NH_ATTR_ENCAP;
+	}
+}
+
 /**
  * @name Attributes
  * @{
diff --git a/lib/route/nexthop_encap.c b/lib/route/nexthop_encap.c
new file mode 100644
index 000000000000..9d9307a069d8
--- /dev/null
+++ b/lib/route/nexthop_encap.c
@@ -0,0 +1,99 @@
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/types.h>
+#include <netlink-private/route/nexthop-encap.h>
+#include <linux/lwtunnel.h>
+
+static struct lwtunnel_encap_type {
+	const char *name;
+	struct nh_encap_ops *ops;
+} lwtunnel_encap_types[__LWTUNNEL_ENCAP_MAX] = {
+	[LWTUNNEL_ENCAP_NONE] = { .name = "none" },
+	[LWTUNNEL_ENCAP_MPLS] = { .name = "mpls" },
+	[LWTUNNEL_ENCAP_IP]   = { .name = "ip" },
+	[LWTUNNEL_ENCAP_IP6]  = { .name = "ip6" },
+	[LWTUNNEL_ENCAP_ILA]  = { .name = "ila" },
+	[LWTUNNEL_ENCAP_BPF]  = { .name = "bpf" },
+};
+
+static const char *nh_encap_type2str(unsigned int type)
+{
+	if (type > LWTUNNEL_ENCAP_MAX)
+		return "unknown";
+
+	return lwtunnel_encap_types[type].name ? : "unknown";
+}
+
+void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp)
+{
+	nl_dump(dp, " encap %s ",
+		nh_encap_type2str(rtnh_encap->ops->encap_type));
+
+	if (rtnh_encap->ops && rtnh_encap->ops->dump)
+		rtnh_encap->ops->dump(rtnh_encap->priv, dp);
+}
+
+int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap)
+{
+	struct nlattr *encap;
+	int err;
+
+	if (!rtnh_encap->ops || !rtnh_encap->ops->build_msg) {
+		NL_DBG(2, "Nexthop encap type not implemented\n");
+		return -NLE_INVAL;
+	}
+
+	NLA_PUT_U16(msg, RTA_ENCAP_TYPE, rtnh_encap->ops->encap_type);
+
+	encap = nla_nest_start(msg, RTA_ENCAP);
+	if (!encap)
+		goto nla_put_failure;
+
+	err = rtnh_encap->ops->build_msg(msg, rtnh_encap->priv);
+	if (err)
+		return err;
+
+	nla_nest_end(msg, encap);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type,
+		       struct rtnl_nexthop *rtnh)
+{
+	uint16_t e_type = nla_get_u16(encap_type);
+
+	if (e_type == LWTUNNEL_ENCAP_NONE) {
+		NL_DBG(2, "RTA_ENCAP_TYPE should not be LWTUNNEL_ENCAP_NONE\n");
+		return -NLE_INVAL;
+	}
+	if (e_type > LWTUNNEL_ENCAP_MAX) {
+		NL_DBG(2, "Unknown RTA_ENCAP_TYPE: %d\n", e_type);
+		return -NLE_INVAL;
+	}
+
+	if (!lwtunnel_encap_types[e_type].ops) {
+		NL_DBG(2, "RTA_ENCAP_TYPE %s is not implemented\n",
+		       lwtunnel_encap_types[e_type].name);
+		return -NLE_MSGTYPE_NOSUPPORT;
+	}
+
+	return lwtunnel_encap_types[e_type].ops->parse_msg(encap, rtnh);
+}
+
+int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b)
+{
+	if (!a && !b)
+		return 0;
+
+	if ((a && !b) || (!a && b) || (a->ops != b->ops))
+		return 1;
+
+	if (!a->ops || !a->ops->compare)
+		return 0;
+
+	return a->ops->compare(a->priv, b->priv);
+}
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index 0f8424816705..8ae6b896294c 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -32,6 +32,7 @@
 
 #include <netlink-private/netlink.h>
 #include <netlink-private/utils.h>
+#include <netlink-private/route/nexthop-encap.h>
 #include <netlink/netlink.h>
 #include <netlink/cache.h>
 #include <netlink/utils.h>
@@ -1014,6 +1015,8 @@ static struct nla_policy route_policy[RTA_MAX+1] = {
 	[RTA_METRICS]	= { .type = NLA_NESTED },
 	[RTA_MULTIPATH]	= { .type = NLA_NESTED },
 	[RTA_TTL_PROPAGATE] = { .type = NLA_U8 },
+	[RTA_ENCAP]	= { .type = NLA_NESTED },
+	[RTA_ENCAP_TYPE] = { .type = NLA_U16 },
 };
 
 static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
@@ -1089,6 +1092,14 @@ static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
 				if (err)
 					goto errout;
 			}
+
+			if (ntb[RTA_ENCAP] && ntb[RTA_ENCAP_TYPE]) {
+				err = nh_encap_parse_msg(ntb[RTA_ENCAP],
+							 ntb[RTA_ENCAP_TYPE],
+							 nh);
+				if (err)
+					goto errout;
+			}
 		}
 
 		rtnl_route_add_nexthop(route, nh);
@@ -1275,6 +1286,13 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
 					     nla_get_u8(tb[RTA_TTL_PROPAGATE]));
 	}
 
+	if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]) {
+		err = nh_encap_parse_msg(tb[RTA_ENCAP],
+					 tb[RTA_ENCAP_TYPE], old_nh);
+		if (err)
+			goto errout;
+	}
+
 	if (old_nh) {
 		rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
 		if (route->rt_nr_nh == 0) {
@@ -1402,6 +1420,9 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
 			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_encap &&
+		    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
+			goto nla_put_failure;
 	} else if (rtnl_route_get_nnexthops(route) > 1) {
 		struct nlattr *multipath;
 		struct rtnl_nexthop *nh;
@@ -1434,6 +1455,10 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
 			if (nh->rtnh_realms)
 				NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
 
+			if (nh->rtnh_encap &&
+			    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
+				goto nla_put_failure;
+
 			rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) -
 						(void *) rtnh;
 		}
-- 
2.1.4




More information about the libnl mailing list