]>
Commit | Line | Data |
---|---|---|
5d50e1d8 | 1 | /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
41d22f7b JK |
2 | * |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 as | |
5 | * published by the Free Software Foundation. | |
6 | */ | |
7 | ||
8 | /* Kernel module implementing an IP set type: the hash:ip,port,net type */ | |
9 | ||
10 | #include <linux/jhash.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/ip.h> | |
13 | #include <linux/skbuff.h> | |
14 | #include <linux/errno.h> | |
41d22f7b JK |
15 | #include <linux/random.h> |
16 | #include <net/ip.h> | |
17 | #include <net/ipv6.h> | |
18 | #include <net/netlink.h> | |
19 | #include <net/tcp.h> | |
20 | ||
21 | #include <linux/netfilter.h> | |
22 | #include <linux/netfilter/ipset/pfxlen.h> | |
23 | #include <linux/netfilter/ipset/ip_set.h> | |
41d22f7b JK |
24 | #include <linux/netfilter/ipset/ip_set_getport.h> |
25 | #include <linux/netfilter/ipset/ip_set_hash.h> | |
26 | ||
35b8dcf8 JK |
27 | #define IPSET_TYPE_REV_MIN 0 |
28 | /* 1 SCTP and UDPLITE support added */ | |
29 | /* 2 Range as input support for IPv4 added */ | |
30 | /* 3 nomatch flag support added */ | |
31 | #define IPSET_TYPE_REV_MAX 4 /* Counters support added */ | |
10111a6e | 32 | |
41d22f7b JK |
33 | MODULE_LICENSE("GPL"); |
34 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
35b8dcf8 | 35 | IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); |
41d22f7b JK |
36 | MODULE_ALIAS("ip_set_hash:ip,port,net"); |
37 | ||
38 | /* Type specific function prefix */ | |
5d50e1d8 | 39 | #define HTYPE hash_ipportnet |
41d22f7b | 40 | |
2a7cef2a JK |
41 | /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 |
42 | * However this way we have to store internally cidr - 1, | |
43 | * dancing back and forth. | |
44 | */ | |
45 | #define IP_SET_HASH_WITH_NETS_PACKED | |
5d50e1d8 JK |
46 | #define IP_SET_HASH_WITH_PROTO |
47 | #define IP_SET_HASH_WITH_NETS | |
2a7cef2a | 48 | |
5d50e1d8 JK |
49 | /* IPv4 variants */ |
50 | ||
51 | /* Member elements */ | |
41d22f7b JK |
52 | struct hash_ipportnet4_elem { |
53 | __be32 ip; | |
54 | __be32 ip2; | |
55 | __be16 port; | |
2a7cef2a JK |
56 | u8 cidr:7; |
57 | u8 nomatch:1; | |
41d22f7b JK |
58 | u8 proto; |
59 | }; | |
60 | ||
5d50e1d8 | 61 | struct hash_ipportnet4t_elem { |
41d22f7b JK |
62 | __be32 ip; |
63 | __be32 ip2; | |
64 | __be16 port; | |
2a7cef2a JK |
65 | u8 cidr:7; |
66 | u8 nomatch:1; | |
41d22f7b JK |
67 | u8 proto; |
68 | unsigned long timeout; | |
69 | }; | |
70 | ||
00d71b27 JK |
71 | struct hash_ipportnet4c_elem { |
72 | __be32 ip; | |
73 | __be32 ip2; | |
74 | __be16 port; | |
75 | u8 cidr:7; | |
76 | u8 nomatch:1; | |
77 | u8 proto; | |
78 | struct ip_set_counter counter; | |
79 | }; | |
80 | ||
81 | struct hash_ipportnet4ct_elem { | |
82 | __be32 ip; | |
83 | __be32 ip2; | |
84 | __be16 port; | |
85 | u8 cidr:7; | |
86 | u8 nomatch:1; | |
87 | u8 proto; | |
88 | struct ip_set_counter counter; | |
89 | unsigned long timeout; | |
90 | }; | |
91 | ||
5d50e1d8 JK |
92 | /* Common functions */ |
93 | ||
41d22f7b JK |
94 | static inline bool |
95 | hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, | |
89dc79b7 JK |
96 | const struct hash_ipportnet4_elem *ip2, |
97 | u32 *multi) | |
41d22f7b JK |
98 | { |
99 | return ip1->ip == ip2->ip && | |
100 | ip1->ip2 == ip2->ip2 && | |
101 | ip1->cidr == ip2->cidr && | |
102 | ip1->port == ip2->port && | |
103 | ip1->proto == ip2->proto; | |
104 | } | |
105 | ||
5d50e1d8 JK |
106 | static inline int |
107 | hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem) | |
41d22f7b | 108 | { |
5d50e1d8 | 109 | return elem->nomatch ? -ENOTEMPTY : 1; |
41d22f7b JK |
110 | } |
111 | ||
112 | static inline void | |
5d50e1d8 | 113 | hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags) |
41d22f7b | 114 | { |
5d50e1d8 | 115 | elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); |
41d22f7b JK |
116 | } |
117 | ||
2a7cef2a | 118 | static inline void |
5d50e1d8 | 119 | hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags) |
2a7cef2a | 120 | { |
5d50e1d8 | 121 | swap(*flags, elem->nomatch); |
2a7cef2a JK |
122 | } |
123 | ||
41d22f7b JK |
124 | static inline void |
125 | hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) | |
126 | { | |
127 | elem->ip2 &= ip_set_netmask(cidr); | |
2a7cef2a | 128 | elem->cidr = cidr - 1; |
41d22f7b JK |
129 | } |
130 | ||
41d22f7b JK |
131 | static bool |
132 | hash_ipportnet4_data_list(struct sk_buff *skb, | |
133 | const struct hash_ipportnet4_elem *data) | |
134 | { | |
2a7cef2a JK |
135 | u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; |
136 | ||
7cf7899d DM |
137 | if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || |
138 | nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || | |
139 | nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || | |
140 | nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || | |
141 | nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || | |
142 | (flags && | |
143 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
144 | goto nla_put_failure; | |
41d22f7b JK |
145 | return 0; |
146 | ||
147 | nla_put_failure: | |
148 | return 1; | |
149 | } | |
150 | ||
5d50e1d8 JK |
151 | static inline void |
152 | hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next, | |
153 | const struct hash_ipportnet4_elem *d) | |
41d22f7b | 154 | { |
5d50e1d8 JK |
155 | next->ip = d->ip; |
156 | next->port = d->port; | |
157 | next->ip2 = d->ip2; | |
41d22f7b JK |
158 | } |
159 | ||
5d50e1d8 | 160 | #define MTYPE hash_ipportnet4 |
41d22f7b JK |
161 | #define PF 4 |
162 | #define HOST_MASK 32 | |
5d50e1d8 | 163 | #include "ip_set_hash_gen.h" |
3d14b171 | 164 | |
41d22f7b JK |
165 | static int |
166 | hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
b66554cf | 167 | const struct xt_action_param *par, |
5d50e1d8 | 168 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
41d22f7b | 169 | { |
5d50e1d8 | 170 | const struct hash_ipportnet *h = set->data; |
41d22f7b | 171 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 172 | struct hash_ipportnet4_elem e = { |
a04d8b6b | 173 | .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, |
9b03a5ef | 174 | }; |
5d50e1d8 | 175 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); |
41d22f7b | 176 | |
41d22f7b | 177 | if (adt == IPSET_TEST) |
5d50e1d8 | 178 | e.cidr = HOST_MASK - 1; |
41d22f7b | 179 | |
ac8cc925 | 180 | if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, |
5d50e1d8 | 181 | &e.port, &e.proto)) |
41d22f7b JK |
182 | return -EINVAL; |
183 | ||
5d50e1d8 JK |
184 | ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); |
185 | ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2); | |
186 | e.ip2 &= ip_set_netmask(e.cidr + 1); | |
41d22f7b | 187 | |
5d50e1d8 | 188 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
41d22f7b JK |
189 | } |
190 | ||
191 | static int | |
192 | hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], | |
3d14b171 | 193 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
41d22f7b | 194 | { |
5d50e1d8 | 195 | const struct hash_ipportnet *h = set->data; |
41d22f7b | 196 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 JK |
197 | struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; |
198 | struct ip_set_ext ext = IP_SET_INIT_UEXT(h); | |
20b2fab4 JK |
199 | u32 ip = 0, ip_to = 0, p = 0, port, port_to; |
200 | u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; | |
5e0c1eb7 | 201 | bool with_ports = false; |
2a7cef2a | 202 | u8 cidr; |
41d22f7b JK |
203 | int ret; |
204 | ||
205 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
206 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
207 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
2a7cef2a | 208 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || |
00d71b27 JK |
209 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || |
210 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
211 | !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
41d22f7b JK |
212 | return -IPSET_ERR_PROTOCOL; |
213 | ||
214 | if (tb[IPSET_ATTR_LINENO]) | |
215 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
216 | ||
5d50e1d8 JK |
217 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || |
218 | ip_set_get_extensions(set, tb, &ext); | |
41d22f7b JK |
219 | if (ret) |
220 | return ret; | |
221 | ||
d0d9e0a5 | 222 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from); |
41d22f7b JK |
223 | if (ret) |
224 | return ret; | |
225 | ||
d0d9e0a5 | 226 | if (tb[IPSET_ATTR_CIDR2]) { |
2a7cef2a JK |
227 | cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); |
228 | if (!cidr || cidr > HOST_MASK) | |
d0d9e0a5 | 229 | return -IPSET_ERR_INVALID_CIDR; |
5d50e1d8 | 230 | e.cidr = cidr - 1; |
d0d9e0a5 | 231 | } |
41d22f7b JK |
232 | |
233 | if (tb[IPSET_ATTR_PORT]) | |
5d50e1d8 | 234 | e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); |
41d22f7b JK |
235 | else |
236 | return -IPSET_ERR_PROTOCOL; | |
237 | ||
238 | if (tb[IPSET_ATTR_PROTO]) { | |
5d50e1d8 JK |
239 | e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); |
240 | with_ports = ip_set_proto_with_ports(e.proto); | |
41d22f7b | 241 | |
5d50e1d8 | 242 | if (e.proto == 0) |
41d22f7b JK |
243 | return -IPSET_ERR_INVALID_PROTO; |
244 | } else | |
245 | return -IPSET_ERR_MISSING_PROTO; | |
246 | ||
5d50e1d8 JK |
247 | if (!(with_ports || e.proto == IPPROTO_ICMP)) |
248 | e.port = 0; | |
41d22f7b | 249 | |
43c56e59 | 250 | if (tb[IPSET_ATTR_CADT_FLAGS]) { |
2a7cef2a JK |
251 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
252 | if (cadt_flags & IPSET_FLAG_NOMATCH) | |
43c56e59 | 253 | flags |= (IPSET_FLAG_NOMATCH << 16); |
2a7cef2a JK |
254 | } |
255 | ||
d0d9e0a5 | 256 | with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; |
41d22f7b | 257 | if (adt == IPSET_TEST || |
d0d9e0a5 JK |
258 | !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports || |
259 | tb[IPSET_ATTR_IP2_TO])) { | |
5d50e1d8 JK |
260 | e.ip = htonl(ip); |
261 | e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1)); | |
262 | ret = adtfn(set, &e, &ext, &ext, flags); | |
0f1799ba | 263 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 264 | ip_set_eexist(ret, flags) ? 0 : ret; |
41d22f7b JK |
265 | } |
266 | ||
4fe198e6 | 267 | ip_to = ip; |
41d22f7b JK |
268 | if (tb[IPSET_ATTR_IP_TO]) { |
269 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
270 | if (ret) | |
271 | return ret; | |
272 | if (ip > ip_to) | |
273 | swap(ip, ip_to); | |
274 | } else if (tb[IPSET_ATTR_CIDR]) { | |
b3aabd14 | 275 | cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); |
41d22f7b | 276 | |
b9fed748 | 277 | if (!cidr || cidr > 32) |
41d22f7b | 278 | return -IPSET_ERR_INVALID_CIDR; |
e6146e86 | 279 | ip_set_mask_from_to(ip, ip_to, cidr); |
d0d9e0a5 | 280 | } |
41d22f7b | 281 | |
5d50e1d8 | 282 | port_to = port = ntohs(e.port); |
d0d9e0a5 | 283 | if (tb[IPSET_ATTR_PORT_TO]) { |
41d22f7b JK |
284 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); |
285 | if (port > port_to) | |
286 | swap(port, port_to); | |
5e0c1eb7 | 287 | } |
4fe198e6 JK |
288 | |
289 | ip2_to = ip2_from; | |
d0d9e0a5 JK |
290 | if (tb[IPSET_ATTR_IP2_TO]) { |
291 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); | |
292 | if (ret) | |
293 | return ret; | |
294 | if (ip2_from > ip2_to) | |
295 | swap(ip2_from, ip2_to); | |
296 | if (ip2_from + UINT_MAX == ip2_to) | |
297 | return -IPSET_ERR_HASH_RANGE; | |
5d50e1d8 JK |
298 | } else |
299 | ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1); | |
41d22f7b | 300 | |
3d14b171 | 301 | if (retried) |
6e27c9b4 | 302 | ip = ntohl(h->next.ip); |
3d14b171 | 303 | for (; !before(ip_to, ip); ip++) { |
5d50e1d8 | 304 | e.ip = htonl(ip); |
6e27c9b4 JK |
305 | p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) |
306 | : port; | |
3d14b171 | 307 | for (; p <= port_to; p++) { |
5d50e1d8 | 308 | e.port = htons(p); |
6e27c9b4 JK |
309 | ip2 = retried |
310 | && ip == ntohl(h->next.ip) | |
311 | && p == ntohs(h->next.port) | |
312 | ? ntohl(h->next.ip2) : ip2_from; | |
d0d9e0a5 | 313 | while (!after(ip2, ip2_to)) { |
5d50e1d8 | 314 | e.ip2 = htonl(ip2); |
d0d9e0a5 | 315 | ip2_last = ip_set_range_to_cidr(ip2, ip2_to, |
2a7cef2a | 316 | &cidr); |
5d50e1d8 JK |
317 | e.cidr = cidr - 1; |
318 | ret = adtfn(set, &e, &ext, &ext, flags); | |
d0d9e0a5 JK |
319 | |
320 | if (ret && !ip_set_eexist(ret, flags)) | |
321 | return ret; | |
322 | else | |
323 | ret = 0; | |
324 | ip2 = ip2_last + 1; | |
325 | } | |
41d22f7b | 326 | } |
3d14b171 | 327 | } |
41d22f7b JK |
328 | return ret; |
329 | } | |
330 | ||
5d50e1d8 | 331 | /* IPv6 variants */ |
41d22f7b JK |
332 | |
333 | struct hash_ipportnet6_elem { | |
334 | union nf_inet_addr ip; | |
335 | union nf_inet_addr ip2; | |
336 | __be16 port; | |
2a7cef2a JK |
337 | u8 cidr:7; |
338 | u8 nomatch:1; | |
41d22f7b JK |
339 | u8 proto; |
340 | }; | |
341 | ||
5d50e1d8 | 342 | struct hash_ipportnet6t_elem { |
41d22f7b JK |
343 | union nf_inet_addr ip; |
344 | union nf_inet_addr ip2; | |
345 | __be16 port; | |
2a7cef2a JK |
346 | u8 cidr:7; |
347 | u8 nomatch:1; | |
41d22f7b JK |
348 | u8 proto; |
349 | unsigned long timeout; | |
350 | }; | |
351 | ||
00d71b27 JK |
352 | struct hash_ipportnet6c_elem { |
353 | union nf_inet_addr ip; | |
354 | union nf_inet_addr ip2; | |
355 | __be16 port; | |
356 | u8 cidr:7; | |
357 | u8 nomatch:1; | |
358 | u8 proto; | |
359 | struct ip_set_counter counter; | |
360 | }; | |
361 | ||
362 | struct hash_ipportnet6ct_elem { | |
363 | union nf_inet_addr ip; | |
364 | union nf_inet_addr ip2; | |
365 | __be16 port; | |
366 | u8 cidr:7; | |
367 | u8 nomatch:1; | |
368 | u8 proto; | |
369 | struct ip_set_counter counter; | |
370 | unsigned long timeout; | |
371 | }; | |
372 | ||
5d50e1d8 JK |
373 | /* Common functions */ |
374 | ||
41d22f7b JK |
375 | static inline bool |
376 | hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, | |
89dc79b7 JK |
377 | const struct hash_ipportnet6_elem *ip2, |
378 | u32 *multi) | |
41d22f7b | 379 | { |
29e3b160 YH |
380 | return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && |
381 | ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && | |
41d22f7b JK |
382 | ip1->cidr == ip2->cidr && |
383 | ip1->port == ip2->port && | |
384 | ip1->proto == ip2->proto; | |
385 | } | |
386 | ||
5d50e1d8 JK |
387 | static inline int |
388 | hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem) | |
2a7cef2a | 389 | { |
5d50e1d8 | 390 | return elem->nomatch ? -ENOTEMPTY : 1; |
2a7cef2a JK |
391 | } |
392 | ||
6eb4c7e9 | 393 | static inline void |
5d50e1d8 | 394 | hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags) |
2a7cef2a | 395 | { |
5d50e1d8 | 396 | elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); |
2a7cef2a JK |
397 | } |
398 | ||
41d22f7b | 399 | static inline void |
5d50e1d8 | 400 | hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags) |
41d22f7b | 401 | { |
5d50e1d8 | 402 | swap(*flags, elem->nomatch); |
41d22f7b JK |
403 | } |
404 | ||
41d22f7b JK |
405 | static inline void |
406 | hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) | |
407 | { | |
408 | ip6_netmask(&elem->ip2, cidr); | |
2a7cef2a | 409 | elem->cidr = cidr - 1; |
41d22f7b JK |
410 | } |
411 | ||
412 | static bool | |
413 | hash_ipportnet6_data_list(struct sk_buff *skb, | |
414 | const struct hash_ipportnet6_elem *data) | |
415 | { | |
2a7cef2a JK |
416 | u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; |
417 | ||
7cf7899d DM |
418 | if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || |
419 | nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || | |
420 | nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || | |
421 | nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || | |
422 | nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || | |
423 | (flags && | |
424 | nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
425 | goto nla_put_failure; | |
41d22f7b JK |
426 | return 0; |
427 | ||
428 | nla_put_failure: | |
429 | return 1; | |
430 | } | |
431 | ||
5d50e1d8 JK |
432 | static inline void |
433 | hash_ipportnet6_data_next(struct hash_ipportnet4_elem *next, | |
434 | const struct hash_ipportnet6_elem *d) | |
41d22f7b | 435 | { |
5d50e1d8 | 436 | next->port = d->port; |
41d22f7b JK |
437 | } |
438 | ||
5d50e1d8 | 439 | #undef MTYPE |
41d22f7b JK |
440 | #undef PF |
441 | #undef HOST_MASK | |
442 | ||
5d50e1d8 | 443 | #define MTYPE hash_ipportnet6 |
41d22f7b JK |
444 | #define PF 6 |
445 | #define HOST_MASK 128 | |
5d50e1d8 JK |
446 | #define IP_SET_EMIT_CREATE |
447 | #include "ip_set_hash_gen.h" | |
3d14b171 | 448 | |
41d22f7b JK |
449 | static int |
450 | hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, | |
b66554cf | 451 | const struct xt_action_param *par, |
5d50e1d8 | 452 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
41d22f7b | 453 | { |
5d50e1d8 | 454 | const struct hash_ipportnet *h = set->data; |
41d22f7b | 455 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 | 456 | struct hash_ipportnet6_elem e = { |
a04d8b6b | 457 | .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, |
9b03a5ef | 458 | }; |
5d50e1d8 | 459 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); |
41d22f7b | 460 | |
41d22f7b | 461 | if (adt == IPSET_TEST) |
5d50e1d8 | 462 | e.cidr = HOST_MASK - 1; |
41d22f7b | 463 | |
ac8cc925 | 464 | if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, |
5d50e1d8 | 465 | &e.port, &e.proto)) |
41d22f7b JK |
466 | return -EINVAL; |
467 | ||
5d50e1d8 JK |
468 | ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); |
469 | ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6); | |
470 | ip6_netmask(&e.ip2, e.cidr + 1); | |
41d22f7b | 471 | |
5d50e1d8 | 472 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); |
41d22f7b JK |
473 | } |
474 | ||
475 | static int | |
476 | hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], | |
3d14b171 | 477 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
41d22f7b | 478 | { |
5d50e1d8 | 479 | const struct hash_ipportnet *h = set->data; |
41d22f7b | 480 | ipset_adtfn adtfn = set->variant->adt[adt]; |
5d50e1d8 JK |
481 | struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; |
482 | struct ip_set_ext ext = IP_SET_INIT_UEXT(h); | |
41d22f7b | 483 | u32 port, port_to; |
5e0c1eb7 | 484 | bool with_ports = false; |
2a7cef2a | 485 | u8 cidr; |
41d22f7b JK |
486 | int ret; |
487 | ||
488 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
489 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
490 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
491 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
2a7cef2a | 492 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || |
00d71b27 JK |
493 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || |
494 | !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) || | |
41d22f7b JK |
495 | tb[IPSET_ATTR_IP_TO] || |
496 | tb[IPSET_ATTR_CIDR])) | |
497 | return -IPSET_ERR_PROTOCOL; | |
d0d9e0a5 JK |
498 | if (unlikely(tb[IPSET_ATTR_IP_TO])) |
499 | return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; | |
41d22f7b JK |
500 | |
501 | if (tb[IPSET_ATTR_LINENO]) | |
502 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
503 | ||
5d50e1d8 JK |
504 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) || |
505 | ip_set_get_extensions(set, tb, &ext); | |
41d22f7b JK |
506 | if (ret) |
507 | return ret; | |
508 | ||
5d50e1d8 | 509 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2); |
41d22f7b JK |
510 | if (ret) |
511 | return ret; | |
512 | ||
2a7cef2a JK |
513 | if (tb[IPSET_ATTR_CIDR2]) { |
514 | cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | |
515 | if (!cidr || cidr > HOST_MASK) | |
516 | return -IPSET_ERR_INVALID_CIDR; | |
5d50e1d8 | 517 | e.cidr = cidr - 1; |
2a7cef2a | 518 | } |
41d22f7b | 519 | |
5d50e1d8 | 520 | ip6_netmask(&e.ip2, e.cidr + 1); |
41d22f7b JK |
521 | |
522 | if (tb[IPSET_ATTR_PORT]) | |
5d50e1d8 | 523 | e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); |
41d22f7b JK |
524 | else |
525 | return -IPSET_ERR_PROTOCOL; | |
526 | ||
527 | if (tb[IPSET_ATTR_PROTO]) { | |
5d50e1d8 JK |
528 | e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); |
529 | with_ports = ip_set_proto_with_ports(e.proto); | |
41d22f7b | 530 | |
5d50e1d8 | 531 | if (e.proto == 0) |
41d22f7b JK |
532 | return -IPSET_ERR_INVALID_PROTO; |
533 | } else | |
534 | return -IPSET_ERR_MISSING_PROTO; | |
535 | ||
5d50e1d8 JK |
536 | if (!(with_ports || e.proto == IPPROTO_ICMPV6)) |
537 | e.port = 0; | |
41d22f7b | 538 | |
43c56e59 | 539 | if (tb[IPSET_ATTR_CADT_FLAGS]) { |
2a7cef2a JK |
540 | u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
541 | if (cadt_flags & IPSET_FLAG_NOMATCH) | |
43c56e59 | 542 | flags |= (IPSET_FLAG_NOMATCH << 16); |
2a7cef2a JK |
543 | } |
544 | ||
5e0c1eb7 | 545 | if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { |
5d50e1d8 | 546 | ret = adtfn(set, &e, &ext, &ext, flags); |
0f1799ba | 547 | return ip_set_enomatch(ret, flags, adt, set) ? -ret : |
43c56e59 | 548 | ip_set_eexist(ret, flags) ? 0 : ret; |
41d22f7b JK |
549 | } |
550 | ||
5d50e1d8 | 551 | port = ntohs(e.port); |
41d22f7b JK |
552 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); |
553 | if (port > port_to) | |
554 | swap(port, port_to); | |
555 | ||
3d14b171 | 556 | if (retried) |
6e27c9b4 | 557 | port = ntohs(h->next.port); |
41d22f7b | 558 | for (; port <= port_to; port++) { |
5d50e1d8 JK |
559 | e.port = htons(port); |
560 | ret = adtfn(set, &e, &ext, &ext, flags); | |
41d22f7b JK |
561 | |
562 | if (ret && !ip_set_eexist(ret, flags)) | |
563 | return ret; | |
564 | else | |
565 | ret = 0; | |
566 | } | |
567 | return ret; | |
568 | } | |
569 | ||
41d22f7b JK |
570 | static struct ip_set_type hash_ipportnet_type __read_mostly = { |
571 | .name = "hash:ip,port,net", | |
572 | .protocol = IPSET_PROTOCOL, | |
3e0304a5 JK |
573 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | |
574 | IPSET_TYPE_NOMATCH, | |
41d22f7b | 575 | .dimension = IPSET_DIM_THREE, |
c15f1c83 | 576 | .family = NFPROTO_UNSPEC, |
35b8dcf8 JK |
577 | .revision_min = IPSET_TYPE_REV_MIN, |
578 | .revision_max = IPSET_TYPE_REV_MAX, | |
41d22f7b JK |
579 | .create = hash_ipportnet_create, |
580 | .create_policy = { | |
581 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
582 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
583 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | |
584 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | |
585 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
5d50e1d8 | 586 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
41d22f7b JK |
587 | }, |
588 | .adt_policy = { | |
589 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
590 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
591 | [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, | |
d0d9e0a5 | 592 | [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, |
41d22f7b JK |
593 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, |
594 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | |
595 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
596 | [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, | |
597 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | |
2a7cef2a | 598 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, |
41d22f7b JK |
599 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, |
600 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
00d71b27 JK |
601 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, |
602 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
41d22f7b JK |
603 | }, |
604 | .me = THIS_MODULE, | |
605 | }; | |
606 | ||
607 | static int __init | |
608 | hash_ipportnet_init(void) | |
609 | { | |
610 | return ip_set_type_register(&hash_ipportnet_type); | |
611 | } | |
612 | ||
613 | static void __exit | |
614 | hash_ipportnet_fini(void) | |
615 | { | |
616 | ip_set_type_unregister(&hash_ipportnet_type); | |
617 | } | |
618 | ||
619 | module_init(hash_ipportnet_init); | |
620 | module_exit(hash_ipportnet_fini); |