diff mbox series

[net-next] netfilter: add segment routing header 'srh' match

Message ID 1514545672-13827-1-git-send-email-amsalam20@gmail.com
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series [net-next] netfilter: add segment routing header 'srh' match | expand

Commit Message

Ahmed Abdelsalam Dec. 29, 2017, 11:07 a.m. UTC
It allows matching packets based on Segment Routing Header
(SRH) information.
The implementation considers revision 7 of the SRH draft.
https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07

Currently supported match options include:
(1) Next Header
(2) Hdr Ext Len
(3) Segments Left
(4) Last Entry
(5) Tag value of SRH

Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>
---
 include/uapi/linux/netfilter_ipv6/ip6t_srh.h |  63 ++++++++++
 net/ipv6/netfilter/Kconfig                   |   9 ++
 net/ipv6/netfilter/Makefile                  |   1 +
 net/ipv6/netfilter/ip6t_srh.c                | 165 +++++++++++++++++++++++++++
 4 files changed, 238 insertions(+)
 create mode 100644 include/uapi/linux/netfilter_ipv6/ip6t_srh.h
 create mode 100644 net/ipv6/netfilter/ip6t_srh.c

Comments

Ahmed Abdelsalam Jan. 2, 2018, 8:37 p.m. UTC | #1
On Fri, 29 Dec 2017 12:07:52 +0100
Ahmed Abdelsalam <amsalam20@gmail.com> wrote:

> It allows matching packets based on Segment Routing Header
> (SRH) information.
> The implementation considers revision 7 of the SRH draft.
> https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07
> 
> Currently supported match options include:
> (1) Next Header
> (2) Hdr Ext Len
> (3) Segments Left
> (4) Last Entry
> (5) Tag value of SRH
> 
> Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>
> ---
>  include/uapi/linux/netfilter_ipv6/ip6t_srh.h |  63 ++++++++++
>  net/ipv6/netfilter/Kconfig                   |   9 ++
>  net/ipv6/netfilter/Makefile                  |   1 +
>  net/ipv6/netfilter/ip6t_srh.c                | 165 +++++++++++++++++++++++++++
>  4 files changed, 238 insertions(+)
>  create mode 100644 include/uapi/linux/netfilter_ipv6/ip6t_srh.h
>  create mode 100644 net/ipv6/netfilter/ip6t_srh.c
> 
> diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_srh.h b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> new file mode 100644
> index 0000000..1b5dbd8
> --- /dev/null
> +++ b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> @@ -0,0 +1,63 @@
> +/**
> + * Definitions for Segment Routing Header 'srh' match
> + *
> + * Author:
> + *       Ahmed Abdelsalam       <amsalam20@gmail.com>
> + */
> +
> +#ifndef _IP6T_SRH_H
> +#define _IP6T_SRH_H
> +
> +#include <linux/types.h>
> +#include <linux/netfilter.h>
> +
> +/* Values for "mt_flags" field in struct ip6t_srh */
> +#define IP6T_SRH_NEXTHDR        0x0001
> +#define IP6T_SRH_LEN_EQ         0x0002
> +#define IP6T_SRH_LEN_GT         0x0004
> +#define IP6T_SRH_LEN_LT         0x0008
> +#define IP6T_SRH_SEGS_EQ        0x0010
> +#define IP6T_SRH_SEGS_GT        0x0020
> +#define IP6T_SRH_SEGS_LT        0x0040
> +#define IP6T_SRH_LAST_EQ        0x0080
> +#define IP6T_SRH_LAST_GT        0x0100
> +#define IP6T_SRH_LAST_LT        0x0200
> +#define IP6T_SRH_TAG            0x0400
> +#define IP6T_SRH_MASK           0x07FF
> +
> +/* Values for "mt_invflags" field in struct ip6t_srh */
> +#define IP6T_SRH_INV_NEXTHDR    0x0001
> +#define IP6T_SRH_INV_LEN_EQ     0x0002
> +#define IP6T_SRH_INV_LEN_GT     0x0004
> +#define IP6T_SRH_INV_LEN_LT     0x0008
> +#define IP6T_SRH_INV_SEGS_EQ    0x0010
> +#define IP6T_SRH_INV_SEGS_GT    0x0020
> +#define IP6T_SRH_INV_SEGS_LT    0x0040
> +#define IP6T_SRH_INV_LAST_EQ    0x0080
> +#define IP6T_SRH_INV_LAST_GT    0x0100
> +#define IP6T_SRH_INV_LAST_LT    0x0200
> +#define IP6T_SRH_INV_TAG        0x0400
> +#define IP6T_SRH_INV_MASK       0x07FF
> +
> +/**
> + *      struct ip6t_srh - SRH match options
> + *      @ next_hdr: Next header field of SRH
> + *      @ hdr_len: Extension header length field of SRH
> + *      @ segs_left: Segments left field of SRH
> + *      @ last_entry: Last entry field of SRH
> + *      @ tag: Tag field of SRH
> + *      @ mt_flags: match options
> + *      @ mt_invflags: Invert the sense of match options
> + */
> +
> +struct ip6t_srh {
> +	__u8                    next_hdr;
> +	__u8                    hdr_len;
> +	__u8                    segs_left;
> +	__u8                    last_entry;
> +	__u16                   tag;
> +	__u16                   mt_flags;
> +	__u16                   mt_invflags;
> +};
> +
> +#endif /*_IP6T_SRH_H*/
> diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
> index 6acb2ee..e1818eb 100644
> --- a/net/ipv6/netfilter/Kconfig
> +++ b/net/ipv6/netfilter/Kconfig
> @@ -232,6 +232,15 @@ config IP6_NF_MATCH_RT
>  
>  	  To compile it as a module, choose M here.  If unsure, say N.
>  
> +config IP6_NF_MATCH_SRH
> +        tristate '"srh" Segment Routing header match support'
> +        depends on NETFILTER_ADVANCED
> +        help
> +          srh matching allows you to match packets based on the segment
> +	  routing header of the packet.
> +
> +          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 c6ee0cd..e0d51a9 100644
> --- a/net/ipv6/netfilter/Makefile
> +++ b/net/ipv6/netfilter/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
>  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
>  
>  # targets
>  obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o
> diff --git a/net/ipv6/netfilter/ip6t_srh.c b/net/ipv6/netfilter/ip6t_srh.c
> new file mode 100644
> index 0000000..75e41dc9
> --- /dev/null
> +++ b/net/ipv6/netfilter/ip6t_srh.c
> @@ -0,0 +1,165 @@
> +/*
> + * Kernel module to match Segment Routing Header (SRH) parameters.
> + *
> + * Author:
> + * Ahmed Abdelsalam <amsalam20@gmail.com>
> + *
> + *
> + *  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_srh.h>
> +#include <linux/netfilter_ipv6/ip6_tables.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
> +MODULE_AUTHOR("Ahmed Abdelsalam  <amsalam20@gmail.com>");
> +
> +/* Test a struct->mt_invflags and a boolean for inequality */
> +#define NF_SRH_INVF(ptr, flag, boolean)	\
> +	((boolean) ^ !!((ptr)->mt_invflags & (flag)))
> +
> +static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
> +{
> +	const struct ip6t_srh *srhinfo = par->matchinfo;
> +	struct ipv6_sr_hdr *srh;
> +	struct ipv6_sr_hdr _srh;
> +	int hdrlen, srhoff = 0;
> +
> +	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
> +		return false;
> +
> +	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;
> +
> +	/* Next Header matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
> +				!(srh->nexthdr == srhinfo->next_hdr)))
> +			return false;
> +
> +	/* Header Extension Length matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
> +				!(srh->hdrlen == srhinfo->hdr_len)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
> +				!(srh->hdrlen > srhinfo->hdr_len)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
> +				!(srh->hdrlen < srhinfo->hdr_len)))
> +			return false;
> +
> +	/* Segments Left matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
> +				!(srh->segments_left == srhinfo->segs_left)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
> +				!(srh->segments_left > srhinfo->segs_left)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
> +				!(srh->segments_left < srhinfo->segs_left)))
> +			return false;
> +
> +	/**
> +	 * Last Entry matching
> +	 * Last_Entry field was introduced in revision 6 of the SRH draft.
> +	 * It was called First_Segment in the previous revision
> +	 */
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
> +				!(srh->first_segment == srhinfo->last_entry)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
> +				!(srh->first_segment > srhinfo->last_entry)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
> +				!(srh->first_segment < srhinfo->last_entry)))
> +			return false;
> +
> +	/**
> +	 * Tag matchig
> +	 * Tag field was introduced in revision 6 of the SRH draft.
> +	 */
> +	if (srhinfo->mt_flags & IP6T_SRH_TAG)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
> +				!(srh->tag == srhinfo->tag)))
> +			return false;
> +	return true;
> +}
> +
> +static int srh_mt6_check(const struct xt_mtchk_param *par)
> +{
> +	const struct ip6t_srh *srhinfo = par->matchinfo;
> +
> +	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
> +		pr_debug("unknown srh match flags  %X\n", srhinfo->mt_flags);
> +		return -EINVAL;
> +	}
> +
> +	if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
> +		pr_debug("unknown srh invflags %X\n", srhinfo->mt_invflags);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct xt_match srh_mt6_reg __read_mostly = {
> +	.name		= "srh",
> +	.family		= NFPROTO_IPV6,
> +	.match		= srh_mt6,
> +	.matchsize	= sizeof(struct ip6t_srh),
> +	.checkentry	= srh_mt6_check,
> +	.me		= THIS_MODULE,
> +};
> +
> +static int __init srh_mt6_init(void)
> +{
> +	return xt_register_match(&srh_mt6_reg);
> +}
> +
> +static void __exit srh_mt6_exit(void)
> +{
> +	xt_unregister_match(&srh_mt6_reg);
> +}
> +
> +module_init(srh_mt6_init);
> +module_exit(srh_mt6_exit);
> -- 
> 2.1.4
> 
David, 
Any comment on this patch ?
Pablo Neira Ayuso Jan. 6, 2018, 11:40 p.m. UTC | #2
Hi Ahmed,

