1 /* Kernel module to match Segment Routing Header (SRH) parameters. */
4 * Ahmed Abdelsalam <amsalam20@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
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>
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter_ipv6/ip6t_srh.h>
22 #include <linux/netfilter_ipv6/ip6_tables.h>
24 /* Test a struct->mt_invflags and a boolean for inequality */
25 #define NF_SRH_INVF(ptr, flag, boolean) \
26 ((boolean) ^ !!((ptr)->mt_invflags & (flag)))
28 static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
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;
35 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
37 srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
41 hdrlen = ipv6_optlen(srh);
42 if (skb->len - srhoff < hdrlen)
45 if (srh->type != IPV6_SRCRT_TYPE_4)
48 if (srh->segments_left > srh->first_segment)
51 /* Next Header matching */
52 if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
53 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
54 !(srh->nexthdr == srhinfo->next_hdr)))
57 /* Header Extension Length matching */
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)))
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)))
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)))
73 /* Segments Left matching */
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)))
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)))
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)))
91 * Last_Entry field was introduced in revision 6 of the SRH draft.
92 * It was called First_Segment in the previous revision
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)))
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)))
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)))
111 * Tag field was introduced in revision 6 of the SRH draft.
113 if (srhinfo->mt_flags & IP6T_SRH_TAG)
114 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
115 !(srh->tag == srhinfo->tag)))
120 static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
122 int hdrlen, psidoff, nsidoff, lsidoff, srhoff = 0;
123 const struct ip6t_srh1 *srhinfo = par->matchinfo;
124 struct in6_addr *psid, *nsid, *lsid;
125 struct in6_addr _psid, _nsid, _lsid;
126 struct ipv6_sr_hdr *srh;
127 struct ipv6_sr_hdr _srh;
129 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
131 srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
135 hdrlen = ipv6_optlen(srh);
136 if (skb->len - srhoff < hdrlen)
139 if (srh->type != IPV6_SRCRT_TYPE_4)
142 if (srh->segments_left > srh->first_segment)
145 /* Next Header matching */
146 if (srhinfo->mt_flags & IP6T_SRH_NEXTHDR)
147 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NEXTHDR,
148 !(srh->nexthdr == srhinfo->next_hdr)))
151 /* Header Extension Length matching */
152 if (srhinfo->mt_flags & IP6T_SRH_LEN_EQ)
153 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_EQ,
154 !(srh->hdrlen == srhinfo->hdr_len)))
156 if (srhinfo->mt_flags & IP6T_SRH_LEN_GT)
157 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_GT,
158 !(srh->hdrlen > srhinfo->hdr_len)))
160 if (srhinfo->mt_flags & IP6T_SRH_LEN_LT)
161 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LEN_LT,
162 !(srh->hdrlen < srhinfo->hdr_len)))
165 /* Segments Left matching */
166 if (srhinfo->mt_flags & IP6T_SRH_SEGS_EQ)
167 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_EQ,
168 !(srh->segments_left == srhinfo->segs_left)))
170 if (srhinfo->mt_flags & IP6T_SRH_SEGS_GT)
171 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_GT,
172 !(srh->segments_left > srhinfo->segs_left)))
174 if (srhinfo->mt_flags & IP6T_SRH_SEGS_LT)
175 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_SEGS_LT,
176 !(srh->segments_left < srhinfo->segs_left)))
180 * Last Entry matching
181 * Last_Entry field was introduced in revision 6 of the SRH draft.
182 * It was called First_Segment in the previous revision
184 if (srhinfo->mt_flags & IP6T_SRH_LAST_EQ)
185 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_EQ,
186 !(srh->first_segment == srhinfo->last_entry)))
188 if (srhinfo->mt_flags & IP6T_SRH_LAST_GT)
189 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_GT,
190 !(srh->first_segment > srhinfo->last_entry)))
192 if (srhinfo->mt_flags & IP6T_SRH_LAST_LT)
193 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LAST_LT,
194 !(srh->first_segment < srhinfo->last_entry)))
199 * Tag field was introduced in revision 6 of the SRH draft
201 if (srhinfo->mt_flags & IP6T_SRH_TAG)
202 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
203 !(srh->tag == srhinfo->tag)))
206 /* Previous SID matching */
207 if (srhinfo->mt_flags & IP6T_SRH_PSID) {
208 if (srh->segments_left == srh->first_segment)
210 psidoff = srhoff + sizeof(struct ipv6_sr_hdr) +
211 ((srh->segments_left + 1) * sizeof(struct in6_addr));
212 psid = skb_header_pointer(skb, psidoff, sizeof(_psid), &_psid);
215 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID,
216 ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk,
217 &srhinfo->psid_addr)))
221 /* Next SID matching */
222 if (srhinfo->mt_flags & IP6T_SRH_NSID) {
223 if (srh->segments_left == 0)
225 nsidoff = srhoff + sizeof(struct ipv6_sr_hdr) +
226 ((srh->segments_left - 1) * sizeof(struct in6_addr));
227 nsid = skb_header_pointer(skb, nsidoff, sizeof(_nsid), &_nsid);
230 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NSID,
231 ipv6_masked_addr_cmp(nsid, &srhinfo->nsid_msk,
232 &srhinfo->nsid_addr)))
236 /* Last SID matching */
237 if (srhinfo->mt_flags & IP6T_SRH_LSID) {
238 lsidoff = srhoff + sizeof(struct ipv6_sr_hdr);
239 lsid = skb_header_pointer(skb, lsidoff, sizeof(_lsid), &_lsid);
242 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID,
243 ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk,
244 &srhinfo->lsid_addr)))
250 static int srh_mt6_check(const struct xt_mtchk_param *par)
252 const struct ip6t_srh *srhinfo = par->matchinfo;
254 if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
255 pr_info_ratelimited("unknown srh match flags %X\n",
260 if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
261 pr_info_ratelimited("unknown srh invflags %X\n",
262 srhinfo->mt_invflags);
269 static int srh1_mt6_check(const struct xt_mtchk_param *par)
271 const struct ip6t_srh1 *srhinfo = par->matchinfo;
273 if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
274 pr_info_ratelimited("unknown srh match flags %X\n",
279 if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
280 pr_info_ratelimited("unknown srh invflags %X\n",
281 srhinfo->mt_invflags);
288 static struct xt_match srh_mt6_reg[] __read_mostly = {
292 .family = NFPROTO_IPV6,
294 .matchsize = sizeof(struct ip6t_srh),
295 .checkentry = srh_mt6_check,
301 .family = NFPROTO_IPV6,
303 .matchsize = sizeof(struct ip6t_srh1),
304 .checkentry = srh1_mt6_check,
309 static int __init srh_mt6_init(void)
311 return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
314 static void __exit srh_mt6_exit(void)
316 xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
319 module_init(srh_mt6_init);
320 module_exit(srh_mt6_exit);
322 MODULE_LICENSE("GPL");
323 MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
324 MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");