[PATCH 2/2] rule: Add support for protocol and port ranges

David Ahern dsa at cumulusnetworks.com
Tue Mar 13 17:17:25 PDT 2018


Add support for recent fib rule features - specifying a protocol
that installed a rule and an IP protocol plus port range for rules.

Signed-off-by: David Ahern <dsa at cumulusnetworks.com>
---
 include/netlink-private/types.h |   6 ++
 include/netlink/route/rule.h    |  16 +++
 lib/route/rule.c                | 213 ++++++++++++++++++++++++++++++++++++----
 libnl-route-3.sym               |  10 ++
 4 files changed, 228 insertions(+), 17 deletions(-)

diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 35c96899f742..db03192d1ba9 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -31,6 +31,7 @@
 #include <linux/tc_act/tc_skbedit.h>
 #include <linux/tc_act/tc_gact.h>
 #include <linux/sock_diag.h>
+#include <linux/fib_rules.h>
 
 #define NL_SOCK_PASSCRED	(1<<1)
 #define NL_OWN_PORT		(1<<2)
@@ -358,6 +359,8 @@ struct rtnl_rule
 	uint8_t		r_action;
 	uint8_t		r_dsfield; /* ipv4 only */
 	uint8_t		r_l3mdev;
+	uint8_t		r_protocol; /* protocol that installed rule */
+	uint8_t		r_ip_proto; /* IP/IPv6 protocol */
 	uint32_t	r_table;
 	uint32_t	r_flags;
 	uint32_t	r_prio;
@@ -369,6 +372,9 @@ struct rtnl_rule
 	struct nl_addr *r_dst;
 	char		r_iifname[IFNAMSIZ];
 	char		r_oifname[IFNAMSIZ];
+
+	struct fib_rule_port_range	r_sport;
+	struct fib_rule_port_range	r_dport;
 };
 
 struct rtnl_neightbl_parms
diff --git a/include/netlink/route/rule.h b/include/netlink/route/rule.h
index 7aee8d2a9cc4..d0c335fcae8e 100644
--- a/include/netlink/route/rule.h
+++ b/include/netlink/route/rule.h
@@ -69,6 +69,22 @@ extern void		rtnl_rule_set_goto(struct rtnl_rule *, uint32_t);
 extern uint32_t		rtnl_rule_get_goto(struct rtnl_rule *);
 extern void		rtnl_rule_set_l3mdev(struct rtnl_rule *, int);
 extern int		rtnl_rule_get_l3mdev(struct rtnl_rule *);
+extern int		rtnl_rule_set_protocol(struct rtnl_rule *, uint8_t);
+extern int		rtnl_rule_get_protocol(struct rtnl_rule *, uint8_t *);
+extern int		rtnl_rule_set_ipproto(struct rtnl_rule *, uint8_t);
+extern int		rtnl_rule_get_ipproto(struct rtnl_rule *, uint8_t *);
+extern int		rtnl_rule_set_sport(struct rtnl_rule *, uint16_t start);
+extern int		rtnl_rule_set_sport_range(struct rtnl_rule *,
+						  uint16_t start,
+						  uint16_t end);
+extern int		rtnl_rule_get_sport(struct rtnl_rule *, uint16_t *start,
+					    uint16_t *end);
+extern int		rtnl_rule_set_dport(struct rtnl_rule *, uint16_t start);
+extern int		rtnl_rule_set_dport_range(struct rtnl_rule *,
+						  uint16_t start,
+						  uint16_t end);
+extern int		rtnl_rule_get_dport(struct rtnl_rule *, uint16_t *start,
+					    uint16_t *end);
 
 #ifdef __cplusplus
 }
diff --git a/lib/route/rule.c b/lib/route/rule.c
index ff5ba09d4abc..6304e30f71f9 100644
--- a/lib/route/rule.c
+++ b/lib/route/rule.c
@@ -25,21 +25,25 @@
 #include <linux/fib_rules.h>
 
 /** @cond SKIP */
-#define RULE_ATTR_FAMILY	0x0001
-#define RULE_ATTR_TABLE		0x0002
-#define RULE_ATTR_ACTION	0x0004
-#define RULE_ATTR_FLAGS		0x0008
-#define RULE_ATTR_IIFNAME	0x0010
-#define RULE_ATTR_OIFNAME	0x0020
-#define RULE_ATTR_PRIO		0x0040
-#define RULE_ATTR_MARK		0x0080
-#define RULE_ATTR_MASK		0x0100
-#define RULE_ATTR_GOTO		0x0200
-#define RULE_ATTR_SRC		0x0400
-#define RULE_ATTR_DST		0x0800
-#define RULE_ATTR_DSFIELD	0x1000
-#define RULE_ATTR_FLOW		0x2000
-#define RULE_ATTR_L3MDEV	0x4000
+#define RULE_ATTR_FAMILY	0x000001
+#define RULE_ATTR_TABLE		0x000002
+#define RULE_ATTR_ACTION	0x000004
+#define RULE_ATTR_FLAGS		0x000008
+#define RULE_ATTR_IIFNAME	0x000010
+#define RULE_ATTR_OIFNAME	0x000020
+#define RULE_ATTR_PRIO		0x000040
+#define RULE_ATTR_MARK		0x000080
+#define RULE_ATTR_MASK		0x000100
+#define RULE_ATTR_GOTO		0x000200
+#define RULE_ATTR_SRC		0x000400
+#define RULE_ATTR_DST		0x000800
+#define RULE_ATTR_DSFIELD	0x001000
+#define RULE_ATTR_FLOW		0x002000
+#define RULE_ATTR_L3MDEV	0x004000
+#define RULE_ATTR_PROTOCOL	0x008000
+#define RULE_ATTR_IP_PROTO	0x010000
+#define RULE_ATTR_SPORT		0x020000
+#define RULE_ATTR_DPORT		0x040000
 
 static struct nl_cache_ops rtnl_rule_ops;
 static struct nl_object_ops rule_obj_ops;
