OSDN Git Service

Merge tag 'acpi-5.1-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[uclinux-h8/linux.git] / net / ipv6 / netfilter / ip6t_srh.c
1 /* Kernel module to match Segment Routing Header (SRH) parameters. */
2
3 /* Author:
4  * Ahmed Abdelsalam <amsalam20@gmail.com>
5  *
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.
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 /* Test a struct->mt_invflags and a boolean for inequality */
25 #define NF_SRH_INVF(ptr, flag, boolean) \
26         ((boolean) ^ !!((ptr)->mt_invflags & (flag)))
27
28 static 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         /* 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)))
55                         return false;
56
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)))
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         /* 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)))
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          * Last Entry matching
91          * Last_Entry field was introduced in revision 6 of the SRH draft.
92          * It was called First_Segment in the previous revision
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          * Tag matchig
111          * Tag field was introduced in revision 6 of the SRH draft.
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
120 static bool srh1_mt6(const struct sk_buff *skb, struct xt_action_param *par)
121 {
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;
128
129         if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
130                 return false;
131         srh = skb_header_pointer(skb, srhoff, sizeof(_srh), &_srh);
132         if (!srh)
133                 return false;
134
135         hdrlen = ipv6_optlen(srh);
136         if (skb->len - srhoff < hdrlen)
137                 return false;
138
139         if (srh->type != IPV6_SRCRT_TYPE_4)
140                 return false;
141
142         if (srh->segments_left > srh->first_segment)
143                 return false;
144
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)))
149                         return false;
150
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)))
155                         return false;
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)))
159                         return false;
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)))
163                         return false;
164
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)))
169                         return false;
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)))
173                         return false;
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)))
177                         return false;
178
179         /**
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
183          */
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)))
187                         return false;
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)))
191                         return false;
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)))
195                         return false;
196
197         /**
198          * Tag matchig
199          * Tag field was introduced in revision 6 of the SRH draft
200          */
201         if (srhinfo->mt_flags & IP6T_SRH_TAG)
202                 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_TAG,
203                                 !(srh->tag == srhinfo->tag)))
204                         return false;
205
206         /* Previous SID matching */
207         if (srhinfo->mt_flags & IP6T_SRH_PSID) {
208                 if (srh->segments_left == srh->first_segment)
209                         return false;
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);
213                 if (!psid)
214                         return false;
215                 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_PSID,
216                                 ipv6_masked_addr_cmp(psid, &srhinfo->psid_msk,
217                                                      &srhinfo->psid_addr)))
218                         return false;
219         }
220
221         /* Next SID matching */
222         if (srhinfo->mt_flags & IP6T_SRH_NSID) {
223                 if (srh->segments_left == 0)
224                         return false;
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);
228                 if (!nsid)
229                         return false;
230                 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_NSID,
231                                 ipv6_masked_addr_cmp(nsid, &srhinfo->nsid_msk,
232                                                      &srhinfo->nsid_addr)))
233                         return false;
234         }
235
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);
240                 if (!lsid)
241                         return false;
242                 if (NF_SRH_INVF(srhinfo, IP6T_SRH_INV_LSID,
243                                 ipv6_masked_addr_cmp(lsid, &srhinfo->lsid_msk,
244                                                      &srhinfo->lsid_addr)))
245                         return false;
246         }
247         return true;
248 }
249
250 static int srh_mt6_check(const struct xt_mtchk_param *par)
251 {
252         const struct ip6t_srh *srhinfo = par->matchinfo;
253
254         if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
255                 pr_info_ratelimited("unknown srh match flags  %X\n",
256                                     srhinfo->mt_flags);
257                 return -EINVAL;
258         }
259
260         if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
261                 pr_info_ratelimited("unknown srh invflags %X\n",
262                                     srhinfo->mt_invflags);
263                 return -EINVAL;
264         }
265
266         return 0;
267 }
268
269 static int srh1_mt6_check(const struct xt_mtchk_param *par)
270 {
271         const struct ip6t_srh1 *srhinfo = par->matchinfo;
272
273         if (srhinfo->mt_flags & ~IP6T_SRH_MASK) {
274                 pr_info_ratelimited("unknown srh match flags  %X\n",
275                                     srhinfo->mt_flags);
276                 return -EINVAL;
277         }
278
279         if (srhinfo->mt_invflags & ~IP6T_SRH_INV_MASK) {
280                 pr_info_ratelimited("unknown srh invflags %X\n",
281                                     srhinfo->mt_invflags);
282                 return -EINVAL;
283         }
284
285         return 0;
286 }
287
288 static struct xt_match srh_mt6_reg[] __read_mostly = {
289         {
290                 .name           = "srh",
291                 .revision       = 0,
292                 .family         = NFPROTO_IPV6,
293                 .match          = srh_mt6,
294                 .matchsize      = sizeof(struct ip6t_srh),
295                 .checkentry     = srh_mt6_check,
296                 .me             = THIS_MODULE,
297         },
298         {
299                 .name           = "srh",
300                 .revision       = 1,
301                 .family         = NFPROTO_IPV6,
302                 .match          = srh1_mt6,
303                 .matchsize      = sizeof(struct ip6t_srh1),
304                 .checkentry     = srh1_mt6_check,
305                 .me             = THIS_MODULE,
306         }
307 };
308
309 static int __init srh_mt6_init(void)
310 {
311         return xt_register_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
312 }
313
314 static void __exit srh_mt6_exit(void)
315 {
316         xt_unregister_matches(srh_mt6_reg, ARRAY_SIZE(srh_mt6_reg));
317 }
318
319 module_init(srh_mt6_init);
320 module_exit(srh_mt6_exit);
321
322 MODULE_LICENSE("GPL");
323 MODULE_DESCRIPTION("Xtables: IPv6 Segment Routing Header match");
324 MODULE_AUTHOR("Ahmed Abdelsalam <amsalam20@gmail.com>");