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:ip,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 | #include <net/tcp.h> |
16 | |
17 | #include <linux/netfilter.h> |
18 | #include <linux/netfilter/ipset/pfxlen.h> |
19 | #include <linux/netfilter/ipset/ip_set.h> |
20 | #include <linux/netfilter/ipset/ip_set_getport.h> |
21 | #include <linux/netfilter/ipset/ip_set_hash.h> |
22 | |
23 | #define IPSET_TYPE_REV_MIN 0 |
24 | /* 1 SCTP and UDPLITE support added */ |
25 | /* 2 Counters support added */ |
26 | /* 3 Comments support added */ |
27 | /* 4 Forceadd support added */ |
28 | /* 5 skbinfo support added */ |
29 | /* 6 bucketsize, initval support added */ |
30 | #define IPSET_TYPE_REV_MAX 7 /* bitmask support added */ |
31 | |
32 | MODULE_LICENSE("GPL" ); |
33 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>" ); |
34 | IP_SET_MODULE_DESC("hash:ip,port" , IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); |
35 | MODULE_ALIAS("ip_set_hash:ip,port" ); |
36 | |
37 | /* Type specific function prefix */ |
38 | #define HTYPE hash_ipport |
39 | #define IP_SET_HASH_WITH_NETMASK |
40 | #define IP_SET_HASH_WITH_BITMASK |
41 | |
42 | /* IPv4 variant */ |
43 | |
44 | /* Member elements */ |
45 | struct hash_ipport4_elem { |
46 | __be32 ip; |
47 | __be16 port; |
48 | u8 proto; |
49 | u8 padding; |
50 | }; |
51 | |
52 | /* Common functions */ |
53 | |
54 | static bool |
55 | hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, |
56 | const struct hash_ipport4_elem *ip2, |
57 | u32 *multi) |
58 | { |
59 | return ip1->ip == ip2->ip && |
60 | ip1->port == ip2->port && |
61 | ip1->proto == ip2->proto; |
62 | } |
63 | |
64 | static bool |
65 | hash_ipport4_data_list(struct sk_buff *skb, |
66 | const struct hash_ipport4_elem *data) |
67 | { |
68 | if (nla_put_ipaddr4(skb, type: IPSET_ATTR_IP, ipaddr: data->ip) || |
69 | nla_put_net16(skb, attrtype: IPSET_ATTR_PORT, value: data->port) || |
70 | nla_put_u8(skb, attrtype: IPSET_ATTR_PROTO, value: data->proto)) |
71 | goto nla_put_failure; |
72 | return false; |
73 | |
74 | nla_put_failure: |
75 | return true; |
76 | } |
77 | |
78 | static void |
79 | hash_ipport4_data_next(struct hash_ipport4_elem *next, |
80 | const struct hash_ipport4_elem *d) |
81 | { |
82 | next->ip = d->ip; |
83 | next->port = d->port; |
84 | } |
85 | |
86 | #define MTYPE hash_ipport4 |
87 | #define HOST_MASK 32 |
88 | #include "ip_set_hash_gen.h" |
89 | |
90 | static int |
91 | hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, |
92 | const struct xt_action_param *par, |
93 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
94 | { |
95 | ipset_adtfn adtfn = set->variant->adt[adt]; |
96 | struct hash_ipport4_elem e = { .ip = 0 }; |
97 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
98 | const struct MTYPE *h = set->data; |
99 | |
100 | if (!ip_set_get_ip4_port(skb, src: opt->flags & IPSET_DIM_TWO_SRC, |
101 | port: &e.port, proto: &e.proto)) |
102 | return -EINVAL; |
103 | |
104 | ip4addrptr(skb, src: opt->flags & IPSET_DIM_ONE_SRC, addr: &e.ip); |
105 | e.ip &= h->bitmask.ip; |
106 | if (e.ip == 0) |
107 | return -EINVAL; |
108 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
109 | } |
110 | |
111 | static int |
112 | hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], |
113 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
114 | { |
115 | struct hash_ipport4 *h = set->data; |
116 | ipset_adtfn adtfn = set->variant->adt[adt]; |
117 | struct hash_ipport4_elem e = { .ip = 0 }; |
118 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
119 | u32 ip, ip_to = 0, p = 0, port, port_to, i = 0; |
120 | bool with_ports = false; |
121 | int ret; |
122 | |
123 | if (tb[IPSET_ATTR_LINENO]) |
124 | *lineno = nla_get_u32(nla: tb[IPSET_ATTR_LINENO]); |
125 | |
126 | if (unlikely(!tb[IPSET_ATTR_IP] || |
127 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || |
128 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO))) |
129 | return -IPSET_ERR_PROTOCOL; |
130 | |
131 | ret = ip_set_get_ipaddr4(nla: tb[IPSET_ATTR_IP], ipaddr: &e.ip); |
132 | if (ret) |
133 | return ret; |
134 | |
135 | ret = ip_set_get_extensions(set, tb, ext: &ext); |
136 | if (ret) |
137 | return ret; |
138 | |
139 | e.ip &= h->bitmask.ip; |
140 | if (e.ip == 0) |
141 | return -EINVAL; |
142 | |
143 | e.port = nla_get_be16(nla: tb[IPSET_ATTR_PORT]); |
144 | |
145 | if (tb[IPSET_ATTR_PROTO]) { |
146 | e.proto = nla_get_u8(nla: tb[IPSET_ATTR_PROTO]); |
147 | with_ports = ip_set_proto_with_ports(proto: e.proto); |
148 | |
149 | if (e.proto == 0) |
150 | return -IPSET_ERR_INVALID_PROTO; |
151 | } else { |
152 | return -IPSET_ERR_MISSING_PROTO; |
153 | } |
154 | |
155 | if (!(with_ports || e.proto == IPPROTO_ICMP)) |
156 | e.port = 0; |
157 | |
158 | if (adt == IPSET_TEST || |
159 | !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || |
160 | tb[IPSET_ATTR_PORT_TO])) { |
161 | ret = adtfn(set, &e, &ext, &ext, flags); |
162 | return ip_set_eexist(ret, flags) ? 0 : ret; |
163 | } |
164 | |
165 | ip_to = ip = ntohl(e.ip); |
166 | if (tb[IPSET_ATTR_IP_TO]) { |
167 | ret = ip_set_get_hostipaddr4(nla: tb[IPSET_ATTR_IP_TO], ipaddr: &ip_to); |
168 | if (ret) |
169 | return ret; |
170 | if (ip > ip_to) |
171 | swap(ip, ip_to); |
172 | } else if (tb[IPSET_ATTR_CIDR]) { |
173 | u8 cidr = nla_get_u8(nla: tb[IPSET_ATTR_CIDR]); |
174 | |
175 | if (!cidr || cidr > HOST_MASK) |
176 | return -IPSET_ERR_INVALID_CIDR; |
177 | ip_set_mask_from_to(ip, ip_to, cidr); |
178 | } |
179 | |
180 | port_to = port = ntohs(e.port); |
181 | if (with_ports && tb[IPSET_ATTR_PORT_TO]) { |
182 | port_to = ip_set_get_h16(attr: tb[IPSET_ATTR_PORT_TO]); |
183 | if (port > port_to) |
184 | swap(port, port_to); |
185 | } |
186 | |
187 | if (retried) |
188 | ip = ntohl(h->next.ip); |
189 | for (; ip <= ip_to; ip++) { |
190 | p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) |
191 | : port; |
192 | for (; p <= port_to; p++, i++) { |
193 | e.ip = htonl(ip); |
194 | e.port = htons(p); |
195 | if (i > IPSET_MAX_RANGE) { |
196 | hash_ipport4_data_next(next: &h->next, d: &e); |
197 | return -ERANGE; |
198 | } |
199 | ret = adtfn(set, &e, &ext, &ext, flags); |
200 | |
201 | if (ret && !ip_set_eexist(ret, flags)) |
202 | return ret; |
203 | |
204 | ret = 0; |
205 | } |
206 | } |
207 | return ret; |
208 | } |
209 | |
210 | /* IPv6 variant */ |
211 | |
212 | struct hash_ipport6_elem { |
213 | union nf_inet_addr ip; |
214 | __be16 port; |
215 | u8 proto; |
216 | u8 padding; |
217 | }; |
218 | |
219 | /* Common functions */ |
220 | |
221 | static bool |
222 | hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, |
223 | const struct hash_ipport6_elem *ip2, |
224 | u32 *multi) |
225 | { |
226 | return ipv6_addr_equal(a1: &ip1->ip.in6, a2: &ip2->ip.in6) && |
227 | ip1->port == ip2->port && |
228 | ip1->proto == ip2->proto; |
229 | } |
230 | |
231 | static bool |
232 | hash_ipport6_data_list(struct sk_buff *skb, |
233 | const struct hash_ipport6_elem *data) |
234 | { |
235 | if (nla_put_ipaddr6(skb, type: IPSET_ATTR_IP, ipaddrptr: &data->ip.in6) || |
236 | nla_put_net16(skb, attrtype: IPSET_ATTR_PORT, value: data->port) || |
237 | nla_put_u8(skb, attrtype: IPSET_ATTR_PROTO, value: data->proto)) |
238 | goto nla_put_failure; |
239 | return false; |
240 | |
241 | nla_put_failure: |
242 | return true; |
243 | } |
244 | |
245 | static void |
246 | hash_ipport6_data_next(struct hash_ipport6_elem *next, |
247 | const struct hash_ipport6_elem *d) |
248 | { |
249 | next->port = d->port; |
250 | } |
251 | |
252 | #undef MTYPE |
253 | #undef HOST_MASK |
254 | |
255 | #define MTYPE hash_ipport6 |
256 | #define HOST_MASK 128 |
257 | #define IP_SET_EMIT_CREATE |
258 | #include "ip_set_hash_gen.h" |
259 | |
260 | static int |
261 | hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, |
262 | const struct xt_action_param *par, |
263 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
264 | { |
265 | ipset_adtfn adtfn = set->variant->adt[adt]; |
266 | struct hash_ipport6_elem e = { .ip = { .all = { 0 } } }; |
267 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); |
268 | const struct MTYPE *h = set->data; |
269 | |
270 | if (!ip_set_get_ip6_port(skb, src: opt->flags & IPSET_DIM_TWO_SRC, |
271 | port: &e.port, proto: &e.proto)) |
272 | return -EINVAL; |
273 | |
274 | ip6addrptr(skb, src: opt->flags & IPSET_DIM_ONE_SRC, addr: &e.ip.in6); |
275 | nf_inet_addr_mask_inplace(a1: &e.ip, mask: &h->bitmask); |
276 | if (ipv6_addr_any(a: &e.ip.in6)) |
277 | return -EINVAL; |
278 | |
279 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
280 | } |
281 | |
282 | static int |
283 | hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], |
284 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
285 | { |
286 | const struct hash_ipport6 *h = set->data; |
287 | ipset_adtfn adtfn = set->variant->adt[adt]; |
288 | struct hash_ipport6_elem e = { .ip = { .all = { 0 } } }; |
289 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); |
290 | u32 port, port_to; |
291 | bool with_ports = false; |
292 | int ret; |
293 | |
294 | if (tb[IPSET_ATTR_LINENO]) |
295 | *lineno = nla_get_u32(nla: tb[IPSET_ATTR_LINENO]); |
296 | |
297 | if (unlikely(!tb[IPSET_ATTR_IP] || |
298 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || |
299 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO))) |
300 | return -IPSET_ERR_PROTOCOL; |
301 | if (unlikely(tb[IPSET_ATTR_IP_TO])) |
302 | return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; |
303 | if (unlikely(tb[IPSET_ATTR_CIDR])) { |
304 | u8 cidr = nla_get_u8(nla: tb[IPSET_ATTR_CIDR]); |
305 | |
306 | if (cidr != HOST_MASK) |
307 | return -IPSET_ERR_INVALID_CIDR; |
308 | } |
309 | |
310 | ret = ip_set_get_ipaddr6(nla: tb[IPSET_ATTR_IP], ipaddr: &e.ip); |
311 | if (ret) |
312 | return ret; |
313 | |
314 | ret = ip_set_get_extensions(set, tb, ext: &ext); |
315 | if (ret) |
316 | return ret; |
317 | |
318 | nf_inet_addr_mask_inplace(a1: &e.ip, mask: &h->bitmask); |
319 | if (ipv6_addr_any(a: &e.ip.in6)) |
320 | return -EINVAL; |
321 | |
322 | e.port = nla_get_be16(nla: tb[IPSET_ATTR_PORT]); |
323 | |
324 | if (tb[IPSET_ATTR_PROTO]) { |
325 | e.proto = nla_get_u8(nla: tb[IPSET_ATTR_PROTO]); |
326 | with_ports = ip_set_proto_with_ports(proto: e.proto); |
327 | |
328 | if (e.proto == 0) |
329 | return -IPSET_ERR_INVALID_PROTO; |
330 | } else { |
331 | return -IPSET_ERR_MISSING_PROTO; |
332 | } |
333 | |
334 | if (!(with_ports || e.proto == IPPROTO_ICMPV6)) |
335 | e.port = 0; |
336 | |
337 | if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { |
338 | ret = adtfn(set, &e, &ext, &ext, flags); |
339 | return ip_set_eexist(ret, flags) ? 0 : ret; |
340 | } |
341 | |
342 | port = ntohs(e.port); |
343 | port_to = ip_set_get_h16(attr: tb[IPSET_ATTR_PORT_TO]); |
344 | if (port > port_to) |
345 | swap(port, port_to); |
346 | |
347 | if (retried) |
348 | port = ntohs(h->next.port); |
349 | for (; port <= port_to; port++) { |
350 | e.port = htons(port); |
351 | ret = adtfn(set, &e, &ext, &ext, flags); |
352 | |
353 | if (ret && !ip_set_eexist(ret, flags)) |
354 | return ret; |
355 | |
356 | ret = 0; |
357 | } |
358 | return ret; |
359 | } |
360 | |
361 | static struct ip_set_type hash_ipport_type __read_mostly = { |
362 | .name = "hash:ip,port" , |
363 | .protocol = IPSET_PROTOCOL, |
364 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, |
365 | .dimension = IPSET_DIM_TWO, |
366 | .family = NFPROTO_UNSPEC, |
367 | .revision_min = IPSET_TYPE_REV_MIN, |
368 | .revision_max = IPSET_TYPE_REV_MAX, |
369 | .create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE, |
370 | .create = hash_ipport_create, |
371 | .create_policy = { |
372 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, |
373 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, |
374 | [IPSET_ATTR_INITVAL] = { .type = NLA_U32 }, |
375 | [IPSET_ATTR_BUCKETSIZE] = { .type = NLA_U8 }, |
376 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, |
377 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, |
378 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, |
379 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
380 | [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, |
381 | [IPSET_ATTR_BITMASK] = { .type = NLA_NESTED }, |
382 | }, |
383 | .adt_policy = { |
384 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, |
385 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, |
386 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, |
387 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, |
388 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, |
389 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, |
390 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, |
391 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, |
392 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
393 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, |
394 | [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, |
395 | .len = IPSET_MAX_COMMENT_SIZE }, |
396 | [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, |
397 | [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, |
398 | [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, |
399 | }, |
400 | .me = THIS_MODULE, |
401 | }; |
402 | |
403 | static int __init |
404 | hash_ipport_init(void) |
405 | { |
406 | return ip_set_type_register(set_type: &hash_ipport_type); |
407 | } |
408 | |
409 | static void __exit |
410 | hash_ipport_fini(void) |
411 | { |
412 | rcu_barrier(); |
413 | ip_set_type_unregister(set_type: &hash_ipport_type); |
414 | } |
415 | |
416 | module_init(hash_ipport_init); |
417 | module_exit(hash_ipport_fini); |
418 | |