blob: 3a5e198641d6a9d9bb3c1ce6a36c0f6fac800fcf [file] [log] [blame]
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +01001/* Copyright (C) 2003-2011 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:net,port 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>
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +010015#include <linux/random.h>
16#include <net/ip.h>
17#include <net/ipv6.h>
18#include <net/netlink.h>
19
20#include <linux/netfilter.h>
21#include <linux/netfilter/ipset/pfxlen.h>
22#include <linux/netfilter/ipset/ip_set.h>
23#include <linux/netfilter/ipset/ip_set_timeout.h>
24#include <linux/netfilter/ipset/ip_set_getport.h>
25#include <linux/netfilter/ipset/ip_set_hash.h>
26
27MODULE_LICENSE("GPL");
28MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
29MODULE_DESCRIPTION("hash:net,port type of IP sets");
30MODULE_ALIAS("ip_set_hash:net,port");
31
32/* Type specific function prefix */
33#define TYPE hash_netport
34
35static bool
36hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
37
38#define hash_netport4_same_set hash_netport_same_set
39#define hash_netport6_same_set hash_netport_same_set
40
41/* The type variant functions: IPv4 */
42
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +010043/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
44 * However this way we have to store internally cidr - 1,
45 * dancing back and forth.
46 */
47#define IP_SET_HASH_WITH_NETS_PACKED
48
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +010049/* Member elements without timeout */
50struct hash_netport4_elem {
51 __be32 ip;
52 __be16 port;
53 u8 proto;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +010054 u8 cidr:7;
55 u8 nomatch:1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +010056};
57
58/* Member elements with timeout support */
59struct hash_netport4_telem {
60 __be32 ip;
61 __be16 port;
62 u8 proto;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +010063 u8 cidr:7;
64 u8 nomatch:1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +010065 unsigned long timeout;
66};
67
68static inline bool
69hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
Jozsef Kadlecsik89dc79b2011-07-21 12:06:18 +020070 const struct hash_netport4_elem *ip2,
71 u32 *multi)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +010072{
73 return ip1->ip == ip2->ip &&
74 ip1->port == ip2->port &&
75 ip1->proto == ip2->proto &&
76 ip1->cidr == ip2->cidr;
77}
78
79static inline bool
80hash_netport4_data_isnull(const struct hash_netport4_elem *elem)
81{
82 return elem->proto == 0;
83}
84
85static inline void
86hash_netport4_data_copy(struct hash_netport4_elem *dst,
87 const struct hash_netport4_elem *src)
88{
89 dst->ip = src->ip;
90 dst->port = src->port;
91 dst->proto = src->proto;
92 dst->cidr = src->cidr;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +010093 dst->nomatch = src->nomatch;
94}
95
96static inline void
97hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
98{
99 dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
100}
101
102static inline bool
103hash_netport4_data_match(const struct hash_netport4_elem *elem)
104{
105 return !elem->nomatch;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100106}
107
108static inline void
109hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
110{
111 elem->ip &= ip_set_netmask(cidr);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100112 elem->cidr = cidr - 1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100113}
114
115static inline void
116hash_netport4_data_zero_out(struct hash_netport4_elem *elem)
117{
118 elem->proto = 0;
119}
120
121static bool
122hash_netport4_data_list(struct sk_buff *skb,
123 const struct hash_netport4_elem *data)
124{
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100125 u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
126
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100127 NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
128 NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100129 NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100130 NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100131 if (flags)
132 NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100133 return 0;
134
135nla_put_failure:
136 return 1;
137}
138
139static bool
140hash_netport4_data_tlist(struct sk_buff *skb,
141 const struct hash_netport4_elem *data)
142{
143 const struct hash_netport4_telem *tdata =
144 (const struct hash_netport4_telem *)data;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100145 u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100146
147 NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
148 NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100149 NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100150 NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
151 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
152 htonl(ip_set_timeout_get(tdata->timeout)));
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100153 if (flags)
154 NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100155
156 return 0;
157
158nla_put_failure:
159 return 1;
160}
161
162#define IP_SET_HASH_WITH_PROTO
163#define IP_SET_HASH_WITH_NETS
164
165#define PF 4
166#define HOST_MASK 32
167#include <linux/netfilter/ipset/ip_set_ahash.h>
168
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200169static inline void
170hash_netport4_data_next(struct ip_set_hash *h,
171 const struct hash_netport4_elem *d)
172{
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200173 h->next.ip = ntohl(d->ip);
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200174 h->next.port = ntohs(d->port);
175}
176
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100177static int
178hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200179 const struct xt_action_param *par,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200180 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100181{
182 const struct ip_set_hash *h = set->data;
183 ipset_adtfn adtfn = set->variant->adt[adt];
184 struct hash_netport4_elem data = {
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100185 .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
Jozsef Kadlecsik9b03a5e2011-06-16 18:58:20 +0200186 };
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100187
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100188 if (adt == IPSET_TEST)
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100189 data.cidr = HOST_MASK - 1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100190
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200191 if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100192 &data.port, &data.proto))
193 return -EINVAL;
194
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200195 ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100196 data.ip &= ip_set_netmask(data.cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100197
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200198 return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100199}
200
201static int
202hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200203 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100204{
205 const struct ip_set_hash *h = set->data;
206 ipset_adtfn adtfn = set->variant->adt[adt];
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100207 struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 };
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200208 u32 port, port_to, p = 0, ip = 0, ip_to, last;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100209 u32 timeout = h->timeout;
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100210 bool with_ports = false;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100211 u8 cidr;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100212 int ret;
213
214 if (unlikely(!tb[IPSET_ATTR_IP] ||
215 !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
216 !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100217 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
218 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100219 return -IPSET_ERR_PROTOCOL;
220
221 if (tb[IPSET_ATTR_LINENO])
222 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
223
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200224 ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100225 if (ret)
226 return ret;
227
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200228 if (tb[IPSET_ATTR_CIDR]) {
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100229 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
230 if (!cidr || cidr > HOST_MASK)
Jozsef Kadlecsik15b4d932011-06-16 19:01:26 +0200231 return -IPSET_ERR_INVALID_CIDR;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100232 data.cidr = cidr - 1;
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200233 }
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100234
235 if (tb[IPSET_ATTR_PORT])
236 data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
237 else
238 return -IPSET_ERR_PROTOCOL;
239
240 if (tb[IPSET_ATTR_PROTO]) {
241 data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100242 with_ports = ip_set_proto_with_ports(data.proto);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100243
244 if (data.proto == 0)
245 return -IPSET_ERR_INVALID_PROTO;
246 } else
247 return -IPSET_ERR_MISSING_PROTO;
248
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100249 if (!(with_ports || data.proto == IPPROTO_ICMP))
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100250 data.port = 0;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100251
252 if (tb[IPSET_ATTR_TIMEOUT]) {
253 if (!with_timeout(h->timeout))
254 return -IPSET_ERR_TIMEOUT;
255 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
256 }
257
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200258 with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100259
260 if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
261 u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
262 if (cadt_flags & IPSET_FLAG_NOMATCH)
263 flags |= (cadt_flags << 16);
264 }
265
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200266 if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100267 data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200268 ret = adtfn(set, &data, timeout, flags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100269 return ip_set_eexist(ret, flags) ? 0 : ret;
270 }
271
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200272 port = port_to = ntohs(data.port);
273 if (tb[IPSET_ATTR_PORT_TO]) {
274 port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
275 if (port_to < port)
276 swap(port, port_to);
277 }
278 if (tb[IPSET_ATTR_IP_TO]) {
279 ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
280 if (ret)
281 return ret;
282 if (ip_to < ip)
283 swap(ip, ip_to);
284 if (ip + UINT_MAX == ip_to)
285 return -IPSET_ERR_HASH_RANGE;
286 } else {
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100287 ip_set_mask_from_to(ip, ip_to, data.cidr + 1);
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200288 }
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100289
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200290 if (retried)
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200291 ip = h->next.ip;
292 while (!after(ip, ip_to)) {
293 data.ip = htonl(ip);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100294 last = ip_set_range_to_cidr(ip, ip_to, &cidr);
295 data.cidr = cidr - 1;
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200296 p = retried && ip == h->next.ip ? h->next.port : port;
297 for (; p <= port_to; p++) {
298 data.port = htons(p);
299 ret = adtfn(set, &data, timeout, flags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100300
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200301 if (ret && !ip_set_eexist(ret, flags))
302 return ret;
303 else
304 ret = 0;
305 }
306 ip = last + 1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100307 }
308 return ret;
309}
310
311static bool
312hash_netport_same_set(const struct ip_set *a, const struct ip_set *b)
313{
314 const struct ip_set_hash *x = a->data;
315 const struct ip_set_hash *y = b->data;
316
317 /* Resizing changes htable_bits, so we ignore it */
318 return x->maxelem == y->maxelem &&
319 x->timeout == y->timeout;
320}
321
322/* The type variant functions: IPv6 */
323
324struct hash_netport6_elem {
325 union nf_inet_addr ip;
326 __be16 port;
327 u8 proto;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100328 u8 cidr:7;
329 u8 nomatch:1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100330};
331
332struct hash_netport6_telem {
333 union nf_inet_addr ip;
334 __be16 port;
335 u8 proto;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100336 u8 cidr:7;
337 u8 nomatch:1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100338 unsigned long timeout;
339};
340
341static inline bool
342hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
Jozsef Kadlecsik89dc79b2011-07-21 12:06:18 +0200343 const struct hash_netport6_elem *ip2,
344 u32 *multi)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100345{
346 return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
347 ip1->port == ip2->port &&
348 ip1->proto == ip2->proto &&
349 ip1->cidr == ip2->cidr;
350}
351
352static inline bool
353hash_netport6_data_isnull(const struct hash_netport6_elem *elem)
354{
355 return elem->proto == 0;
356}
357
358static inline void
359hash_netport6_data_copy(struct hash_netport6_elem *dst,
360 const struct hash_netport6_elem *src)
361{
362 memcpy(dst, src, sizeof(*dst));
363}
364
365static inline void
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100366hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
367{
368 dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
369}
370
371static inline bool
372hash_netport6_data_match(const struct hash_netport6_elem *elem)
373{
374 return !elem->nomatch;
375}
376
377static inline void
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100378hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
379{
380 elem->proto = 0;
381}
382
383static inline void
384ip6_netmask(union nf_inet_addr *ip, u8 prefix)
385{
386 ip->ip6[0] &= ip_set_netmask6(prefix)[0];
387 ip->ip6[1] &= ip_set_netmask6(prefix)[1];
388 ip->ip6[2] &= ip_set_netmask6(prefix)[2];
389 ip->ip6[3] &= ip_set_netmask6(prefix)[3];
390}
391
392static inline void
393hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
394{
395 ip6_netmask(&elem->ip, cidr);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100396 elem->cidr = cidr - 1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100397}
398
399static bool
400hash_netport6_data_list(struct sk_buff *skb,
401 const struct hash_netport6_elem *data)
402{
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100403 u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
404
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100405 NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
406 NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100407 NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100408 NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100409 if (flags)
410 NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100411 return 0;
412
413nla_put_failure:
414 return 1;
415}
416
417static bool
418hash_netport6_data_tlist(struct sk_buff *skb,
419 const struct hash_netport6_elem *data)
420{
421 const struct hash_netport6_telem *e =
422 (const struct hash_netport6_telem *)data;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100423 u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100424
425 NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
426 NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100427 NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100428 NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
429 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
430 htonl(ip_set_timeout_get(e->timeout)));
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100431 if (flags)
432 NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100433 return 0;
434
435nla_put_failure:
436 return 1;
437}
438
439#undef PF
440#undef HOST_MASK
441
442#define PF 6
443#define HOST_MASK 128
444#include <linux/netfilter/ipset/ip_set_ahash.h>
445
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200446static inline void
447hash_netport6_data_next(struct ip_set_hash *h,
448 const struct hash_netport6_elem *d)
449{
450 h->next.port = ntohs(d->port);
451}
452
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100453static int
454hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200455 const struct xt_action_param *par,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200456 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100457{
458 const struct ip_set_hash *h = set->data;
459 ipset_adtfn adtfn = set->variant->adt[adt];
460 struct hash_netport6_elem data = {
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100461 .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
Jozsef Kadlecsik9b03a5e2011-06-16 18:58:20 +0200462 };
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100463
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100464 if (adt == IPSET_TEST)
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100465 data.cidr = HOST_MASK - 1;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100466
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200467 if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100468 &data.port, &data.proto))
469 return -EINVAL;
470
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200471 ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100472 ip6_netmask(&data.ip, data.cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100473
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200474 return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100475}
476
477static int
478hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200479 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100480{
481 const struct ip_set_hash *h = set->data;
482 ipset_adtfn adtfn = set->variant->adt[adt];
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100483 struct hash_netport6_elem data = { .cidr = HOST_MASK - 1 };
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100484 u32 port, port_to;
485 u32 timeout = h->timeout;
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100486 bool with_ports = false;
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100487 u8 cidr;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100488 int ret;
489
490 if (unlikely(!tb[IPSET_ATTR_IP] ||
491 !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
492 !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100493 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
494 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100495 return -IPSET_ERR_PROTOCOL;
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200496 if (unlikely(tb[IPSET_ATTR_IP_TO]))
497 return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100498
499 if (tb[IPSET_ATTR_LINENO])
500 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
501
502 ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
503 if (ret)
504 return ret;
505
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100506 if (tb[IPSET_ATTR_CIDR]) {
507 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
508 if (!cidr || cidr > HOST_MASK)
509 return -IPSET_ERR_INVALID_CIDR;
510 data.cidr = cidr - 1;
511 }
512 ip6_netmask(&data.ip, data.cidr + 1);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100513
514 if (tb[IPSET_ATTR_PORT])
515 data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
516 else
517 return -IPSET_ERR_PROTOCOL;
518
519 if (tb[IPSET_ATTR_PROTO]) {
520 data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100521 with_ports = ip_set_proto_with_ports(data.proto);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100522
523 if (data.proto == 0)
524 return -IPSET_ERR_INVALID_PROTO;
525 } else
526 return -IPSET_ERR_MISSING_PROTO;
527
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100528 if (!(with_ports || data.proto == IPPROTO_ICMPV6))
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100529 data.port = 0;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100530
531 if (tb[IPSET_ATTR_TIMEOUT]) {
532 if (!with_timeout(h->timeout))
533 return -IPSET_ERR_TIMEOUT;
534 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
535 }
536
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100537 if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
538 u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
539 if (cadt_flags & IPSET_FLAG_NOMATCH)
540 flags |= (cadt_flags << 16);
541 }
542
Jozsef Kadlecsik5e0c1eb2011-03-20 15:33:26 +0100543 if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200544 ret = adtfn(set, &data, timeout, flags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100545 return ip_set_eexist(ret, flags) ? 0 : ret;
546 }
547
548 port = ntohs(data.port);
549 port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
550 if (port > port_to)
551 swap(port, port_to);
552
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200553 if (retried)
554 port = h->next.port;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100555 for (; port <= port_to; port++) {
556 data.port = htons(port);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200557 ret = adtfn(set, &data, timeout, flags);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100558
559 if (ret && !ip_set_eexist(ret, flags))
560 return ret;
561 else
562 ret = 0;
563 }
564 return ret;
565}
566
567/* Create hash:ip type of sets */
568
569static int
570hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
571{
572 struct ip_set_hash *h;
573 u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
574 u8 hbits;
Jozsef Kadlecsik26a5d3c2012-05-14 01:47:01 +0000575 size_t hsize;
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100576
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100577 if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100578 return -IPSET_ERR_INVALID_FAMILY;
579
580 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
581 !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
582 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
583 return -IPSET_ERR_PROTOCOL;
584
585 if (tb[IPSET_ATTR_HASHSIZE]) {
586 hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
587 if (hashsize < IPSET_MIMINAL_HASHSIZE)
588 hashsize = IPSET_MIMINAL_HASHSIZE;
589 }
590
591 if (tb[IPSET_ATTR_MAXELEM])
592 maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
593
594 h = kzalloc(sizeof(*h)
595 + sizeof(struct ip_set_hash_nets)
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100596 * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100597 if (!h)
598 return -ENOMEM;
599
600 h->maxelem = maxelem;
601 get_random_bytes(&h->initval, sizeof(h->initval));
602 h->timeout = IPSET_NO_TIMEOUT;
603
604 hbits = htable_bits(hashsize);
Jozsef Kadlecsik26a5d3c2012-05-14 01:47:01 +0000605 hsize = htable_size(hbits);
606 if (hsize == 0) {
607 kfree(h);
608 return -ENOMEM;
609 }
610 h->table = ip_set_alloc(hsize);
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100611 if (!h->table) {
612 kfree(h);
613 return -ENOMEM;
614 }
615 h->table->htable_bits = hbits;
616
617 set->data = h;
618
619 if (tb[IPSET_ATTR_TIMEOUT]) {
620 h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
621
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100622 set->variant = set->family == NFPROTO_IPV4
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100623 ? &hash_netport4_tvariant : &hash_netport6_tvariant;
624
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100625 if (set->family == NFPROTO_IPV4)
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100626 hash_netport4_gc_init(set);
627 else
628 hash_netport6_gc_init(set);
629 } else {
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100630 set->variant = set->family == NFPROTO_IPV4
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100631 ? &hash_netport4_variant : &hash_netport6_variant;
632 }
633
634 pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
635 set->name, jhash_size(h->table->htable_bits),
636 h->table->htable_bits, h->maxelem, set->data, h->table);
637
638 return 0;
639}
640
641static struct ip_set_type hash_netport_type __read_mostly = {
642 .name = "hash:net,port",
643 .protocol = IPSET_PROTOCOL,
644 .features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
645 .dimension = IPSET_DIM_TWO,
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100646 .family = NFPROTO_UNSPEC,
Jozsef Kadlecsikf1e00b32011-06-16 18:51:41 +0200647 .revision_min = 0,
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200648 /* 1 SCTP and UDPLITE support added */
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100649 /* 2, Range as input support for IPv4 added */
650 .revision_max = 3, /* nomatch flag support added */
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100651 .create = hash_netport_create,
652 .create_policy = {
653 [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
654 [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
655 [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
656 [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
657 [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
658 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
659 },
660 .adt_policy = {
661 [IPSET_ATTR_IP] = { .type = NLA_NESTED },
Jozsef Kadlecsikd0d9e0a2011-06-16 18:52:41 +0200662 [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100663 [IPSET_ATTR_PORT] = { .type = NLA_U16 },
664 [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
665 [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
666 [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
667 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
668 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
Jozsef Kadlecsik2a7cef22012-01-14 17:16:36 +0100669 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
Jozsef Kadlecsik21f45022011-02-01 15:53:55 +0100670 },
671 .me = THIS_MODULE,
672};
673
674static int __init
675hash_netport_init(void)
676{
677 return ip_set_type_register(&hash_netport_type);
678}
679
680static void __exit
681hash_netport_fini(void)
682{
683 ip_set_type_unregister(&hash_netport_type);
684}
685
686module_init(hash_netport_init);
687module_exit(hash_netport_fini);