[nf-next] netfilter: Add support for inner IPv6 packet match

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As described in the SRv6 network programming document [1], the SRv6
information can be added to a packet in two different modes, insert or encap.

As shown below, you can see the original IPv6 packet and how it is carried in
the two different encapsulation modes.

In the insert mode the SRH header is inserted in the original IPv6 packet,
immediately after the IPv6 header and before the transport level header.
The original IPv6 header is modified, in particular the IPv6 destination
address is replaced with the IPv6 address of the first segment in the
segment list, while the original IPv6 destination address is carried in
the SRH header [2] as the last segment of the segment list.

In the encap mode the original IPv6 packet is carried as the inner packet
of an IPv6-in-IPv6 encapsulated packet. The outer IPv6 packet carries the
SRH header with the segment list.

In case of SRv6 encapsulated packets, we should be able to match the
source and destination address of the original packet since they
represent the end-to-end information.

This patch supports matching source and destination addresses of inner
IPv6 packets. It works for SRv6 and IP6IP6 encapsualted packets.

Original IPv6 packet
+-------------+--------------------+
| IPv6 header |       payload      |
+-------------+--------------------+

SRv6 packet - insert mode
+-------------+---------+--------------------+
| IPv6 header |   SRH   |       payload      |
+-------------+---------+--------------------+

SRv6 packet - encap mode
+-------------+---------+-------------+--------------------+
| IPv6 header |   SRH   | IPv6 header |       payload      |
+-------------+---------+-------------+--------------------+

[1] https://tools.ietf.org/html/draft-filsfils-spring-srv6-network-programming-03
[2] https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07

Signed-off-by: Ahmed Abdelsalam <amsalam20@xxxxxxxxx>
---
 include/uapi/linux/netfilter_ipv6/ip6t_inner6.h |  21 ++++
 net/ipv6/netfilter/Kconfig                      |  10 ++
 net/ipv6/netfilter/Makefile                     |   1 +
 net/ipv6/netfilter/ip6t_inner6.c                | 156 ++++++++++++++++++++++++
 4 files changed, 188 insertions(+)
 create mode 100644 include/uapi/linux/netfilter_ipv6/ip6t_inner6.h
 create mode 100644 net/ipv6/netfilter/ip6t_inner6.c

diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_inner6.h b/include/uapi/linux/netfilter_ipv6/ip6t_inner6.h
new file mode 100644
index 0000000..7017fa4
--- /dev/null
+++ b/include/uapi/linux/netfilter_ipv6/ip6t_inner6.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _IP6T_INNER6_H
+#define _IP6T_INNER6_H
+
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+/* Values for "invflags" field in struct ip6t_inner6 */
+#define IP6T_INNER6_INV_SRC	0x01
+#define IP6T_INNER6_INV_DST	0x02
+#define IP6T_INNER6_INV_MASK	0x03
+
+struct ip6t_inner6 {
+	/* Source and destination addr of inner IPv6 packet */
+	struct in6_addr inner_src, inner_dst;
+	/* Mask for src and dest addr of inner IPv6 packet */
+	struct in6_addr inner_smsk, inner_dmsk;
+	__u8		invflags;
+};
+
+#endif /*_IP6T_INNER6_H*/
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 4a634b7..58ed2c9 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -250,6 +250,16 @@ config IP6_NF_MATCH_SRH
 
           To compile it as a module, choose M here.  If unsure, say N.
 
+config IP6_NF_MATCH_INNER6
+        tristate '"inner6" Inner IPv6 packet match support'
+        depends on NETFILTER_ADVANCED
+        help
+	  inner6 matching allows you to match the source and destination
+	  address of inner IPv6 packet. It works for both SRv6 and IP6IP6
+	  encapsulated packets.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 # The targets
 config IP6_NF_TARGET_HL
 	tristate '"HL" hoplimit target support'
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index d984057..40e332a 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o
 obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o
 obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
 obj-$(CONFIG_IP6_NF_MATCH_SRH) += ip6t_srh.o
+obj-$(CONFIG_IP6_NF_MATCH_INNER6) += ip6t_inner6.o
 
 # targets
 obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o
