File Index
Files and directories
Sign in
FILE

ip6t_srh.c

File preview with syntax highlighting.

linux-6.19.14/net/ipv6/netfilter/ip6t_srh.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* Kernel module to match Segment Routing Header (SRH) parameters. */
  3
  4/* Author:
  5 * Ahmed Abdelsalam <amsalam20@gmail.com>
  6 */
  7
  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>
 13#include <net/ipv6.h>
 14#include <net/seg6.h>
 15
 16#include <linux/netfilter/x_tables.h>
 17#include <linux/netfilter_ipv6/ip6t_srh.h>
 18#include <linux/netfilter_ipv6/ip6_tables.h>
 19
 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)))
 23
 24static bool srh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
 25{
 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;
 30
 31	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
 32		return false;
 33	srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
 34	if (!srh)
 35		return false;
 36
 37	hdrlen = ipv6_optlen(srh);
 38	if (skb->len - srhoff < hdrlen)
 39		return false;
 40
 41	if (srh->type != IPV6_SRCRT_TYPE_4)
 42		return false;
 43
 44	if (srh->segments_left > srh->first_segment)
 45		return false;
 46
 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)))
 51			return false;
 52
 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)))
 57			return false;
 58
 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)))
 62			return false;
 63
 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)))
 67			return false;
 68
 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)))
 73			return false;
 74
 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)))
 78			return false;
 79
 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)))
 83			return false;
 84
 85	/**
 86	 * Last Entry matching
 87	 * Last_Entry field was introduced in revision 6 of the SRH draft.
 88	 * It was called First_Segment in the previous revision
 89	 */
 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)))
 93			return false;
 94
 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)))
 98			return false;
 99
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)))
103			return false;
104
105	/**
106	 * Tag matchig
107	 * Tag field was introduced in revision 6 of the SRH draft.
108	 */
109	if (srhinfo->mt_flags & IP6T_SRH_TAG)
110		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
111				!(srh->tag == srhinfo->tag)))
112			return false;
113	return true;
114}
115
116static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
117{
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;
124
125	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
126		return false;
127	srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
128	if (!srh)
129		return false;
130
131	hdrlen = ipv6_optlen(srh);
132	if (skb->len - srhoff < hdrlen)
133		return false;
134
135	if (srh->type != IPV6_SRCRT_TYPE_4)
136		return false;
137
138	if (srh->segments_left > srh->first_segment)
139		return false;
140
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)))
145			return false;
146
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)))
151			return false;
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)))
155			return false;
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)))
159			return false;
160
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)))
165			return false;
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)))
169			return false;
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)))
173			return false;
174
175	/**
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
179	 */
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)))
183			return false;
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)))
187			return false;
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)))
191			return false;
192
193	/**
194	 * Tag matchig
195	 * Tag field was introduced in revision 6 of the SRH draft
196	 */
197	if (srhinfo->mt_flags & IP6T_SRH_TAG)
198		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
199				!(srh->tag == srhinfo->tag)))
200			return false;
201
202	/* Previous SID matching */
203	if (srhinfo->mt_flags & IP6T_SRH_PSID) {
204		if (srh->segments_left == srh->first_segment)
205			return false;
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);
209		if (!psid)
210			return false;
211		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID,
212				ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk,
213						     &srhinfo->psid_addr)))
214			return false;
215	}
216
217	/* Next SID matching */
218	if (srhinfo->mt_flags & IP6T_SRH_NSID) {
219		if (srh->segments_left == 0)
220			return false;
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);
224		if (!nsid)
225			return false;
226		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NSID,
227				ipv6_masked_addr_cmp(nsid, &srhinfo->nsid_msk,
228						     &srhinfo->nsid_addr)))
229			return false;
230	}
231
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);
236		if (!lsid)
237			return false;
238		if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID,
239				ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk,
240						     &srhinfo->lsid_addr)))
241			return false;
242	}
243	return true;
244}
245
246static int srh_mt6_check(const struct xt_mtchk_param *par)
247{
248	const struct ip6t_srh *srhinfo = par->matchinfo;
249
250	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
251		pr_info_ratelimited("unknown srh match flags  %X\n",
252				    srhinfo->mt_flags);
253		return -EINVAL;
254	}
255
256	if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
257		pr_info_ratelimited("unknown srh invflags %X\n",
258				    srhinfo->mt_invflags);
259		return -EINVAL;
260	}
261
262	return 0;
263}
264
265static int srh1_mt6_check(const struct xt_mtchk_param *par)
266{
267	const struct ip6t_srh1 *srhinfo = par->matchinfo;
268
269	if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
270		pr_info_ratelimited("unknown srh match flags  %X\n",
271				    srhinfo->mt_flags);
272		return -EINVAL;
273	}
274
275	if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
276		pr_info_ratelimited("unknown srh invflags %X\n",
277				    srhinfo->mt_invflags);
278		return -EINVAL;
279	}
280
281	return 0;
282}
283
284static struct xt_match srh_mt6_reg[] __read_mostly = {
285	{
286		.name		= "srh",
287		.revision	= 0,
288		.family		= NFPROTO_IPV6,
289		.match		= srh_mt6,
290		.matchsize	= sizeof(struct ip6t_srh),
291		.checkentry	= srh_mt6_check,
292		.me		= THIS_MODULE,
293	},
294	{
295		.name           = "srh",
296		.revision       = 1,
297		.family         = NFPROTO_IPV6,
298		.match          = srh1_mt6,
299		.matchsize      = sizeof(struct ip6t_srh1),
300		.checkentry     = srh1_mt6_check,
301		.me             = THIS_MODULE,
302	}
303};
304
305static int __init srh_mt6_init(void)
306{
307	return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
308}
309
310static void __exit srh_mt6_exit(void)
311{
312	xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
313}
314
315module_init(srh_mt6_init);
316module_exit(srh_mt6_exit);
317
318MODULE_LICENSE("GPL");
319MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
320MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");
~/ Read-only file preview.