@@ -82,6 +86,12 @@ static struct nla_policy rule_policy[FRA_MAX+1] = {
 	[FRA_GOTO]	= { .type = NLA_U32 },
 	[FRA_FLOW]	= { .type = NLA_U32 },
 	[FRA_L3MDEV]	= { .type = NLA_U8 },
+	[FRA_PROTOCOL]	= { .type = NLA_U8 },
+	[FRA_IP_PROTO]	= { .type = NLA_U8 },
+	[FRA_SPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+			      .maxlen = sizeof(struct fib_rule_port_range) },
+	[FRA_DPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+			      .maxlen = sizeof(struct fib_rule_port_range) },
 };
 
 static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
@@ -182,6 +192,32 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 		rule->ce_mask |= RULE_ATTR_L3MDEV;
 	}
 
+	if (tb[FRA_PROTOCOL]) {
+		rule->r_protocol = nla_get_u8(tb[FRA_PROTOCOL]);
+		rule->ce_mask |= RULE_ATTR_PROTOCOL;
+	}
+
+	if (tb[FRA_IP_PROTO]) {
+		rule->r_ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
+		rule->ce_mask |= RULE_ATTR_IP_PROTO;
+	}
+
+	if (tb[FRA_SPORT_RANGE]) {
+		struct fib_rule_port_range *pr;
+
+		pr = nla_data(tb[FRA_SPORT_RANGE]);
+		rule->r_sport = *pr;
+		rule->ce_mask |= RULE_ATTR_SPORT;
+	}
+
+	if (tb[FRA_DPORT_RANGE]) {
+		struct fib_rule_port_range *pr;
+
+		pr = nla_data(tb[FRA_DPORT_RANGE]);
+		rule->r_dport = *pr;
+		rule->ce_mask |= RULE_ATTR_DPORT;
+	}
+
 	err = pp->pp_cb((struct nl_object *) rule, pp);
 errout:
 	rtnl_rule_put(rule);
@@ -232,6 +268,30 @@ static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p)
 	if (r->ce_mask & RULE_ATTR_L3MDEV)
 		nl_dump(p, "lookup [l3mdev-table] ");
 
+	if (r->ce_mask & RULE_ATTR_IP_PROTO)
+		nl_dump(p, "ipproto %s ",
+			nl_ip_proto2str(r->r_ip_proto, buf, sizeof(buf)));
+
+	if (r->ce_mask & RULE_ATTR_SPORT) {
+		if (r->r_sport.start == r->r_sport.end)
+			nl_dump(p, "sport %u ", r->r_sport.start);
+		else
+			nl_dump(p, "sport %u-%u ",
+				r->r_sport.start, r->r_sport.end);
+	}
+
+	if (r->ce_mask & RULE_ATTR_DPORT) {
+		if (r->r_dport.start == r->r_dport.end)
+			nl_dump(p, "dport %u ", r->r_dport.start);
+		else
+			nl_dump(p, "dport %u-%u ",
+				r->r_dport.start, r->r_dport.end);
+	}
+
+	if (r->ce_mask & RULE_ATTR_PROTOCOL)
+		nl_dump(p, "protocol %s ",
+			rtnl_route_proto2str(r->r_protocol, buf, sizeof(buf)));
+
 	if (r->ce_mask & RULE_ATTR_FLOW)
 		nl_dump(p, "flow %s ",
 			rtnl_realms2str(r->r_flow, buf, sizeof(buf)));
@@ -256,8 +316,6 @@ static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 	rule_dump_details(obj, p);
 }
 