On Fri, Dec 29, 2017 at 12:07:52PM +0100, Ahmed Abdelsalam wrote:
> It allows matching packets based on Segment Routing Header
> (SRH) information.
> The implementation considers revision 7 of the SRH draft.
> https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07
> 
> Currently supported match options include:
> (1) Next Header
> (2) Hdr Ext Len
> (3) Segments Left
> (4) Last Entry
> (5) Tag value of SRH
> 
> Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>
> ---
>  include/uapi/linux/netfilter_ipv6/ip6t_srh.h |  63 ++++++++++
>  net/ipv6/netfilter/Kconfig                   |   9 ++
>  net/ipv6/netfilter/Makefile                  |   1 +
>  net/ipv6/netfilter/ip6t_srh.c                | 165 +++++++++++++++++++++++++++
>  4 files changed, 238 insertions(+)
>  create mode 100644 include/uapi/linux/netfilter_ipv6/ip6t_srh.h
>  create mode 100644 net/ipv6/netfilter/ip6t_srh.c
> 
> diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_srh.h b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> new file mode 100644
> index 0000000..1b5dbd8
> --- /dev/null
> +++ b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> @@ -0,0 +1,63 @@
> +/**
> + * Definitions for Segment Routing Header 'srh' match
> + *
> + * Author:
> + *       Ahmed Abdelsalam       <amsalam20@gmail.com>
> + */

Please, add this in SPDX format instead.

See include/uapi/linux/netfilter/xt_owner.h for instance.

> +#ifndef _IP6T_SRH_H
> +#define _IP6T_SRH_H
> +
> +#include <linux/types.h>
> +#include <linux/netfilter.h>
> +
> +/* Values for "mt_flags" field in struct ip6t_srh */
> +#define IP6T_SRH_NEXTHDR        0x0001
> +#define IP6T_SRH_LEN_EQ         0x0002
> +#define IP6T_SRH_LEN_GT         0x0004
> +#define IP6T_SRH_LEN_LT         0x0008
> +#define IP6T_SRH_SEGS_EQ        0x0010
> +#define IP6T_SRH_SEGS_GT        0x0020
> +#define IP6T_SRH_SEGS_LT        0x0040
> +#define IP6T_SRH_LAST_EQ        0x0080
> +#define IP6T_SRH_LAST_GT        0x0100
> +#define IP6T_SRH_LAST_LT        0x0200
> +#define IP6T_SRH_TAG            0x0400
> +#define IP6T_SRH_MASK           0x07FF
> +
> +/* Values for "mt_invflags" field in struct ip6t_srh */
> +#define IP6T_SRH_INV_NEXTHDR    0x0001
> +#define IP6T_SRH_INV_LEN_EQ     0x0002
> +#define IP6T_SRH_INV_LEN_GT     0x0004
> +#define IP6T_SRH_INV_LEN_LT     0x0008
> +#define IP6T_SRH_INV_SEGS_EQ    0x0010
> +#define IP6T_SRH_INV_SEGS_GT    0x0020
> +#define IP6T_SRH_INV_SEGS_LT    0x0040
> +#define IP6T_SRH_INV_LAST_EQ    0x0080
> +#define IP6T_SRH_INV_LAST_GT    0x0100
> +#define IP6T_SRH_INV_LAST_LT    0x0200
> +#define IP6T_SRH_INV_TAG        0x0400
> +#define IP6T_SRH_INV_MASK       0x07FF