diff --git a/net/ipv6/netfilter/ip6t_inner6.c b/net/ipv6/netfilter/ip6t_inner6.c
new file mode 100644
index 0000000..391fd7e
--- /dev/null
+++ b/net/ipv6/netfilter/ip6t_inner6.c
@@ -0,0 +1,156 @@
+/* Kernel module to match inner IPv6 packet parameters. */
+
+/* Author:
+ * Ahmed Abdelsalam <amsalam20@xxxxxxxxx>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version 2
+ *	of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/ipv6.h>
+#include <net/seg6.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv6/ip6t_inner6.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Test a struct->invflags and a boolean for inequality */
+#define NF_INNER6_INVF(ptr, flag, boolean)	\
+	((boolean) ^ !!((ptr)->invflags & (flag)))
+
+static inline bool
+inner6_mt_encap(const struct sk_buff *skb, int innoff,
+		const struct ip6t_inner6 *inner6info)
+{
+	const struct ipv6hdr *inner_hdr;
+	struct ipv6hdr _inner_hdr;
+
+	inner_hdr = skb_header_pointer(skb, innoff,
+				       sizeof(_inner_hdr), &_inner_hdr);
+	if (!inner_hdr)
+		return false;
+	if (NF_INNER6_INVF(inner6info, IP6T_INNER6_INV_SRC,
+			   ipv6_masked_addr_cmp(&inner_hdr->saddr,
+						&inner6info->inner_smsk,
+						&inner6info->inner_src)) ||
+	    NF_INNER6_INVF(inner6info, IP6T_INNER6_INV_DST,
+			   ipv6_masked_addr_cmp(&inner_hdr->daddr,
+						&inner6info->inner_dmsk,
+						&inner6info->inner_dst)))
+		return false;
+	return true;
+}
+
+static inline bool
+inner6_mt_insert(const struct sk_buff *skb,
+		 int srhoff, const struct ip6t_inner6 *inner6info)
+{
+	const struct ipv6_sr_hdr *srh;
+	struct ipv6_sr_hdr _srh;
+	struct in6_addr _daddr;
+	const struct in6_addr *daddr;
+	int hdrlen;
+
+	srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
+	if (!srh)
+		return false;
+	hdrlen = ipv6_optlen(srh);
+	if (skb->len - srhoff < hdrlen)
+		return false;
+	if (srh->type != IPV6_SRCRT_TYPE_4)
+		return false;
+	if (srh->segments_left > srh->first_segment)
+		return false;
+	daddr = skb_header_pointer(skb, srhoff + sizeof(struct ipv6_sr_hdr),
+				   sizeof(_daddr), &_daddr);
+	WARN_ON(!daddr);
+	if (NF_INNER6_INVF(inner6info, IP6T_INNER6_INV_SRC,
+			   ipv6_masked_addr_cmp(&ipv6_hdr(skb)->saddr,
+						&inner6info->inner_smsk,
+						&inner6info->inner_src)) ||
+	    NF_INNER6_INVF(inner6info, IP6T_INNER6_INV_DST,
+			   ipv6_masked_addr_cmp(daddr,
+						&inner6info->inner_dmsk,
+						&inner6info->inner_dst)))
+		return false;
+	return true;
+}
+
+static inline bool
+inner_match(const struct sk_buff *skb, int srhoff,
+	    int innoff, int encap, const struct ip6t_inner6 *inner6info)
+{
+	if (encap)
+		return inner6_mt_encap(skb, innoff, inner6info);
+	return inner6_mt_insert(skb, srhoff, inner6info);
+}
+
+static inline bool
+sr6_pre_processor(const struct sk_buff *skb,
+		  int *innoff, int *srhoff, int *encap)
+{
+	if (ipv6_find_hdr(skb, innoff, IPPROTO_IPV6,
+			  NULL, NULL) == IPPROTO_IPV6){
+		*encap = 1;
+		return true;
+	}
+	if (ipv6_find_hdr(skb, srhoff, IPPROTO_ROUTING,
+			  NULL, NULL) == IPPROTO_ROUTING)
+		return true;
+	return false;
+}
+
+static bool inner6_mt6(const struct sk_buff *skb, struct xt_action_param *par)
+{
+	int innoff = 0, srhoff = 0, encap = 0;
+	const struct ip6t_inner6 *inner6info = par->matchinfo;
+
+	if (!sr6_pre_processor(skb, &innoff, &srhoff, &encap))
+		return false;
+	if (!inner_match(skb, srhoff, innoff, encap, inner6info))
+		return false;
+	return true;
+}
+
+static int inner6_mt6_check(const struct xt_mtchk_param *par)
+{
+	const struct ip6t_inner6 *inner6info = par->matchinfo;
+
+	if (inner6info->invflags & ~IP6T_INNER6_INV_MASK) {
+		pr_err("unknown inner6 invflags %X\n", inner6info->invflags);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct xt_match inner6_mt6_reg __read_mostly = {
+	.name		= "inner6",
+	.family		= NFPROTO_IPV6,
+	.match		= inner6_mt6,
+	.matchsize	= sizeof(struct ip6t_inner6),
+	.checkentry	= inner6_mt6_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init inner6_mt6_init(void)
+{
+	return xt_register_match(&inner6_mt6_reg);
+}
+
+static void __exit inner6_mt6_exit(void)
+{
+	xt_unregister_match(&inner6_mt6_reg);
+}
+module_init(inner6_mt6_init);
+module_exit(inner6_mt6_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Xtables: Inner IPv6 packet match");
+MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@xxxxxxxxx>");
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux