Commit 7c3ad056ef79fd10f5f111c807ccbd9fa9068c7f
Committed by
Jozsef Kadlecsik
1 parent
1785e8f473
netfilter: ipset: Add hash:net,port,net module to kernel.
This adds a new set that provides similar functionality to ip,port,net but permits arbitrary size subnets for both the first and last parameter. Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa> Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Showing 3 changed files with 598 additions and 0 deletions Side-by-side Diff
net/netfilter/ipset/Kconfig
... | ... | @@ -90,6 +90,15 @@ |
90 | 90 | |
91 | 91 | To compile it as a module, choose M here. If unsure, say N. |
92 | 92 | |
93 | +config IP_SET_HASH_NETPORTNET | |
94 | + tristate "hash:net,port,net set support" | |
95 | + depends on IP_SET | |
96 | + help | |
97 | + This option adds the hash:net,port,net set type support, by which | |
98 | + one can store two IPv4/IPv6 subnets, and a protocol/port in a set. | |
99 | + | |
100 | + To compile it as a module, choose M here. If unsure, say N. | |
101 | + | |
93 | 102 | config IP_SET_HASH_NET |
94 | 103 | tristate "hash:net set support" |
95 | 104 | depends on IP_SET |
net/netfilter/ipset/Makefile
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o |
22 | 22 | obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o |
23 | 23 | obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o |
24 | +obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o | |
24 | 25 | |
25 | 26 | # list types |
26 | 27 | obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o |
net/netfilter/ipset/ip_set_hash_netportnet.c
1 | +/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
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> | |
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> | |
24 | +#include <linux/netfilter/ipset/ip_set_getport.h> | |
25 | +#include <linux/netfilter/ipset/ip_set_hash.h> | |
26 | + | |
27 | +#define IPSET_TYPE_REV_MIN 0 | |
28 | +#define IPSET_TYPE_REV_MAX 0 /* Comments support added */ | |
29 | + | |
30 | +MODULE_LICENSE("GPL"); | |
31 | +MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>"); | |
32 | +IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); | |
33 | +MODULE_ALIAS("ip_set_hash:net,port,net"); | |
34 | + | |
35 | +/* Type specific function prefix */ | |
36 | +#define HTYPE hash_netportnet | |
37 | +#define IP_SET_HASH_WITH_PROTO | |
38 | +#define IP_SET_HASH_WITH_NETS | |
39 | +#define IPSET_NET_COUNT 2 | |
40 | + | |
41 | +/* IPv4 variant */ | |
42 | + | |
43 | +/* Member elements */ | |
44 | +struct hash_netportnet4_elem { | |
45 | + union { | |
46 | + __be32 ip[2]; | |
47 | + __be64 ipcmp; | |
48 | + }; | |
49 | + __be16 port; | |
50 | + union { | |
51 | + u8 cidr[2]; | |
52 | + u16 ccmp; | |
53 | + }; | |
54 | + u8 nomatch:1; | |
55 | + u8 proto; | |
56 | +}; | |
57 | + | |
58 | +/* Common functions */ | |
59 | + | |
60 | +static inline bool | |
61 | +hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1, | |
62 | + const struct hash_netportnet4_elem *ip2, | |
63 | + u32 *multi) | |
64 | +{ | |
65 | + return ip1->ipcmp == ip2->ipcmp && | |
66 | + ip1->ccmp == ip2->ccmp && | |
67 | + ip1->port == ip2->port && | |
68 | + ip1->proto == ip2->proto; | |
69 | +} | |
70 | + | |
71 | +static inline int | |
72 | +hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem) | |
73 | +{ | |
74 | + return elem->nomatch ? -ENOTEMPTY : 1; | |
75 | +} | |
76 | + | |
77 | +static inline void | |
78 | +hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags) | |
79 | +{ | |
80 | + elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); | |
81 | +} | |
82 | + | |
83 | +static inline void | |
84 | +hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags) | |
85 | +{ | |
86 | + swap(*flags, elem->nomatch); | |
87 | +} | |
88 | + | |
89 | +static inline void | |
90 | +hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem, | |
91 | + struct hash_netportnet4_elem *orig) | |
92 | +{ | |
93 | + elem->ip[1] = orig->ip[1]; | |
94 | +} | |
95 | + | |
96 | +static inline void | |
97 | +hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem, | |
98 | + u8 cidr, bool inner) | |
99 | +{ | |
100 | + if (inner) { | |
101 | + elem->ip[1] &= ip_set_netmask(cidr); | |
102 | + elem->cidr[1] = cidr; | |
103 | + } else { | |
104 | + elem->ip[0] &= ip_set_netmask(cidr); | |
105 | + elem->cidr[0] = cidr; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +static bool | |
110 | +hash_netportnet4_data_list(struct sk_buff *skb, | |
111 | + const struct hash_netportnet4_elem *data) | |
112 | +{ | |
113 | + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; | |
114 | + | |
115 | + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || | |
116 | + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || | |
117 | + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || | |
118 | + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || | |
119 | + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || | |
120 | + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || | |
121 | + (flags && | |
122 | + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
123 | + goto nla_put_failure; | |
124 | + return 0; | |
125 | + | |
126 | +nla_put_failure: | |
127 | + return 1; | |
128 | +} | |
129 | + | |
130 | +static inline void | |
131 | +hash_netportnet4_data_next(struct hash_netportnet4_elem *next, | |
132 | + const struct hash_netportnet4_elem *d) | |
133 | +{ | |
134 | + next->ipcmp = d->ipcmp; | |
135 | + next->port = d->port; | |
136 | +} | |
137 | + | |
138 | +#define MTYPE hash_netportnet4 | |
139 | +#define PF 4 | |
140 | +#define HOST_MASK 32 | |
141 | +#include "ip_set_hash_gen.h" | |
142 | + | |
143 | +static int | |
144 | +hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
145 | + const struct xt_action_param *par, | |
146 | + enum ipset_adt adt, struct ip_set_adt_opt *opt) | |
147 | +{ | |
148 | + const struct hash_netportnet *h = set->data; | |
149 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
150 | + struct hash_netportnet4_elem e = { | |
151 | + .cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), | |
152 | + .cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK), | |
153 | + }; | |
154 | + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); | |
155 | + | |
156 | + if (adt == IPSET_TEST) | |
157 | + e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; | |
158 | + | |
159 | + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, | |
160 | + &e.port, &e.proto)) | |
161 | + return -EINVAL; | |
162 | + | |
163 | + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); | |
164 | + ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]); | |
165 | + e.ip[0] &= ip_set_netmask(e.cidr[0]); | |
166 | + e.ip[1] &= ip_set_netmask(e.cidr[1]); | |
167 | + | |
168 | + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); | |
169 | +} | |
170 | + | |
171 | +static int | |
172 | +hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], | |
173 | + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
174 | +{ | |
175 | + const struct hash_netportnet *h = set->data; | |
176 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
177 | + struct hash_netportnet4_elem e = { .cidr[0] = HOST_MASK, | |
178 | + .cidr[1] = HOST_MASK }; | |
179 | + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); | |
180 | + u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to; | |
181 | + u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; | |
182 | + bool with_ports = false; | |
183 | + u8 cidr, cidr2; | |
184 | + int ret; | |
185 | + | |
186 | + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
187 | + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
188 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
189 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
190 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || | |
191 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
192 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
193 | + return -IPSET_ERR_PROTOCOL; | |
194 | + | |
195 | + if (tb[IPSET_ATTR_LINENO]) | |
196 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
197 | + | |
198 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || | |
199 | + ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || | |
200 | + ip_set_get_extensions(set, tb, &ext); | |
201 | + if (ret) | |
202 | + return ret; | |
203 | + | |
204 | + if (tb[IPSET_ATTR_CIDR]) { | |
205 | + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
206 | + if (!cidr || cidr > HOST_MASK) | |
207 | + return -IPSET_ERR_INVALID_CIDR; | |
208 | + e.cidr[0] = cidr; | |
209 | + } | |
210 | + | |
211 | + if (tb[IPSET_ATTR_CIDR2]) { | |
212 | + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | |
213 | + if (!cidr || cidr > HOST_MASK) | |
214 | + return -IPSET_ERR_INVALID_CIDR; | |
215 | + e.cidr[1] = cidr; | |
216 | + } | |
217 | + | |
218 | + if (tb[IPSET_ATTR_PORT]) | |
219 | + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | |
220 | + else | |
221 | + return -IPSET_ERR_PROTOCOL; | |
222 | + | |
223 | + if (tb[IPSET_ATTR_PROTO]) { | |
224 | + e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | |
225 | + with_ports = ip_set_proto_with_ports(e.proto); | |
226 | + | |
227 | + if (e.proto == 0) | |
228 | + return -IPSET_ERR_INVALID_PROTO; | |
229 | + } else | |
230 | + return -IPSET_ERR_MISSING_PROTO; | |
231 | + | |
232 | + if (!(with_ports || e.proto == IPPROTO_ICMP)) | |
233 | + e.port = 0; | |
234 | + | |
235 | + if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
236 | + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | |
237 | + if (cadt_flags & IPSET_FLAG_NOMATCH) | |
238 | + flags |= (IPSET_FLAG_NOMATCH << 16); | |
239 | + } | |
240 | + | |
241 | + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; | |
242 | + if (adt == IPSET_TEST || | |
243 | + !(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) { | |
244 | + e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); | |
245 | + e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); | |
246 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
247 | + return ip_set_enomatch(ret, flags, adt, set) ? -ret : | |
248 | + ip_set_eexist(ret, flags) ? 0 : ret; | |
249 | + } | |
250 | + | |
251 | + ip_to = ip; | |
252 | + if (tb[IPSET_ATTR_IP_TO]) { | |
253 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
254 | + if (ret) | |
255 | + return ret; | |
256 | + if (ip > ip_to) | |
257 | + swap(ip, ip_to); | |
258 | + if (unlikely(ip + UINT_MAX == ip_to)) | |
259 | + return -IPSET_ERR_HASH_RANGE; | |
260 | + } | |
261 | + | |
262 | + port_to = port = ntohs(e.port); | |
263 | + if (tb[IPSET_ATTR_PORT_TO]) { | |
264 | + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | |
265 | + if (port > port_to) | |
266 | + swap(port, port_to); | |
267 | + } | |
268 | + | |
269 | + ip2_to = ip2_from; | |
270 | + if (tb[IPSET_ATTR_IP2_TO]) { | |
271 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); | |
272 | + if (ret) | |
273 | + return ret; | |
274 | + if (ip2_from > ip2_to) | |
275 | + swap(ip2_from, ip2_to); | |
276 | + if (unlikely(ip2_from + UINT_MAX == ip2_to)) | |
277 | + return -IPSET_ERR_HASH_RANGE; | |
278 | + } | |
279 | + | |
280 | + if (retried) | |
281 | + ip = ntohl(h->next.ip[0]); | |
282 | + | |
283 | + while (!after(ip, ip_to)) { | |
284 | + e.ip[0] = htonl(ip); | |
285 | + ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr); | |
286 | + e.cidr[0] = cidr; | |
287 | + p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port) | |
288 | + : port; | |
289 | + for (; p <= port_to; p++) { | |
290 | + e.port = htons(p); | |
291 | + ip2 = (retried && ip == ntohl(h->next.ip[0]) && | |
292 | + p == ntohs(h->next.port)) ? ntohl(h->next.ip[1]) | |
293 | + : ip2_from; | |
294 | + while (!after(ip2, ip2_to)) { | |
295 | + e.ip[1] = htonl(ip2); | |
296 | + ip2_last = ip_set_range_to_cidr(ip2, ip2_to, | |
297 | + &cidr2); | |
298 | + e.cidr[1] = cidr2; | |
299 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
300 | + if (ret && !ip_set_eexist(ret, flags)) | |
301 | + return ret; | |
302 | + else | |
303 | + ret = 0; | |
304 | + ip2 = ip2_last + 1; | |
305 | + } | |
306 | + } | |
307 | + ip = ip_last + 1; | |
308 | + } | |
309 | + return ret; | |
310 | +} | |
311 | + | |
312 | +/* IPv6 variant */ | |
313 | + | |
314 | +struct hash_netportnet6_elem { | |
315 | + union nf_inet_addr ip[2]; | |
316 | + __be16 port; | |
317 | + union { | |
318 | + u8 cidr[2]; | |
319 | + u16 ccmp; | |
320 | + }; | |
321 | + u8 nomatch:1; | |
322 | + u8 proto; | |
323 | +}; | |
324 | + | |
325 | +/* Common functions */ | |
326 | + | |
327 | +static inline bool | |
328 | +hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1, | |
329 | + const struct hash_netportnet6_elem *ip2, | |
330 | + u32 *multi) | |
331 | +{ | |
332 | + return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && | |
333 | + ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && | |
334 | + ip1->ccmp == ip2->ccmp && | |
335 | + ip1->port == ip2->port && | |
336 | + ip1->proto == ip2->proto; | |
337 | +} | |
338 | + | |
339 | +static inline int | |
340 | +hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem) | |
341 | +{ | |
342 | + return elem->nomatch ? -ENOTEMPTY : 1; | |
343 | +} | |
344 | + | |
345 | +static inline void | |
346 | +hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags) | |
347 | +{ | |
348 | + elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); | |
349 | +} | |
350 | + | |
351 | +static inline void | |
352 | +hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags) | |
353 | +{ | |
354 | + swap(*flags, elem->nomatch); | |
355 | +} | |
356 | + | |
357 | +static inline void | |
358 | +hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem, | |
359 | + struct hash_netportnet6_elem *orig) | |
360 | +{ | |
361 | + elem->ip[1] = orig->ip[1]; | |
362 | +} | |
363 | + | |
364 | +static inline void | |
365 | +hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem, | |
366 | + u8 cidr, bool inner) | |
367 | +{ | |
368 | + if (inner) { | |
369 | + ip6_netmask(&elem->ip[1], cidr); | |
370 | + elem->cidr[1] = cidr; | |
371 | + } else { | |
372 | + ip6_netmask(&elem->ip[0], cidr); | |
373 | + elem->cidr[0] = cidr; | |
374 | + } | |
375 | +} | |
376 | + | |
377 | +static bool | |
378 | +hash_netportnet6_data_list(struct sk_buff *skb, | |
379 | + const struct hash_netportnet6_elem *data) | |
380 | +{ | |
381 | + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; | |
382 | + | |
383 | + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || | |
384 | + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || | |
385 | + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || | |
386 | + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || | |
387 | + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || | |
388 | + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || | |
389 | + (flags && | |
390 | + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
391 | + goto nla_put_failure; | |
392 | + return 0; | |
393 | + | |
394 | +nla_put_failure: | |
395 | + return 1; | |
396 | +} | |
397 | + | |
398 | +static inline void | |
399 | +hash_netportnet6_data_next(struct hash_netportnet4_elem *next, | |
400 | + const struct hash_netportnet6_elem *d) | |
401 | +{ | |
402 | + next->port = d->port; | |
403 | +} | |
404 | + | |
405 | +#undef MTYPE | |
406 | +#undef PF | |
407 | +#undef HOST_MASK | |
408 | + | |
409 | +#define MTYPE hash_netportnet6 | |
410 | +#define PF 6 | |
411 | +#define HOST_MASK 128 | |
412 | +#define IP_SET_EMIT_CREATE | |
413 | +#include "ip_set_hash_gen.h" | |
414 | + | |
415 | +static int | |
416 | +hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, | |
417 | + const struct xt_action_param *par, | |
418 | + enum ipset_adt adt, struct ip_set_adt_opt *opt) | |
419 | +{ | |
420 | + const struct hash_netportnet *h = set->data; | |
421 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
422 | + struct hash_netportnet6_elem e = { | |
423 | + .cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), | |
424 | + .cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK), | |
425 | + }; | |
426 | + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); | |
427 | + | |
428 | + if (adt == IPSET_TEST) | |
429 | + e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; | |
430 | + | |
431 | + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, | |
432 | + &e.port, &e.proto)) | |
433 | + return -EINVAL; | |
434 | + | |
435 | + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); | |
436 | + ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6); | |
437 | + ip6_netmask(&e.ip[0], e.cidr[0]); | |
438 | + ip6_netmask(&e.ip[1], e.cidr[1]); | |
439 | + | |
440 | + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); | |
441 | +} | |
442 | + | |
443 | +static int | |
444 | +hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], | |
445 | + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
446 | +{ | |
447 | + const struct hash_netportnet *h = set->data; | |
448 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
449 | + struct hash_netportnet6_elem e = { .cidr[0] = HOST_MASK, | |
450 | + .cidr[1] = HOST_MASK }; | |
451 | + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); | |
452 | + u32 port, port_to; | |
453 | + bool with_ports = false; | |
454 | + int ret; | |
455 | + | |
456 | + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
457 | + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | |
458 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | |
459 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
460 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || | |
461 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
462 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
463 | + return -IPSET_ERR_PROTOCOL; | |
464 | + if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) | |
465 | + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; | |
466 | + | |
467 | + if (tb[IPSET_ATTR_LINENO]) | |
468 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
469 | + | |
470 | + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || | |
471 | + ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || | |
472 | + ip_set_get_extensions(set, tb, &ext); | |
473 | + if (ret) | |
474 | + return ret; | |
475 | + | |
476 | + if (tb[IPSET_ATTR_CIDR]) | |
477 | + e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
478 | + | |
479 | + if (tb[IPSET_ATTR_CIDR2]) | |
480 | + e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | |
481 | + | |
482 | + if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || | |
483 | + e.cidr[1] > HOST_MASK)) | |
484 | + return -IPSET_ERR_INVALID_CIDR; | |
485 | + | |
486 | + ip6_netmask(&e.ip[0], e.cidr[0]); | |
487 | + ip6_netmask(&e.ip[1], e.cidr[1]); | |
488 | + | |
489 | + if (tb[IPSET_ATTR_PORT]) | |
490 | + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | |
491 | + else | |
492 | + return -IPSET_ERR_PROTOCOL; | |
493 | + | |
494 | + if (tb[IPSET_ATTR_PROTO]) { | |
495 | + e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | |
496 | + with_ports = ip_set_proto_with_ports(e.proto); | |
497 | + | |
498 | + if (e.proto == 0) | |
499 | + return -IPSET_ERR_INVALID_PROTO; | |
500 | + } else | |
501 | + return -IPSET_ERR_MISSING_PROTO; | |
502 | + | |
503 | + if (!(with_ports || e.proto == IPPROTO_ICMPV6)) | |
504 | + e.port = 0; | |
505 | + | |
506 | + if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
507 | + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | |
508 | + if (cadt_flags & IPSET_FLAG_NOMATCH) | |
509 | + flags |= (IPSET_FLAG_NOMATCH << 16); | |
510 | + } | |
511 | + | |
512 | + if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { | |
513 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
514 | + return ip_set_enomatch(ret, flags, adt, set) ? -ret : | |
515 | + ip_set_eexist(ret, flags) ? 0 : ret; | |
516 | + } | |
517 | + | |
518 | + port = ntohs(e.port); | |
519 | + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | |
520 | + if (port > port_to) | |
521 | + swap(port, port_to); | |
522 | + | |
523 | + if (retried) | |
524 | + port = ntohs(h->next.port); | |
525 | + for (; port <= port_to; port++) { | |
526 | + e.port = htons(port); | |
527 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
528 | + | |
529 | + if (ret && !ip_set_eexist(ret, flags)) | |
530 | + return ret; | |
531 | + else | |
532 | + ret = 0; | |
533 | + } | |
534 | + return ret; | |
535 | +} | |
536 | + | |
537 | +static struct ip_set_type hash_netportnet_type __read_mostly = { | |
538 | + .name = "hash:net,port,net", | |
539 | + .protocol = IPSET_PROTOCOL, | |
540 | + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | | |
541 | + IPSET_TYPE_NOMATCH, | |
542 | + .dimension = IPSET_DIM_THREE, | |
543 | + .family = NFPROTO_UNSPEC, | |
544 | + .revision_min = IPSET_TYPE_REV_MIN, | |
545 | + .revision_max = IPSET_TYPE_REV_MAX, | |
546 | + .create = hash_netportnet_create, | |
547 | + .create_policy = { | |
548 | + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
549 | + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
550 | + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | |
551 | + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | |
552 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
553 | + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
554 | + }, | |
555 | + .adt_policy = { | |
556 | + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
557 | + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
558 | + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, | |
559 | + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, | |
560 | + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | |
561 | + [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | |
562 | + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
563 | + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, | |
564 | + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | |
565 | + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
566 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
567 | + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
568 | + [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, | |
569 | + [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
570 | + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, | |
571 | + }, | |
572 | + .me = THIS_MODULE, | |
573 | +}; | |
574 | + | |
575 | +static int __init | |
576 | +hash_netportnet_init(void) | |
577 | +{ | |
578 | + return ip_set_type_register(&hash_netportnet_type); | |
579 | +} | |
580 | + | |
581 | +static void __exit | |
582 | +hash_netportnet_fini(void) | |
583 | +{ | |
584 | + ip_set_type_unregister(&hash_netportnet_type); | |
585 | +} | |
586 | + | |
587 | +module_init(hash_netportnet_init); | |
588 | +module_exit(hash_netportnet_fini); |