-#define RULE_ATTR_FLAGS		0x0008
-
 static uint64_t rule_compare(struct nl_object *_a, struct nl_object *_b,
 			     uint64_t attrs, int flags)
 {
@@ -430,6 +488,19 @@ static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags,
 	if (tmpl->ce_mask & RULE_ATTR_L3MDEV)
 		NLA_PUT_U8(msg, FRA_L3MDEV, tmpl->r_l3mdev);
 
+	if (tmpl->ce_mask & RULE_ATTR_IP_PROTO)
+		NLA_PUT_U8(msg, FRA_IP_PROTO, tmpl->r_ip_proto);
+
+	if (tmpl->ce_mask & RULE_ATTR_SPORT)
+		NLA_PUT(msg, FRA_SPORT_RANGE, sizeof(tmpl->r_sport),
+			&tmpl->r_sport);
+
+	if (tmpl->ce_mask & RULE_ATTR_DPORT)
+		NLA_PUT(msg, FRA_DPORT_RANGE, sizeof(tmpl->r_dport),
+			&tmpl->r_dport);
+
+	if (tmpl->ce_mask & RULE_ATTR_PROTOCOL)
+		NLA_PUT_U8(msg, FRA_PROTOCOL, tmpl->r_protocol);
 
 	*result = msg;
 	return 0;
@@ -744,6 +815,114 @@ int rtnl_rule_get_l3mdev(struct rtnl_rule *rule)
 	return rule->r_l3mdev;
 }
 
+int rtnl_rule_set_protocol(struct rtnl_rule *rule, uint8_t protocol)
+{
+	if (protocol) {
+		rule->r_protocol = protocol;
+		rule->ce_mask |= RULE_ATTR_PROTOCOL;
+	} else {
+		rule->r_protocol = 0;
+		rule->ce_mask &= ~((uint32_t) RULE_ATTR_PROTOCOL);
+	}
+	return 0;
+}
+
+int rtnl_rule_get_protocol(struct rtnl_rule *rule, uint8_t *protocol)
+{
+	if (!(rule->ce_mask & RULE_ATTR_PROTOCOL))
+		return -NLE_INVAL;
+
+	*protocol = rule->r_protocol;
+	return 0;
+}
+
+int rtnl_rule_set_ipproto(struct rtnl_rule *rule, uint8_t ip_proto)
+{
+	if (ip_proto) {
+		rule->r_ip_proto = ip_proto;
+		rule->ce_mask |= RULE_ATTR_IP_PROTO;
+	} else {
+		rule->r_ip_proto = 0;
+		rule->ce_mask &= ~((uint32_t) RULE_ATTR_IP_PROTO);
+	}
+	return 0;
+}
+
+int rtnl_rule_get_ipproto(struct rtnl_rule *rule, uint8_t *ip_proto)
+{
+	if (!(rule->ce_mask & RULE_ATTR_IP_PROTO))
+		return -NLE_INVAL;
+
+	*ip_proto = rule->r_ip_proto;
+	return 0;
+}
+
+static int __rtnl_rule_set_port(struct fib_rule_port_range *prange,
+				uint16_t start, uint16_t end,
+				uint64_t attr, uint64_t *mask)
+{
+	if ((start && end < start) || (end && !start))
+		return -NLE_INVAL;
+
+	if (start) {
+		prange->start = start;
+		prange->end = end;
+		*mask |= attr;
+	} else {
+		prange->start = 0;
+		prange->end = 0;
+		*mask &= ~attr;
+
+	}
+	return 0;
+}
+
+int rtnl_rule_set_sport(struct rtnl_rule *rule, uint16_t sport)
+{
+	return __rtnl_rule_set_port(&rule->r_sport, sport, sport,
+				    RULE_ATTR_SPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_set_sport_range(struct rtnl_rule *rule, uint16_t start,
+			      uint16_t end)
+{
+	return __rtnl_rule_set_port(&rule->r_sport, start, end,
+				    RULE_ATTR_SPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_get_sport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end)
+{
+	if (!(rule->ce_mask & RULE_ATTR_SPORT))
+		return -NLE_INVAL;
+
+	*start = rule->r_sport.start;
+	*end = rule->r_sport.end;
+	return 0;
+}
+
+int rtnl_rule_set_dport(struct rtnl_rule *rule, uint16_t dport)
+{
+	return __rtnl_rule_set_port(&rule->r_dport, dport, dport,
+				    RULE_ATTR_DPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_set_dport_range(struct rtnl_rule *rule, uint16_t start,
+			      uint16_t end)
+{
+	return __rtnl_rule_set_port(&rule->r_dport, start, end,
+				    RULE_ATTR_DPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_get_dport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end)
+{
+	if (!(rule->ce_mask & RULE_ATTR_DPORT))
+		return -NLE_INVAL;
+
+	*start = rule->r_dport.start;
+	*end = rule->r_dport.end;
+	return 0;
+}
+
 void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
 {
 	rule->r_flow = realms;
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 7c531a0ee131..6aca90b8260b 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1088,4 +1088,14 @@ libnl_3_5 {
         rtnl_link_geneve_get_udp_zero_csum6_rx;
         rtnl_link_geneve_set_flags;
         rtnl_link_geneve_get_flags;
+	rtnl_rule_set_protocol;
+	rtnl_rule_get_protocol;
+	rtnl_rule_set_ipproto;
+	rtnl_rule_get_ipproto;
+	rtnl_rule_set_sport;
+	rtnl_rule_set_sport_range;
+	rtnl_rule_get_sport;
+	rtnl_rule_set_dport;
+	rtnl_rule_set_dport_range;
+	rtnl_rule_get_dport;
 } libnl_3_4;
-- 
2.11.0




More information about the libnl mailing list