Skip to contentSkip to conversation

netfilter: nf_meta: support for nexthop and nexthop6

PATCH In review Anders K. Pedersen | Cohaesio 2 participants 4 messages Sep 14, 2016 – Sep 22, 2016

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 netfilter: nf_meta: support for nexthop and nexthop6

From: Anders K. Pedersen <[email protected]>

Add meta support for IPv4 nexthop and IPv6 nexthop6 (i.e. the directly connected IP address that an outgoing packet is sent to), which can be used either for matching or accounting, eg.

 # nft add rule filter postrouting \
	ip daddr 192.168.1.0/24 meta nexthop != 192.168.0.1 drop

This will drop any traffic to 192.168.1.0/24 that is not routed via 192.168.0.1.

 # nft add rule filter postrouting \
	flow table acct { meta nexthop timeout 600s counter }
 # nft add rule ip6 filter postrouting \
	flow table acct { meta nexthop6 timeout 600s counter }

These rules count outgoing traffic per nexthop. Note that the timeout releases an entry if no traffic is seen for this nexthop within 10 minutes.

---
 include/uapi/linux/netfilter/nf_tables.h |  2 ++
 net/netfilter/nft_meta.c                 | 23 +++++++++++++++++++++++
 2 files changed, 25 insertions(+)
include/uapi/linux/netfilter/nf_tables.h +2 -0
index 24161e2..6ef8ac9 100644
@@ -721,6 +721,8 @@ enum nft_meta_keys {
 	NFT_META_OIFGROUP,
 	NFT_META_CGROUP,
 	NFT_META_PRANDOM,
+	NFT_META_NEXTHOP,
+	NFT_META_NEXTHOP6,
 };
 
 /**
net/netfilter/nft_meta.c +23 -0
index 2863f34..a283c80 100644
@@ -20,6 +20,8 @@
 #include <linux/smp.h>
 #include <linux/static_key.h>
 #include <net/dst.h>
+#include <net/ip6_route.h>
+#include <net/route.h>
 #include <net/sock.h>
 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
 #include <net/netfilter/nf_tables.h>
@@ -188,6 +190,23 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 		*dest = prandom_u32_state(state);
 		break;
 	}
+	case NFT_META_NEXTHOP: {
+		const struct rtable *rt = skb_rtable(skb);
+
+		if (pkt->pf != NFPROTO_IPV4 || !rt)
+			goto err;
+		*dest = rt_nexthop(rt, ip_hdr(skb)->daddr);
+		break;
+	}
+	case NFT_META_NEXTHOP6: {
+		struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
+
+		if (pkt->pf != NFPROTO_IPV6 || !rt)
+			goto err;
+		memcpy(dest, rt6_nexthop(rt, &ipv6_hdr(skb)->daddr),
+		       sizeof(struct in6_addr));
+		break;
+	}
 	default:
 		WARN_ON(1);
 		goto err;
@@ -271,8 +290,12 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
 #ifdef CONFIG_CGROUP_NET_CLASSID
 	case NFT_META_CGROUP:
 #endif
+	case NFT_META_NEXTHOP:
 		len = sizeof(u32);
 		break;
+	case NFT_META_NEXTHOP6:
+		len = sizeof(struct in6_addr);
+		break;
 	case NFT_META_IIFNAME:
 	case NFT_META_OIFNAME:
 		len = IFNAMSIZ;
#1 lore raw

Hi Anders,

Quoted 31 lines · Anders K. Pedersen | Cohaesio

From: Anders K. Pedersen <[email protected]>

Add meta support for IPv4 nexthop and IPv6 nexthop6 (i.e. the directly connected IP address that an outgoing packet is sent to), which can be used either for matching or accounting, eg.

 # nft add rule filter postrouting \
	ip daddr 192.168.1.0/24 meta nexthop != 192.168.0.1 drop

This will drop any traffic to 192.168.1.0/24 that is not routed via 192.168.0.1.

 # nft add rule filter postrouting \
	flow table acct { meta nexthop timeout 600s counter }
 # nft add rule ip6 filter postrouting \
	flow table acct { meta nexthop6 timeout 600s counter }

These rules count outgoing traffic per nexthop. Note that the timeout releases an entry if no traffic is seen for this nexthop within 10 minutes.

---
 include/uapi/linux/netfilter/nf_tables.h |  2 ++
 net/netfilter/nft_meta.c                 | 23 +++++++++++++++++++++++
 2 files changed, 25 insertions(+)
include/uapi/linux/netfilter/nf_tables.h +0 -0
index 24161e2..6ef8ac9 100644

include/uapi/linux/netfilter/nf_tables.h enum nft_meta_keys { · line 726

Quoted 6 lines
include/uapi/linux/netfilter/nf_tables.h +2 -0
@@ -721,6 +721,8 @@ enum nft_meta_keys {
 	NFT_META_OIFGROUP,
 	NFT_META_CGROUP,
 	NFT_META_PRANDOM,
+	NFT_META_NEXTHOP,
+	NFT_META_NEXTHOP6,
 };

Florian is working on explicitly fib lookup expression, for the existing route attached to the skbuff, I think we can add nft_rt_ipv4.c, nft_rt_ipv6.c and nft_rt_inet.c expressions instead for this? One per family, have a look at nft_meta_bridge.c for reference, it should look similar.

I think many other rt fields could be useful with a valid usecase.

BTW, proposed syntax is:

# nft add rule filter postrouting \
	flow table acct { rt ip nexthop timeout 600s counter }
# nft add rule ip6 filter postrouting \
	flow table acct { rt ip6 nexthop timeout 600s counter }

Then, for the inet family:

# nft add rule inet filter postrouting \
	ether type ip flow table acct { rt ip nexthop timeout 600s counter }

This one should bail out if:

# nft add rule inet filter postrouting \
	ether type ip flow table acct { rt ip6 nexthop timeout 600s counter }
                   ~~                      ^^^

they don't match, this is just a bit of code at
nftables/src/evaluate.c

Thus, we pass an explicit NFTA_RT_FAMILY attribute to explicitly indicate the family type so we can use this from the inet table too.

You need to add a expr/rt.c expression to libnftnl, it is boiler plate code you can use meta expression as reference.

Quoted 1 line

From nft, you have to add a new EXPR_RT, there will be code missing in

evaluate.c, netlink_linearize.c and netlink_delinearize.c

net/netfilter/nft_meta.c void nft_meta_get_eval(const struct nft_expr *expr, · line 196

Quoted 13 lines

/**

net/netfilter/nft_meta.c +4 -0
index 2863f34..a283c80 100644
@@ -188,6 +190,23 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 		*dest = prandom_u32_state(state);
 		break;
 	}
+	case NFT_META_NEXTHOP: {
+		const struct rtable *rt = skb_rtable(skb);
+
+		if (pkt->pf != NFPROTO_IPV4 || !rt)

With the approach above, we will not need to check for pkt->pf != NFPROTO_IPV4, given this will be checked from the _init() path of the expression.

It will be a bit more code though.

Would you have a look at this? Let me know, thanks!

#2 lore raw

Hi Pablo,

Quoted 11 lines · Pablo Neira Ayuso

Hi Anders,

Quoted 5 lines · Anders K. Pedersen | Cohaesio

Add meta support for IPv4 nexthop and IPv6 nexthop6 (i.e. the directly
connected IP address that an outgoing packet is sent to), which can be used
either for matching or accounting, eg.

Florian is working on explicitly fib lookup expression,

I had noticed his RFC before doing this, but thought that it wouldn't be ideal for my use case, since 1) it might not give the route that is actually used in case of ECMP, and 2) it would be less efficient to perform an additional route lookup in stead of just reusing the one that has to be performed anyway for routing the packet - especially on a router with a full BGP feed (currently close to 600.000 routes).

Quoted 43 lines

for the
existing route attached to the skbuff, I think we can add nft_rt_ipv4.c, nft_rt_ipv6.c and nft_rt_inet.c expressions instead for
this? One per family, have a look at nft_meta_bridge.c for reference, it should look similar.

I think many other rt fields could be useful with a valid usecase.

BTW, proposed syntax is:

# nft add rule filter postrouting \
	flow table acct { rt ip nexthop timeout 600s counter }
# nft add rule ip6 filter postrouting \
	flow table acct { rt ip6 nexthop timeout 600s counter }

Then, for the inet family:

# nft add rule inet filter postrouting \
	ether type ip flow table acct { rt ip nexthop timeout 600s
counter }

This one should bail out if:

# nft add rule inet filter postrouting \
	ether type ip flow table acct { rt ip6 nexthop timeout 600s
counter }
                   ~~                      ^^^

they don't match, this is just a bit of code at
nftables/src/evaluate.c

Thus, we pass an explicit NFTA_RT_FAMILY attribute to explicitly indicate the family type so we can use this from the inet table too.

You need to add a expr/rt.c expression to libnftnl, it is boiler plate
code you can use meta expression as reference.

From nft, you have to add a new EXPR_RT, there will be code missing in
evaluate.c, netlink_linearize.c and netlink_delinearize.c

...

Quoted 7 lines

With the approach above, we will not need to check for pkt->pf != NFPROTO_IPV4, given this will be checked from the _init() path of the expression.

It will be a bit more code though.

Would you have a look at this? Let me know, thanks!

I had an initial look at this and found that it would involve modifying/creating the following files:

nf-next:
include/net/netfilter/nft_rt.h - new file based on nft_meta.h include/uapi/linux/netfilter/nf_tables.h
 - new NFT_RT_* and NFTA_RT_* - based on what exists for nft_meta net/netfilter/{Kconfig,Makefile} - new config NFT_RT and NFT_RT_INET net/netfilter/nft_rt.c - new file based on nft_meta.c net/netfilter/nft_rt_inet.c - new file based on nft_meta_bridge.c net/ipv4/netfilter/{Kconfig,Makefile} - new config NFT_RT_IPV4 net/ipv4/netfilter/nft_rt_ipv4.c - new file based on nft_meta_bridge.c net/ipv6/netfilter/{Kconfig,Makefile} - new config NFT_RT_IPV6 net/ipv6/netfilter/nft_rt_ipv6.c - new file based on nft_meta_bridge.c

libnftnl:
include/linux/netfilter/nf_tables.h - same as kernel changes include/libnftnl/expr.h - add new NFTNL_EXPR_RT_* and NFT_EXPR_RT_* src/expr/rt.c - new file based on meta.c
src/expr_ops.c - add references to expr_ops_rt src/Makefile.am - add expr/rt.c

nftables:
doc/nft.xml - document new rt expression include/linux/netfilter/nf_tables.h - same as kernel changes include/expression.h - add new EXPR_RT
include/rt.h - new file based on meta.h include/statement.h - add new STMT_RT and rt_stmt src/evaluate.c - add handling of EXPR_RT and STMT_RT src/netlink_linearize.c - add handling of EXPR_RT and STMT_RT src/netlink_delinearize.c - add handling of EXPR_RT and STMT_RT src/rt.c - new file based on meta.c
src/Makefile.am - add rt.c to nft_SOURCES src/parser_bison.y - define new keywords and syntax src/scanner.l - define new keywords

Does this seem right, or have I missed something?

It looks like quite a bit more code than my first attempt, but I can give it a try. I don't know how much time I'll have for this, so it will probably take some weeks to do.

Regards,
Anders K. Pedersen

#3 lore raw
Quoted 13 lines · Anders K. Pedersen | Cohaesio

Hi Pablo,

Quoted 9 lines · Pablo Neira Ayuso

Hi Anders,

Quoted 5 lines · Anders K. Pedersen | Cohaesio

Add meta support for IPv4 nexthop and IPv6 nexthop6 (i.e. the directly
connected IP address that an outgoing packet is sent to), which can be used
either for matching or accounting, eg.

Quoted 8 lines
Quoted 1 line

Florian is working on explicitly fib lookup expression,

I had noticed his RFC before doing this, but thought that it wouldn't be ideal for my use case, since 1) it might not give the route that is actually used in case of ECMP, and 2) it would be less efficient to perform an additional route lookup in stead of just reusing the one that has to be performed anyway for routing the packet - especially on a router with a full BGP feed (currently close to 600.000 routes).

Sorry, I didn't mean we should select fib or rt, I think they have both valid usecases. So I'm fine with what you're doing.

[...]

Quoted 38 lines
Quoted 1 line

Would you have a look at this? Let me know, thanks!

I had an initial look at this and found that it would involve modifying/creating the following files:

nf-next:
include/net/netfilter/nft_rt.h - new file based on nft_meta.h include/uapi/linux/netfilter/nf_tables.h
 - new NFT_RT_* and NFTA_RT_* - based on what exists for nft_meta net/netfilter/{Kconfig,Makefile} - new config NFT_RT and NFT_RT_INET net/netfilter/nft_rt.c - new file based on nft_meta.c net/netfilter/nft_rt_inet.c - new file based on nft_meta_bridge.c net/ipv4/netfilter/{Kconfig,Makefile} - new config NFT_RT_IPV4 net/ipv4/netfilter/nft_rt_ipv4.c - new file based on nft_meta_bridge.c net/ipv6/netfilter/{Kconfig,Makefile} - new config NFT_RT_IPV6 net/ipv6/netfilter/nft_rt_ipv6.c - new file based on nft_meta_bridge.c

libnftnl:
include/linux/netfilter/nf_tables.h - same as kernel changes include/libnftnl/expr.h - add new NFTNL_EXPR_RT_* and NFT_EXPR_RT_* src/expr/rt.c - new file based on meta.c
src/expr_ops.c - add references to expr_ops_rt src/Makefile.am - add expr/rt.c

nftables:
doc/nft.xml - document new rt expression include/linux/netfilter/nf_tables.h - same as kernel changes include/expression.h - add new EXPR_RT
include/rt.h - new file based on meta.h include/statement.h - add new STMT_RT and rt_stmt src/evaluate.c - add handling of EXPR_RT and STMT_RT src/netlink_linearize.c - add handling of EXPR_RT and STMT_RT src/netlink_delinearize.c - add handling of EXPR_RT and STMT_RT src/rt.c - new file based on meta.c
src/Makefile.am - add rt.c to nft_SOURCES src/parser_bison.y - define new keywords and syntax src/scanner.l - define new keywords

Quoted 5 lines

Does this seem right, or have I missed something?

It looks like quite a bit more code than my first attempt, but I can give it a try. I don't know how much time I'll have for this, so it will probably take some weeks to do.

Yes, it's a bit of more code than this, but if there are more usecase to access the skb_rtable(), then this makes sense to me.

And no problem wrt. time.

Please, let us know if you have any problem. Thanks!