![Ahmed Abdelsalam](/assets/img/avatar_default.png)
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> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
162 lines
4.3 KiB
C
162 lines
4.3 KiB
C
/* 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>
|
|
|
|
/* 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_err("unknown srh match flags %X\n", srhinfo->mt_flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
|
|
pr_err("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);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
|
|
MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");
|