1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org> */ |
3 | |
4 | /* Kernel module implementing an IP set type: the hash:net,port type */ |
5 | |
6 | #include <linux/jhash.h> |
7 | #include <linux/module.h> |
8 | #include <linux/ip.h> |
9 | #include <linux/skbuff.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/random.h> |
12 | #include <net/ip.h> |
13 | #include <net/ipv6.h> |
14 | #include <net/netlink.h> |
15 | |
16 | #include <linux/netfilter.h> |
17 | #include <linux/netfilter/ipset/pfxlen.h> |
18 | #include <linux/netfilter/ipset/ip_set.h> |
19 | #include <linux/netfilter/ipset/ip_set_getport.h> |
20 | #include <linux/netfilter/ipset/ip_set_hash.h> |
21 | |
22 | #define IPSET_TYPE_REV_MIN 0 |
23 | /* 1 SCTP and UDPLITE support added */ |
24 | /* 2 Range as input support for IPv4 added */ |
25 | /* 3 nomatch flag support added */ |
26 | /* 4 Counters support added */ |
27 | /* 5 Comments support added */ |
28 | /* 6 Forceadd support added */ |
29 | /* 7 skbinfo support added */ |
30 | #define IPSET_TYPE_REV_MAX 8 /* bucketsize, initval support added */ |
31 | |
32 | MODULE_LICENSE("GPL" ); |
33 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>" ); |
34 | IP_SET_MODULE_DESC("hash:net,port" , IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); |
35 | MODULE_ALIAS("ip_set_hash:net,port" ); |
36 | |
37 | /* Type specific function prefix */ |
38 | #define HTYPE hash_netport |
39 | #define IP_SET_HASH_WITH_PROTO |
40 | #define IP_SET_HASH_WITH_NETS |
41 | |
42 | /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 |
43 | * However this way we have to store internally cidr - 1, |
44 | * dancing back and forth. |
45 | */ |
46 | #define IP_SET_HASH_WITH_NETS_PACKED |
47 | |
48 | /* IPv4 variant */ |
49 | |
50 | /* Member elements */ |
51 | struct hash_netport4_elem { |
52 | __be32 ip; |
53 | __be16 port; |
54 | u8 proto; |
55 | u8 cidr:7; |
56 | u8 nomatch:1; |
57 | }; |
58 | |
59 | /* Common functions */ |
60 | |
61 | static bool |
62 | hash_netport4_data_equal(const struct hash_netport4_elem *ip1, |
63 | const struct hash_netport4_elem *ip2, |
64 | u32 *multi) |
65 | { |
66 | return ip1->ip == ip2->ip && |
67 | ip1->port == ip2->port && |
68 | ip1->proto == ip2->proto && |
69 | ip1->cidr == ip2->cidr; |
70 | } |
71 | |
72 | static int |
73 | hash_netport4_do_data_match(const struct hash_netport4_elem *elem) |
74 | { |
75 | return elem->nomatch ? -ENOTEMPTY : 1; |
76 | } |
77 | |
78 | static void |
79 | hash_netport4_data_set_flags(struct hash_netport4_elem *elem, u32 flags) |
80 | { |
81 | elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); |
82 | } |
83 | |
84 | static void |
85 | hash_netport4_data_reset_flags(struct hash_netport4_elem *elem, u8 *flags) |
86 | { |
87 | swap(*flags, elem->nomatch); |
88 | } |
89 | |
90 | static void |
91 | hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr) |
92 | { |
93 | elem->ip &= ip_set_netmask(pfxlen: cidr); |
94 | elem->cidr = cidr - 1; |
95 | } |
96 | |
97 | static bool |
98 | hash_netport4_data_list(struct sk_buff *skb, |
99 | const struct hash_netport4_elem *data) |
100 | { |
101 | u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; |
102 | |
103 | if (nla_put_ipaddr4(skb, type: IPSET_ATTR_IP, ipaddr: data->ip) || |
104 | nla_put_net16(skb, attrtype: IPSET_ATTR_PORT, value: data->port) || |
105 | nla_put_u8(skb, attrtype: IPSET_ATTR_CIDR, value: data->cidr + 1) || |
106 | nla_put_u8(skb, attrtype: IPSET_ATTR_PROTO, value: data->proto) || |
107 | (flags && |
108 | nla_put_net32(skb, attrtype: IPSET_ATTR_CADT_FLAGS, htonl(flags)))) |
109 | goto nla_put_failure; |
110 | return false; |
111 | |
112 | nla_put_failure: |
113 | return true; |
114 | } |
115 | |
116 | static void |
117 | hash_netport4_data_next(struct hash_netport4_elem *next, |
118 | const struct hash_netport4_elem *d) |
119 | { |
120 | next->ip = d->ip; |
121 | next->port = d->port; |
122 | } |
123 | |
124 | #define MTYPE hash_netport4 |
125 | #define HOST_MASK 32 |
126 | #include "ip_set_hash_gen.h" |
127 | |
128 | static int |
129 | hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, |
130 | const struct xt_action_param *par, |
131 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
132 | { |
133 | const struct hash_netport4 *h = set->data; |
134 | ipset_adtfn adtfn = set->variant->adt[adt]; |
135 | struct hash_netport4_elem e = { |
136 | .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), |
137 | }; |
138 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
139 | |
140 | if (adt == IPSET_TEST) |
141 | e.cidr = HOST_MASK - 1; |
142 | |
143 | if (!ip_set_get_ip4_port(skb, src: opt->flags & IPSET_DIM_TWO_SRC, |
144 | port: &e.port, proto: &e.proto)) |
145 | return -EINVAL; |
146 | |
147 | ip4addrptr(skb, src: opt->flags & IPSET_DIM_ONE_SRC, addr: &e.ip); |
148 | e.ip &= ip_set_netmask(pfxlen: e.cidr + 1); |
149 | |
150 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
151 | } |
152 | |
153 | static int |
154 | hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], |
155 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
156 | { |
157 | struct hash_netport4 *h = set->data; |
158 | ipset_adtfn adtfn = set->variant->adt[adt]; |
159 | struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 }; |
160 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
161 | u32 port, port_to, p = 0, ip = 0, ip_to = 0, i = 0; |
162 | bool with_ports = false; |
163 | u8 cidr; |
164 | int ret; |
165 | |
166 | if (tb[IPSET_ATTR_LINENO]) |
167 | *lineno = nla_get_u32(nla: tb[IPSET_ATTR_LINENO]); |
168 | |
169 | if (unlikely(!tb[IPSET_ATTR_IP] || |
170 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || |
171 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || |
172 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) |
173 | return -IPSET_ERR_PROTOCOL; |
174 | |
175 | ret = ip_set_get_hostipaddr4(nla: tb[IPSET_ATTR_IP], ipaddr: &ip); |
176 | if (ret) |
177 | return ret; |
178 | |
179 | ret = ip_set_get_extensions(set, tb, ext: &ext); |
180 | if (ret) |
181 | return ret; |
182 | |
183 | if (tb[IPSET_ATTR_CIDR]) { |
184 | cidr = nla_get_u8(nla: tb[IPSET_ATTR_CIDR]); |
185 | if (!cidr || cidr > HOST_MASK) |
186 | return -IPSET_ERR_INVALID_CIDR; |
187 | e.cidr = cidr - 1; |
188 | } |
189 | |
190 | e.port = nla_get_be16(nla: tb[IPSET_ATTR_PORT]); |
191 | |
192 | if (tb[IPSET_ATTR_PROTO]) { |
193 | e.proto = nla_get_u8(nla: tb[IPSET_ATTR_PROTO]); |
194 | with_ports = ip_set_proto_with_ports(proto: e.proto); |
195 | |
196 | if (e.proto == 0) |
197 | return -IPSET_ERR_INVALID_PROTO; |
198 | } else { |
199 | return -IPSET_ERR_MISSING_PROTO; |
200 | } |
201 | |
202 | if (!(with_ports || e.proto == IPPROTO_ICMP)) |
203 | e.port = 0; |
204 | |
205 | with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; |
206 | |
207 | if (tb[IPSET_ATTR_CADT_FLAGS]) { |
208 | u32 cadt_flags = ip_set_get_h32(attr: tb[IPSET_ATTR_CADT_FLAGS]); |
209 | |
210 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
211 | flags |= (IPSET_FLAG_NOMATCH << 16); |
212 | } |
213 | |
214 | if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) { |
215 | e.ip = htonl(ip & ip_set_hostmask(e.cidr + 1)); |
216 | ret = adtfn(set, &e, &ext, &ext, flags); |
217 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
218 | ip_set_eexist(ret, flags) ? 0 : ret; |
219 | } |
220 | |
221 | port = port_to = ntohs(e.port); |
222 | if (tb[IPSET_ATTR_PORT_TO]) { |
223 | port_to = ip_set_get_h16(attr: tb[IPSET_ATTR_PORT_TO]); |
224 | if (port_to < port) |
225 | swap(port, port_to); |
226 | } |
227 | if (tb[IPSET_ATTR_IP_TO]) { |
228 | ret = ip_set_get_hostipaddr4(nla: tb[IPSET_ATTR_IP_TO], ipaddr: &ip_to); |
229 | if (ret) |
230 | return ret; |
231 | if (ip_to < ip) |
232 | swap(ip, ip_to); |
233 | if (ip + UINT_MAX == ip_to) |
234 | return -IPSET_ERR_HASH_RANGE; |
235 | } else { |
236 | ip_set_mask_from_to(ip, ip_to, e.cidr + 1); |
237 | } |
238 | |
239 | if (retried) { |
240 | ip = ntohl(h->next.ip); |
241 | p = ntohs(h->next.port); |
242 | } else { |
243 | p = port; |
244 | } |
245 | do { |
246 | e.ip = htonl(ip); |
247 | ip = ip_set_range_to_cidr(from: ip, to: ip_to, cidr: &cidr); |
248 | e.cidr = cidr - 1; |
249 | for (; p <= port_to; p++, i++) { |
250 | e.port = htons(p); |
251 | if (i > IPSET_MAX_RANGE) { |
252 | hash_netport4_data_next(next: &h->next, d: &e); |
253 | return -ERANGE; |
254 | } |
255 | ret = adtfn(set, &e, &ext, &ext, flags); |
256 | if (ret && !ip_set_eexist(ret, flags)) |
257 | return ret; |
258 | |
259 | ret = 0; |
260 | } |
261 | p = port; |
262 | } while (ip++ < ip_to); |
263 | return ret; |
264 | } |
265 | |
266 | /* IPv6 variant */ |
267 | |
268 | struct hash_netport6_elem { |
269 | union nf_inet_addr ip; |
270 | __be16 port; |
271 | u8 proto; |
272 | u8 cidr:7; |
273 | u8 nomatch:1; |
274 | }; |
275 | |
276 | /* Common functions */ |
277 | |
278 | static bool |
279 | hash_netport6_data_equal(const struct hash_netport6_elem *ip1, |
280 | const struct hash_netport6_elem *ip2, |
281 | u32 *multi) |
282 | { |
283 | return ipv6_addr_equal(a1: &ip1->ip.in6, a2: &ip2->ip.in6) && |
284 | ip1->port == ip2->port && |
285 | ip1->proto == ip2->proto && |
286 | ip1->cidr == ip2->cidr; |
287 | } |
288 | |
289 | static int |
290 | hash_netport6_do_data_match(const struct hash_netport6_elem *elem) |
291 | { |
292 | return elem->nomatch ? -ENOTEMPTY : 1; |
293 | } |
294 | |
295 | static void |
296 | hash_netport6_data_set_flags(struct hash_netport6_elem *elem, u32 flags) |
297 | { |
298 | elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); |
299 | } |
300 | |
301 | static void |
302 | hash_netport6_data_reset_flags(struct hash_netport6_elem *elem, u8 *flags) |
303 | { |
304 | swap(*flags, elem->nomatch); |
305 | } |
306 | |
307 | static void |
308 | hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr) |
309 | { |
310 | ip6_netmask(ip: &elem->ip, prefix: cidr); |
311 | elem->cidr = cidr - 1; |
312 | } |
313 | |
314 | static bool |
315 | hash_netport6_data_list(struct sk_buff *skb, |
316 | const struct hash_netport6_elem *data) |
317 | { |
318 | u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; |
319 | |
320 | if (nla_put_ipaddr6(skb, type: IPSET_ATTR_IP, ipaddrptr: &data->ip.in6) || |
321 | nla_put_net16(skb, attrtype: IPSET_ATTR_PORT, value: data->port) || |
322 | nla_put_u8(skb, attrtype: IPSET_ATTR_CIDR, value: data->cidr + 1) || |
323 | nla_put_u8(skb, attrtype: IPSET_ATTR_PROTO, value: data->proto) || |
324 | (flags && |
325 | nla_put_net32(skb, attrtype: IPSET_ATTR_CADT_FLAGS, htonl(flags)))) |
326 | goto nla_put_failure; |
327 | return false; |
328 | |
329 | nla_put_failure: |
330 | return true; |
331 | } |
332 | |
333 | static void |
334 | hash_netport6_data_next(struct hash_netport6_elem *next, |
335 | const struct hash_netport6_elem *d) |
336 | { |
337 | next->port = d->port; |
338 | } |
339 | |
340 | #undef MTYPE |
341 | #undef HOST_MASK |
342 | |
343 | #define MTYPE hash_netport6 |
344 | #define HOST_MASK 128 |
345 | #define IP_SET_EMIT_CREATE |
346 | #include "ip_set_hash_gen.h" |
347 | |
348 | static int |
349 | hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, |
350 | const struct xt_action_param *par, |
351 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
352 | { |
353 | const struct hash_netport6 *h = set->data; |
354 | ipset_adtfn adtfn = set->variant->adt[adt]; |
355 | struct hash_netport6_elem e = { |
356 | .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), |
357 | }; |
358 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
359 | |
360 | if (adt == IPSET_TEST) |
361 | e.cidr = HOST_MASK - 1; |
362 | |
363 | if (!ip_set_get_ip6_port(skb, src: opt->flags & IPSET_DIM_TWO_SRC, |
364 | port: &e.port, proto: &e.proto)) |
365 | return -EINVAL; |
366 | |
367 | ip6addrptr(skb, src: opt->flags & IPSET_DIM_ONE_SRC, addr: &e.ip.in6); |
368 | ip6_netmask(ip: &e.ip, prefix: e.cidr + 1); |
369 | |
370 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
371 | } |
372 | |
373 | static int |
374 | hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], |
375 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
376 | { |
377 | const struct hash_netport6 *h = set->data; |
378 | ipset_adtfn adtfn = set->variant->adt[adt]; |
379 | struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 }; |
380 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
381 | u32 port, port_to; |
382 | bool with_ports = false; |
383 | u8 cidr; |
384 | int ret; |
385 | |
386 | if (tb[IPSET_ATTR_LINENO]) |
387 | *lineno = nla_get_u32(nla: tb[IPSET_ATTR_LINENO]); |
388 | |
389 | if (unlikely(!tb[IPSET_ATTR_IP] || |
390 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || |
391 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || |
392 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) |
393 | return -IPSET_ERR_PROTOCOL; |
394 | if (unlikely(tb[IPSET_ATTR_IP_TO])) |
395 | return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; |
396 | |
397 | ret = ip_set_get_ipaddr6(nla: tb[IPSET_ATTR_IP], ipaddr: &e.ip); |
398 | if (ret) |
399 | return ret; |
400 | |
401 | ret = ip_set_get_extensions(set, tb, ext: &ext); |
402 | if (ret) |
403 | return ret; |
404 | |
405 | if (tb[IPSET_ATTR_CIDR]) { |
406 | cidr = nla_get_u8(nla: tb[IPSET_ATTR_CIDR]); |
407 | if (!cidr || cidr > HOST_MASK) |
408 | return -IPSET_ERR_INVALID_CIDR; |
409 | e.cidr = cidr - 1; |
410 | } |
411 | ip6_netmask(ip: &e.ip, prefix: e.cidr + 1); |
412 | |
413 | e.port = nla_get_be16(nla: tb[IPSET_ATTR_PORT]); |
414 | |
415 | if (tb[IPSET_ATTR_PROTO]) { |
416 | e.proto = nla_get_u8(nla: tb[IPSET_ATTR_PROTO]); |
417 | with_ports = ip_set_proto_with_ports(proto: e.proto); |
418 | |
419 | if (e.proto == 0) |
420 | return -IPSET_ERR_INVALID_PROTO; |
421 | } else { |
422 | return -IPSET_ERR_MISSING_PROTO; |
423 | } |
424 | |
425 | if (!(with_ports || e.proto == IPPROTO_ICMPV6)) |
426 | e.port = 0; |
427 | |
428 | if (tb[IPSET_ATTR_CADT_FLAGS]) { |
429 | u32 cadt_flags = ip_set_get_h32(attr: tb[IPSET_ATTR_CADT_FLAGS]); |
430 | |
431 | if (cadt_flags & IPSET_FLAG_NOMATCH) |
432 | flags |= (IPSET_FLAG_NOMATCH << 16); |
433 | } |
434 | |
435 | if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { |
436 | ret = adtfn(set, &e, &ext, &ext, flags); |
437 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
438 | ip_set_eexist(ret, flags) ? 0 : ret; |
439 | } |
440 | |
441 | port = ntohs(e.port); |
442 | port_to = ip_set_get_h16(attr: tb[IPSET_ATTR_PORT_TO]); |
443 | if (port > port_to) |
444 | swap(port, port_to); |
445 | |
446 | if (retried) |
447 | port = ntohs(h->next.port); |
448 | for (; port <= port_to; port++) { |
449 | e.port = htons(port); |
450 | ret = adtfn(set, &e, &ext, &ext, flags); |
451 | |
452 | if (ret && !ip_set_eexist(ret, flags)) |
453 | return ret; |
454 | |
455 | ret = 0; |
456 | } |
457 | return ret; |
458 | } |
459 | |
460 | static struct ip_set_type hash_netport_type __read_mostly = { |
461 | .name = "hash:net,port" , |
462 | .protocol = IPSET_PROTOCOL, |
463 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH, |
464 | .dimension = IPSET_DIM_TWO, |
465 | .family = NFPROTO_UNSPEC, |
466 | .revision_min = IPSET_TYPE_REV_MIN, |
467 | .revision_max = IPSET_TYPE_REV_MAX, |
468 | .create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE, |
469 | .create = hash_netport_create, |
470 | .create_policy = { |
471 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, |
472 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, |
473 | [IPSET_ATTR_INITVAL] = { .type = NLA_U32 }, |
474 | [IPSET_ATTR_BUCKETSIZE] = { .type = NLA_U8 }, |
475 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, |
476 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, |
477 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, |
478 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
479 | }, |
480 | .adt_policy = { |
481 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, |
482 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, |
483 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, |
484 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, |
485 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, |
486 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, |
487 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, |
488 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, |
489 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
490 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
491 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, |
492 | [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, |
493 | .len = IPSET_MAX_COMMENT_SIZE }, |
494 | [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, |
495 | [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, |
496 | [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, |
497 | }, |
498 | .me = THIS_MODULE, |
499 | }; |
500 | |
501 | static int __init |
502 | hash_netport_init(void) |
503 | { |
504 | return ip_set_type_register(set_type: &hash_netport_type); |
505 | } |
506 | |
507 | static void __exit |
508 | hash_netport_fini(void) |
509 | { |
510 | rcu_barrier(); |
511 | ip_set_type_unregister(set_type: &hash_netport_type); |
512 | } |
513 | |
514 | module_init(hash_netport_init); |
515 | module_exit(hash_netport_fini); |
516 | |