Looking at all these EQ, GT, LT... I think this should be very easy to
implement in nf_tables with no kernel changes.

You only need to add the protocol definition to:

nftables/src/exthdr.c

Would you have a look into this? This would be very much appreciated
to we keep nftables in sync with what we have in iptables.

> +
> +/**
> + *      struct ip6t_srh - SRH match options
> + *      @ next_hdr: Next header field of SRH
> + *      @ hdr_len: Extension header length field of SRH
> + *      @ segs_left: Segments left field of SRH
> + *      @ last_entry: Last entry field of SRH
> + *      @ tag: Tag field of SRH
> + *      @ mt_flags: match options
> + *      @ mt_invflags: Invert the sense of match options
> + */
> +
> +struct ip6t_srh {
> +	__u8                    next_hdr;
> +	__u8                    hdr_len;
> +	__u8                    segs_left;
> +	__u8                    last_entry;
> +	__u16                   tag;
> +	__u16                   mt_flags;
> +	__u16                   mt_invflags;
> +};
> +
> +#endif /*_IP6T_SRH_H*/
> diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
> index 6acb2ee..e1818eb 100644
> --- a/net/ipv6/netfilter/Kconfig
> +++ b/net/ipv6/netfilter/Kconfig
> @@ -232,6 +232,15 @@ config IP6_NF_MATCH_RT
>  
>  	  To compile it as a module, choose M here.  If unsure, say N.
>  
> +config IP6_NF_MATCH_SRH
> +        tristate '"srh" Segment Routing header match support'
> +        depends on NETFILTER_ADVANCED
> +        help
> +          srh matching allows you to match packets based on the segment
> +	  routing header of the packet.
> +
> +          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 c6ee0cd..e0d51a9 100644
> --- a/net/ipv6/netfilter/Makefile
> +++ b/net/ipv6/netfilter/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
>  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
>  
>  # targets
>  obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o
> diff --git a/net/ipv6/netfilter/ip6t_srh.c b/net/ipv6/netfilter/ip6t_srh.c
> new file mode 100644
> index 0000000..75e41dc9
> --- /dev/null
> +++ b/net/ipv6/netfilter/ip6t_srh.c
> @@ -0,0 +1,165 @@
> +/*
> + * Kernel module to match Segment Routing Header (SRH) parameters.
> + *
> + * Author:
> + * Ahmed Abdelsalam <amsalam20@gmail.com>
> + *
> + *
> + *  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_srh.h>
> +#include <linux/netfilter_ipv6/ip6_tables.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
> +MODULE_AUTHOR("Ahmed Abdelsalam  <amsalam20@gmail.com>");

Not a deal breaker, but modern code usually places these MODULE_*
lines at the end of the file.

> +
> +/* Test a struct->mt_invflags and a boolean for inequality */
> +#define NF_SRH_INVF(ptr, flag, boolean)	\
> +	((boolean) ^ !!((ptr)->mt_invflags & (flag)))
> +
> +static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
> +{
> +	const struct ip6t_srh *srhinfo = par->matchinfo;
> +	struct ipv6_sr_hdr *srh;
> +	struct ipv6_sr_hdr _srh;
> +	int hdrlen, srhoff = 0;
> +
> +	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
> +		return false;
> +
> +	srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
> +

Remove unnecessary line break.

> +	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;
> +
> +	/* Next Header matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
> +				!(srh->nexthdr == srhinfo->next_hdr)))
> +			return false;
> +
> +	/* Header Extension Length matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
> +				!(srh->hdrlen == srhinfo->hdr_len)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
> +				!(srh->hdrlen > srhinfo->hdr_len)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
> +				!(srh->hdrlen < srhinfo->hdr_len)))
> +			return false;
> +
> +	/* Segments Left matching */
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
> +				!(srh->segments_left == srhinfo->segs_left)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
> +				!(srh->segments_left > srhinfo->segs_left)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
> +				!(srh->segments_left < srhinfo->segs_left)))
> +			return false;
> +
> +	/**
> +	 * Last Entry matching
> +	 * Last_Entry field was introduced in revision 6 of the SRH draft.
> +	 * It was called First_Segment in the previous revision
> +	 */
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
> +				!(srh->first_segment == srhinfo->last_entry)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
> +				!(srh->first_segment > srhinfo->last_entry)))
> +			return false;
> +
> +	if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
> +				!(srh->first_segment < srhinfo->last_entry)))
> +			return false;
> +
> +	/**
> +	 * Tag matchig
> +	 * Tag field was introduced in revision 6 of the SRH draft.
> +	 */
> +	if (srhinfo->mt_flags & IP6T_SRH_TAG)
> +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
> +				!(srh->tag == srhinfo->tag)))
> +			return false;
> +	return true;
> +}
> +
> +static int srh_mt6_check(const struct xt_mtchk_param *par)
> +{
> +	const struct ip6t_srh *srhinfo = par->matchinfo;
> +
> +	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
> +		pr_debug("unknown srh match flags  %X\n", srhinfo->mt_flags);

Better call pr_err() here. I know we don't do this in other
extensions, but we should not use pr_debug() for this. ip6tables
explicit refers to 'dmesg' when -EINVAL is returned.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ahmed Abdelsalam Jan. 7, 2018, 4:29 p.m. UTC | #3
On Sun, 7 Jan 2018 00:40:03 +0100
Pablo Neira Ayuso <pablo@netfilter.org> wrote:

> Hi Ahmed,
> 
> On Fri, Dec 29, 2017 at 12:07:52PM +0100, Ahmed Abdelsalam wrote:
> > It allows matching packets based on Segment Routing Header
> > (SRH) information.
> > The implementation considers revision 7 of the SRH draft.
> > https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07
> > 
> > Currently supported match options include:
> > (1) Next Header
> > (2) Hdr Ext Len
> > (3) Segments Left
> > (4) Last Entry
> > (5) Tag value of SRH
> > 
> > Signed-off-by: Ahmed Abdelsalam <amsalam20@gmail.com>
> > ---
> >  include/uapi/linux/netfilter_ipv6/ip6t_srh.h |  63 ++++++++++
> >  net/ipv6/netfilter/Kconfig                   |   9 ++
> >  net/ipv6/netfilter/Makefile                  |   1 +
> >  net/ipv6/netfilter/ip6t_srh.c                | 165 +++++++++++++++++++++++++++
> >  4 files changed, 238 insertions(+)
> >  create mode 100644 include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> >  create mode 100644 net/ipv6/netfilter/ip6t_srh.c
> > 
> > diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_srh.h b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> > new file mode 100644
> > index 0000000..1b5dbd8
> > --- /dev/null
> > +++ b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
> > @@ -0,0 +1,63 @@
> > +/**
> > + * Definitions for Segment Routing Header 'srh' match
> > + *
> > + * Author:
> > + *       Ahmed Abdelsalam       <amsalam20@gmail.com>
> > + */
> 
> Please, add this in SPDX format instead.
> 
> See include/uapi/linux/netfilter/xt_owner.h for instance.
> 
Ok
> > +#ifndef _IP6T_SRH_H
> > +#define _IP6T_SRH_H
> > +
> > +#include <linux/types.h>
> > +#include <linux/netfilter.h>
> > +
> > +/* Values for "mt_flags" field in struct ip6t_srh */
> > +#define IP6T_SRH_NEXTHDR        0x0001
> > +#define IP6T_SRH_LEN_EQ         0x0002
> > +#define IP6T_SRH_LEN_GT         0x0004
> > +#define IP6T_SRH_LEN_LT         0x0008
> > +#define IP6T_SRH_SEGS_EQ        0x0010
> > +#define IP6T_SRH_SEGS_GT        0x0020
> > +#define IP6T_SRH_SEGS_LT        0x0040
> > +#define IP6T_SRH_LAST_EQ        0x0080
> > +#define IP6T_SRH_LAST_GT        0x0100
> > +#define IP6T_SRH_LAST_LT        0x0200
> > +#define IP6T_SRH_TAG            0x0400
> > +#define IP6T_SRH_MASK           0x07FF
> > +
> > +/* Values for "mt_invflags" field in struct ip6t_srh */
> > +#define IP6T_SRH_INV_NEXTHDR    0x0001
> > +#define IP6T_SRH_INV_LEN_EQ     0x0002
> > +#define IP6T_SRH_INV_LEN_GT     0x0004
> > +#define IP6T_SRH_INV_LEN_LT     0x0008
> > +#define IP6T_SRH_INV_SEGS_EQ    0x0010
> > +#define IP6T_SRH_INV_SEGS_GT    0x0020
> > +#define IP6T_SRH_INV_SEGS_LT    0x0040
> > +#define IP6T_SRH_INV_LAST_EQ    0x0080
> > +#define IP6T_SRH_INV_LAST_GT    0x0100
> > +#define IP6T_SRH_INV_LAST_LT    0x0200
> > +#define IP6T_SRH_INV_TAG        0x0400
> > +#define IP6T_SRH_INV_MASK       0x07FF
> 
> Looking at all these EQ, GT, LT... I think this should be very easy to
> implement in nf_tables with no kernel changes.
> 
> You only need to add the protocol definition to:
> 
> nftables/src/exthdr.c
> 
> Would you have a look into this? This would be very much appreciated
> to we keep nftables in sync with what we have in iptables.
Yes, I look into it. I will send you a patch for nf_tables as well. 
> 
> > +
> > +/**
> > + *      struct ip6t_srh - SRH match options
> > + *      @ next_hdr: Next header field of SRH
> > + *      @ hdr_len: Extension header length field of SRH
> > + *      @ segs_left: Segments left field of SRH
> > + *      @ last_entry: Last entry field of SRH
> > + *      @ tag: Tag field of SRH
> > + *      @ mt_flags: match options
> > + *      @ mt_invflags: Invert the sense of match options
> > + */
> > +
> > +struct ip6t_srh {
> > +	__u8                    next_hdr;
> > +	__u8                    hdr_len;
> > +	__u8                    segs_left;
> > +	__u8                    last_entry;
> > +	__u16                   tag;
> > +	__u16                   mt_flags;
> > +	__u16                   mt_invflags;
> > +};
> > +
> > +#endif /*_IP6T_SRH_H*/
> > diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
> > index 6acb2ee..e1818eb 100644
> > --- a/net/ipv6/netfilter/Kconfig
> > +++ b/net/ipv6/netfilter/Kconfig
> > @@ -232,6 +232,15 @@ config IP6_NF_MATCH_RT
> >  
> >  	  To compile it as a module, choose M here.  If unsure, say N.
> >  
> > +config IP6_NF_MATCH_SRH
> > +        tristate '"srh" Segment Routing header match support'
> > +        depends on NETFILTER_ADVANCED
> > +        help
> > +          srh matching allows you to match packets based on the segment
> > +	  routing header of the packet.
> > +
> > +          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 c6ee0cd..e0d51a9 100644
> > --- a/net/ipv6/netfilter/Makefile
> > +++ b/net/ipv6/netfilter/Makefile
> > @@ -54,6 +54,7 @@ obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
> >  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
> >  
> >  # targets
> >  obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o
> > diff --git a/net/ipv6/netfilter/ip6t_srh.c b/net/ipv6/netfilter/ip6t_srh.c
> > new file mode 100644
> > index 0000000..75e41dc9
> > --- /dev/null
> > +++ b/net/ipv6/netfilter/ip6t_srh.c
> > @@ -0,0 +1,165 @@
> > +/*
> > + * Kernel module to match Segment Routing Header (SRH) parameters.
> > + *
> > + * Author:
> > + * Ahmed Abdelsalam <amsalam20@gmail.com>
> > + *
> > + *
> > + *  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_srh.h>
> > +#include <linux/netfilter_ipv6/ip6_tables.h>
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
> > +MODULE_AUTHOR("Ahmed Abdelsalam  <amsalam20@gmail.com>");
> 
> Not a deal breaker, but modern code usually places these MODULE_*
> lines at the end of the file.
> 
I will move them to the end. 
> > +
> > +/* Test a struct->mt_invflags and a boolean for inequality */
> > +#define NF_SRH_INVF(ptr, flag, boolean)	\
> > +	((boolean) ^ !!((ptr)->mt_invflags & (flag)))
> > +
> > +static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
> > +{
> > +	const struct ip6t_srh *srhinfo = par->matchinfo;
> > +	struct ipv6_sr_hdr *srh;
> > +	struct ipv6_sr_hdr _srh;
> > +	int hdrlen, srhoff = 0;
> > +
> > +	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
> > +		return false;
> > +
> > +	srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
> > +
> 
> Remove unnecessary line break.
> 
Ok 
> > +	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;
> > +
> > +	/* Next Header matching */
> > +	if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
> > +				!(srh->nexthdr == srhinfo->next_hdr)))
> > +			return false;
> > +
> > +	/* Header Extension Length matching */
> > +	if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
> > +				!(srh->hdrlen == srhinfo->hdr_len)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
> > +				!(srh->hdrlen > srhinfo->hdr_len)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
> > +				!(srh->hdrlen < srhinfo->hdr_len)))
> > +			return false;
> > +
> > +	/* Segments Left matching */
> > +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
> > +				!(srh->segments_left == srhinfo->segs_left)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
> > +				!(srh->segments_left > srhinfo->segs_left)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
> > +				!(srh->segments_left < srhinfo->segs_left)))
> > +			return false;
> > +
> > +	/**
> > +	 * Last Entry matching
> > +	 * Last_Entry field was introduced in revision 6 of the SRH draft.
> > +	 * It was called First_Segment in the previous revision
> > +	 */
> > +	if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
> > +				!(srh->first_segment == srhinfo->last_entry)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
> > +				!(srh->first_segment > srhinfo->last_entry)))
> > +			return false;
> > +
> > +	if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
> > +				!(srh->first_segment < srhinfo->last_entry)))
> > +			return false;
> > +
> > +	/**
> > +	 * Tag matchig
> > +	 * Tag field was introduced in revision 6 of the SRH draft.
> > +	 */
> > +	if (srhinfo->mt_flags & IP6T_SRH_TAG)
> > +		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
> > +				!(srh->tag == srhinfo->tag)))
> > +			return false;
> > +	return true;
> > +}
> > +
> > +static int srh_mt6_check(const struct xt_mtchk_param *par)
> > +{
> > +	const struct ip6t_srh *srhinfo = par->matchinfo;
> > +
> > +	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
> > +		pr_debug("unknown srh match flags  %X\n", srhinfo->mt_flags);
> 
> Better call pr_err() here. I know we don't do this in other
> extensions, but we should not use pr_debug() for this. ip6tables
> explicit refers to 'dmesg' when -EINVAL is returned.
>
I will change this one also . 
> Thanks.

I will send a new pacth addressing these comments. 
Thanks
diff mbox series

Patch

diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_srh.h b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
new file mode 100644
index 0000000..1b5dbd8
--- /dev/null
+++ b/include/uapi/linux/netfilter_ipv6/ip6t_srh.h
@@ -0,0 +1,63 @@ 
+/**
+ * Definitions for Segment Routing Header 'srh' match
+ *
+ * Author:
+ *       Ahmed Abdelsalam       <amsalam20@gmail.com>
+ */
+
+#ifndef _IP6T_SRH_H
+#define _IP6T_SRH_H
+
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+/* Values for "mt_flags" field in struct ip6t_srh */
+#define IP6T_SRH_NEXTHDR        0x0001
+#define IP6T_SRH_LEN_EQ         0x0002
+#define IP6T_SRH_LEN_GT         0x0004
+#define IP6T_SRH_LEN_LT         0x0008
+#define IP6T_SRH_SEGS_EQ        0x0010
+#define IP6T_SRH_SEGS_GT        0x0020
+#define IP6T_SRH_SEGS_LT        0x0040
+#define IP6T_SRH_LAST_EQ        0x0080
+#define IP6T_SRH_LAST_GT        0x0100
+#define IP6T_SRH_LAST_LT        0x0200
+#define IP6T_SRH_TAG            0x0400
+#define IP6T_SRH_MASK           0x07FF
+
+/* Values for "mt_invflags" field in struct ip6t_srh */
+#define IP6T_SRH_INV_NEXTHDR    0x0001
+#define IP6T_SRH_INV_LEN_EQ     0x0002
+#define IP6T_SRH_INV_LEN_GT     0x0004
+#define IP6T_SRH_INV_LEN_LT     0x0008
+#define IP6T_SRH_INV_SEGS_EQ    0x0010
+#define IP6T_SRH_INV_SEGS_GT    0x0020
+#define IP6T_SRH_INV_SEGS_LT    0x0040
+#define IP6T_SRH_INV_LAST_EQ    0x0080
+#define IP6T_SRH_INV_LAST_GT    0x0100
+#define IP6T_SRH_INV_LAST_LT    0x0200
+#define IP6T_SRH_INV_TAG        0x0400
+#define IP6T_SRH_INV_MASK       0x07FF
+
+/**
+ *      struct ip6t_srh - SRH match options
+ *      @ next_hdr: Next header field of SRH
+ *      @ hdr_len: Extension header length field of SRH
+ *      @ segs_left: Segments left field of SRH
+ *      @ last_entry: Last entry field of SRH
+ *      @ tag: Tag field of SRH
+ *      @ mt_flags: match options
+ *      @ mt_invflags: Invert the sense of match options
+ */
+
+struct ip6t_srh {
+	__u8                    next_hdr;
+	__u8                    hdr_len;
+	__u8                    segs_left;
+	__u8                    last_entry;
+	__u16                   tag;
+	__u16                   mt_flags;
+	__u16                   mt_invflags;
+};
+
+#endif /*_IP6T_SRH_H*/
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 6acb2ee..e1818eb 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -232,6 +232,15 @@  config IP6_NF_MATCH_RT
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP6_NF_MATCH_SRH
+        tristate '"srh" Segment Routing header match support'
+        depends on NETFILTER_ADVANCED
+        help
+          srh matching allows you to match packets based on the segment
+	  routing header of the packet.
+
+          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 c6ee0cd..e0d51a9 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -54,6 +54,7 @@  obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
 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
 
 # targets
 obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o
diff --git a/net/ipv6/netfilter/ip6t_srh.c b/net/ipv6/netfilter/ip6t_srh.c
new file mode 100644
index 0000000..75e41dc9
--- /dev/null
+++ b/net/ipv6/netfilter/ip6t_srh.c
@@ -0,0 +1,165 @@ 
+/*
+ * Kernel module to match Segment Routing Header (SRH) parameters.
+ *
+ * Author:
+ * Ahmed Abdelsalam <amsalam20@gmail.com>
+ *
+ *
+ *  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_srh.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
+MODULE_AUTHOR("Ahmed Abdelsalam  <amsalam20@gmail.com>");
+
+/* Test a struct->mt_invflags and a boolean for inequality */
+#define NF_SRH_INVF(ptr, flag, boolean)	\
+	((boolean) ^ !!((ptr)->mt_invflags & (flag)))
+
+static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
+{
+	const struct ip6t_srh *srhinfo = par->matchinfo;
+	struct ipv6_sr_hdr *srh;
+	struct ipv6_sr_hdr _srh;
+	int hdrlen, srhoff = 0;
+
+	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
+		return false;
+
+	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;
+
+	/* Next Header matching */
+	if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
+				!(srh->nexthdr == srhinfo->next_hdr)))
+			return false;
+
+	/* Header Extension Length matching */
+	if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
+				!(srh->hdrlen == srhinfo->hdr_len)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
+				!(srh->hdrlen > srhinfo->hdr_len)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
+				!(srh->hdrlen < srhinfo->hdr_len)))
+			return false;
+
+	/* Segments Left matching */
+	if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
+				!(srh->segments_left == srhinfo->segs_left)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
+				!(srh->segments_left > srhinfo->segs_left)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
+				!(srh->segments_left < srhinfo->segs_left)))
+			return false;
+
+	/**
+	 * Last Entry matching
+	 * Last_Entry field was introduced in revision 6 of the SRH draft.
+	 * It was called First_Segment in the previous revision
+	 */
+	if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
+				!(srh->first_segment == srhinfo->last_entry)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
+				!(srh->first_segment > srhinfo->last_entry)))
+			return false;
+
+	if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
+				!(srh->first_segment < srhinfo->last_entry)))
+			return false;
+
+	/**
+	 * Tag matchig
+	 * Tag field was introduced in revision 6 of the SRH draft.
+	 */
+	if (srhinfo->mt_flags & IP6T_SRH_TAG)
+		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
+				!(srh->tag == srhinfo->tag)))
+			return false;
+	return true;
+}
+
+static int srh_mt6_check(const struct xt_mtchk_param *par)
+{
+	const struct ip6t_srh *srhinfo = par->matchinfo;
+
+	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
+		pr_debug("unknown srh match flags  %X\n", srhinfo->mt_flags);
+		return -EINVAL;
+	}
+
+	if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
+		pr_debug("unknown srh invflags %X\n", srhinfo->mt_invflags);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct xt_match srh_mt6_reg __read_mostly = {
+	.name		= "srh",
+	.family		= NFPROTO_IPV6,
+	.match		= srh_mt6,
+	.matchsize	= sizeof(struct ip6t_srh),
+	.checkentry	= srh_mt6_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init srh_mt6_init(void)
+{
+	return xt_register_match(&srh_mt6_reg);
+}
+
+static void __exit srh_mt6_exit(void)
+{
+	xt_unregister_match(&srh_mt6_reg);
+}
+
+module_init(srh_mt6_init);
+module_exit(srh_mt6_exit);