1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Kernel module to match Segment Routing Header (SRH) parameters. */
5 * Ahmed Abdelsalam <amsalam20@gmail.com>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <linux/ipv6.h>
12 #include <linux/types.h>
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter_ipv6/ip6t_srh.h>
18 #include <linux/netfilter_ipv6/ip6_tables.h>
20 /* Test a struct->mt_invflags and a boolean for inequality */
21 #define NF_SRH_INVF(ptr, flag, boolean) \
22 ((boolean) ^ !!((ptr)->mt_invflags & (flag)))
24 static bool srh_mt6(const struct sk_buff
*skb
, struct xt_action_param
*par
)
26 const struct ip6t_srh
*srhinfo
= par
->matchinfo
;
27 struct ipv6_sr_hdr
*srh
;
28 struct ipv6_sr_hdr _srh
;
29 int hdrlen
, srhoff
= 0;
31 if (ipv6_find_hdr(skb
, &srhoff
, IPPROTO_ROUTING
, NULL
, NULL
) < 0)
33 srh
= skb_header_pointer(skb
, srhoff
, sizeof(_srh
), &_srh
);
37 hdrlen
= ipv6_optlen(srh
);
38 if (skb
->len
- srhoff
< hdrlen
)
41 if (srh
->type
!= IPV6_SRCRT_TYPE_4
)
44 if (srh
->segments_left
> srh
->first_segment
)
47 /* Next Header matching */
48 if (srhinfo
->mt_flags
& IP6T_SRH_NEXTHDR
)
49 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_NEXTHDR
,
50 !(srh
->nexthdr
== srhinfo
->next_hdr
)))
53 /* Header Extension Length matching */
54 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_EQ
)
55 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_EQ
,
56 !(srh
->hdrlen
== srhinfo
->hdr_len
)))
59 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_GT
)
60 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_GT
,
61 !(srh
->hdrlen
> srhinfo
->hdr_len
)))
64 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_LT
)
65 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_LT
,
66 !(srh
->hdrlen
< srhinfo
->hdr_len
)))
69 /* Segments Left matching */
70 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_EQ
)
71 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_EQ
,
72 !(srh
->segments_left
== srhinfo
->segs_left
)))
75 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_GT
)
76 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_GT
,
77 !(srh
->segments_left
> srhinfo
->segs_left
)))
80 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_LT
)
81 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_LT
,
82 !(srh
->segments_left
< srhinfo
->segs_left
)))
87 * Last_Entry field was introduced in revision 6 of the SRH draft.
88 * It was called First_Segment in the previous revision
90 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_EQ
)
91 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_EQ
,
92 !(srh
->first_segment
== srhinfo
->last_entry
)))
95 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_GT
)
96 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_GT
,
97 !(srh
->first_segment
> srhinfo
->last_entry
)))
100 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_LT
)
101 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_LT
,
102 !(srh
->first_segment
< srhinfo
->last_entry
)))
107 * Tag field was introduced in revision 6 of the SRH draft.
109 if (srhinfo
->mt_flags
& IP6T_SRH_TAG
)
110 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_TAG
,
111 !(srh
->tag
== srhinfo
->tag
)))
116 static bool srh1_mt6(const struct sk_buff
*skb
, struct xt_action_param
*par
)
118 int hdrlen
, psidoff
, nsidoff
, lsidoff
, srhoff
= 0;
119 const struct ip6t_srh1
*srhinfo
= par
->matchinfo
;
120 struct in6_addr
*psid
, *nsid
, *lsid
;
121 struct in6_addr _psid
, _nsid
, _lsid
;
122 struct ipv6_sr_hdr
*srh
;
123 struct ipv6_sr_hdr _srh
;
125 if (ipv6_find_hdr(skb
, &srhoff
, IPPROTO_ROUTING
, NULL
, NULL
) < 0)
127 srh
= skb_header_pointer(skb
, srhoff
, sizeof(_srh
), &_srh
);
131 hdrlen
= ipv6_optlen(srh
);
132 if (skb
->len
- srhoff
< hdrlen
)
135 if (srh
->type
!= IPV6_SRCRT_TYPE_4
)
138 if (srh
->segments_left
> srh
->first_segment
)
141 /* Next Header matching */
142 if (srhinfo
->mt_flags
& IP6T_SRH_NEXTHDR
)
143 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_NEXTHDR
,
144 !(srh
->nexthdr
== srhinfo
->next_hdr
)))
147 /* Header Extension Length matching */
148 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_EQ
)
149 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_EQ
,
150 !(srh
->hdrlen
== srhinfo
->hdr_len
)))
152 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_GT
)
153 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_GT
,
154 !(srh
->hdrlen
> srhinfo
->hdr_len
)))
156 if (srhinfo
->mt_flags
& IP6T_SRH_LEN_LT
)
157 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LEN_LT
,
158 !(srh
->hdrlen
< srhinfo
->hdr_len
)))
161 /* Segments Left matching */
162 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_EQ
)
163 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_EQ
,
164 !(srh
->segments_left
== srhinfo
->segs_left
)))
166 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_GT
)
167 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_GT
,
168 !(srh
->segments_left
> srhinfo
->segs_left
)))
170 if (srhinfo
->mt_flags
& IP6T_SRH_SEGS_LT
)
171 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_SEGS_LT
,
172 !(srh
->segments_left
< srhinfo
->segs_left
)))
176 * Last Entry matching
177 * Last_Entry field was introduced in revision 6 of the SRH draft.
178 * It was called First_Segment in the previous revision
180 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_EQ
)
181 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_EQ
,
182 !(srh
->first_segment
== srhinfo
->last_entry
)))
184 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_GT
)
185 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_GT
,
186 !(srh
->first_segment
> srhinfo
->last_entry
)))
188 if (srhinfo
->mt_flags
& IP6T_SRH_LAST_LT
)
189 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LAST_LT
,
190 !(srh
->first_segment
< srhinfo
->last_entry
)))
195 * Tag field was introduced in revision 6 of the SRH draft
197 if (srhinfo
->mt_flags
& IP6T_SRH_TAG
)
198 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_TAG
,
199 !(srh
->tag
== srhinfo
->tag
)))
202 /* Previous SID matching */
203 if (srhinfo
->mt_flags
& IP6T_SRH_PSID
) {
204 if (srh
->segments_left
== srh
->first_segment
)
206 psidoff
= srhoff
+ sizeof(struct ipv6_sr_hdr
) +
207 ((srh
->segments_left
+ 1) * sizeof(struct in6_addr
));
208 psid
= skb_header_pointer(skb
, psidoff
, sizeof(_psid
), &_psid
);
211 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_PSID
,
212 ipv6_masked_addr_cmp(psid
, &srhinfo
->psid_msk
,
213 &srhinfo
->psid_addr
)))
217 /* Next SID matching */
218 if (srhinfo
->mt_flags
& IP6T_SRH_NSID
) {
219 if (srh
->segments_left
== 0)
221 nsidoff
= srhoff
+ sizeof(struct ipv6_sr_hdr
) +
222 ((srh
->segments_left
- 1) * sizeof(struct in6_addr
));
223 nsid
= skb_header_pointer(skb
, nsidoff
, sizeof(_nsid
), &_nsid
);
226 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_NSID
,
227 ipv6_masked_addr_cmp(nsid
, &srhinfo
->nsid_msk
,
228 &srhinfo
->nsid_addr
)))
232 /* Last SID matching */
233 if (srhinfo
->mt_flags
& IP6T_SRH_LSID
) {
234 lsidoff
= srhoff
+ sizeof(struct ipv6_sr_hdr
);
235 lsid
= skb_header_pointer(skb
, lsidoff
, sizeof(_lsid
), &_lsid
);
238 if (NF_SRH_INVF(srhinfo
, IP6T_SRH_INV_LSID
,
239 ipv6_masked_addr_cmp(lsid
, &srhinfo
->lsid_msk
,
240 &srhinfo
->lsid_addr
)))
246 static int srh_mt6_check(const struct xt_mtchk_param
*par
)
248 const struct ip6t_srh
*srhinfo
= par
->matchinfo
;
250 if (srhinfo
->mt_flags
& ~IP6T_SRH_MASK
) {
251 pr_info_ratelimited("unknown srh match flags %X\n",
256 if (srhinfo
->mt_invflags
& ~IP6T_SRH_INV_MASK
) {
257 pr_info_ratelimited("unknown srh invflags %X\n",
258 srhinfo
->mt_invflags
);
265 static int srh1_mt6_check(const struct xt_mtchk_param
*par
)
267 const struct ip6t_srh1
*srhinfo
= par
->matchinfo
;
269 if (srhinfo
->mt_flags
& ~IP6T_SRH_MASK
) {
270 pr_info_ratelimited("unknown srh match flags %X\n",
275 if (srhinfo
->mt_invflags
& ~IP6T_SRH_INV_MASK
) {
276 pr_info_ratelimited("unknown srh invflags %X\n",
277 srhinfo
->mt_invflags
);
284 static struct xt_match srh_mt6_reg
[] __read_mostly
= {
288 .family
= NFPROTO_IPV6
,
290 .matchsize
= sizeof(struct ip6t_srh
),
291 .checkentry
= srh_mt6_check
,
297 .family
= NFPROTO_IPV6
,
299 .matchsize
= sizeof(struct ip6t_srh1
),
300 .checkentry
= srh1_mt6_check
,
305 static int __init
srh_mt6_init(void)
307 return xt_register_matches(srh_mt6_reg
, ARRAY_SIZE(srh_mt6_reg
));
310 static void __exit
srh_mt6_exit(void)
312 xt_unregister_matches(srh_mt6_reg
, ARRAY_SIZE(srh_mt6_reg
));
315 module_init(srh_mt6_init
);
316 module_exit(srh_mt6_exit
);
318 MODULE_LICENSE("GPL");
319 MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
320 MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");