1
2
3
4
5
6
7
8
9
10
11
12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13#include <linux/module.h>
14#include <linux/skbuff.h>
15#include <linux/ipv6.h>
16#include <linux/types.h>
17#include <net/ipv6.h>
18#include <net/seg6.h>
19
20#include <linux/netfilter/x_tables.h>
21#include <linux/netfilter_ipv6/ip6t_srh.h>
22#include <linux/netfilter_ipv6/ip6_tables.h>
23
24
25#define NF_SRH_INVF(ptr, flag, boolean) \
26 ((boolean) ^ !!((ptr)->mt_invflags & (flag)))
27
28static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
29{
30 const struct ip6t_srh *srhinfo = par->matchinfo;
31 struct ipv6_sr_hdr *srh;
32 struct ipv6_sr_hdr _srh;
33 int hdrlen, srhoff = 0;
34
35 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
36 return false;
37 srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
38 if (!srh)
39 return false;
40
41 hdrlen = ipv6_optlen(srh);
42 if (skb->len - srhoff < hdrlen)
43 return false;
44
45 if (srh->type != IPV6_SRCRT_TYPE_4)
46 return false;
47
48 if (srh->segments_left > srh->first_segment)
49 return false;
50
51
52 if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
53 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
54 !(srh->nexthdr == srhinfo->next_hdr)))
55 return false;
56
57
58 if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
59 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
60 !(srh->hdrlen == srhinfo->hdr_len)))
61 return false;
62
63 if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
64 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
65 !(srh->hdrlen > srhinfo->hdr_len)))
66 return false;
67
68 if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
69 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
70 !(srh->hdrlen < srhinfo->hdr_len)))
71 return false;
72
73
74 if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
75 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
76 !(srh->segments_left == srhinfo->segs_left)))
77 return false;
78
79 if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
80 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
81 !(srh->segments_left > srhinfo->segs_left)))
82 return false;
83
84 if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
85 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
86 !(srh->segments_left < srhinfo->segs_left)))
87 return false;
88
89
90
91
92
93
94 if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
95 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
96 !(srh->first_segment == srhinfo->last_entry)))
97 return false;
98
99 if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
100 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
101 !(srh->first_segment > srhinfo->last_entry)))
102 return false;
103
104 if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
105 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
106 !(srh->first_segment < srhinfo->last_entry)))
107 return false;
108
109
110
111
112
113 if (srhinfo->mt_flags & IP6T_SRH_TAG)
114 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
115 !(srh->tag == srhinfo->tag)))
116 return false;
117 return true;
118}
119
120static int srh_mt6_check(const struct xt_mtchk_param *par)
121{
122 const struct ip6t_srh *srhinfo = par->matchinfo;
123
124 if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
125 pr_info_ratelimited("unknown srh match flags %X\n",
126 srhinfo->mt_flags);
127 return -EINVAL;
128 }
129
130 if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
131 pr_info_ratelimited("unknown srh invflags %X\n",
132 srhinfo->mt_invflags);
133 return -EINVAL;
134 }
135
136 return 0;
137}
138
139static struct xt_match srh_mt6_reg __read_mostly = {
140 .name = "srh",
141 .family = NFPROTO_IPV6,
142 .match = srh_mt6,
143 .matchsize = sizeof(struct ip6t_srh),
144 .checkentry = srh_mt6_check,
145 .me = THIS_MODULE,
146};
147
148static int __init srh_mt6_init(void)
149{
150 return xt_register_match(&srh_mt6_reg);
151}
152
153static void __exit srh_mt6_exit(void)
154{
155 xt_unregister_match(&srh_mt6_reg);
156}
157
158module_init(srh_mt6_init);
159module_exit(srh_mt6_exit);
160
161MODULE_LICENSE("GPL");
162MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
163MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");
164