Commit ea53ac5b630e813aec20c7cdcfe511daca70dee4
Committed by
Jozsef Kadlecsik
1 parent
d9628bbeca
netfilter: ipset: Add hash:net,net module to kernel.
This adds a new set that provides the ability to configure pairs of subnets. A small amount of additional handling code has been added to the generic hash header file - this code is conditionally activated by a preprocessor definition. 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 4 changed files with 541 additions and 9 deletions Side-by-side Diff
net/netfilter/ipset/Kconfig
... | ... | @@ -99,6 +99,15 @@ |
99 | 99 | |
100 | 100 | To compile it as a module, choose M here. If unsure, say N. |
101 | 101 | |
102 | +config IP_SET_HASH_NETNET | |
103 | + tristate "hash:net,net set support" | |
104 | + depends on IP_SET | |
105 | + help | |
106 | + This option adds the hash:net,net set type support, by which | |
107 | + one can store IPv4/IPv6 network address/prefix pairs in a set. | |
108 | + | |
109 | + To compile it as a module, choose M here. If unsure, say N. | |
110 | + | |
102 | 111 | config IP_SET_HASH_NETPORT |
103 | 112 | tristate "hash:net,port set support" |
104 | 113 | depends on IP_SET |
net/netfilter/ipset/Makefile
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o |
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 | +obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o | |
23 | 24 | |
24 | 25 | # list types |
25 | 26 | obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o |
net/netfilter/ipset/ip_set_hash_gen.h
... | ... | @@ -142,11 +142,16 @@ |
142 | 142 | } |
143 | 143 | |
144 | 144 | #ifdef IP_SET_HASH_WITH_NETS |
145 | +#if IPSET_NET_COUNT > 1 | |
146 | +#define __CIDR(cidr, i) (cidr[i]) | |
147 | +#else | |
148 | +#define __CIDR(cidr, i) (cidr) | |
149 | +#endif | |
145 | 150 | #ifdef IP_SET_HASH_WITH_NETS_PACKED |
146 | 151 | /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ |
147 | -#define CIDR(cidr) (cidr + 1) | |
152 | +#define CIDR(cidr, i) (__CIDR(cidr, i) + 1) | |
148 | 153 | #else |
149 | -#define CIDR(cidr) (cidr) | |
154 | +#define CIDR(cidr, i) (__CIDR(cidr, i)) | |
150 | 155 | #endif |
151 | 156 | |
152 | 157 | #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) |
... | ... | @@ -210,6 +215,7 @@ |
210 | 215 | #define mtype_do_data_match(d) 1 |
211 | 216 | #endif |
212 | 217 | #define mtype_data_set_flags IPSET_TOKEN(MTYPE, _data_set_flags) |
218 | +#define mtype_data_reset_elem IPSET_TOKEN(MTYPE, _data_reset_elem) | |
213 | 219 | #define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags) |
214 | 220 | #define mtype_data_netmask IPSET_TOKEN(MTYPE, _data_netmask) |
215 | 221 | #define mtype_data_list IPSET_TOKEN(MTYPE, _data_list) |
... | ... | @@ -461,6 +467,9 @@ |
461 | 467 | struct mtype_elem *data; |
462 | 468 | u32 i; |
463 | 469 | int j; |
470 | +#ifdef IP_SET_HASH_WITH_NETS | |
471 | + u8 k; | |
472 | +#endif | |
464 | 473 | |
465 | 474 | rcu_read_lock_bh(); |
466 | 475 | t = rcu_dereference_bh(h->table); |
... | ... | @@ -471,8 +480,9 @@ |
471 | 480 | if (ip_set_timeout_expired(ext_timeout(data, set))) { |
472 | 481 | pr_debug("expired %u/%u\n", i, j); |
473 | 482 | #ifdef IP_SET_HASH_WITH_NETS |
474 | - mtype_del_cidr(h, CIDR(data->cidr), | |
475 | - nets_length, 0); | |
483 | + for (k = 0; k < IPSET_NET_COUNT; k++) | |
484 | + mtype_del_cidr(h, CIDR(data->cidr, k), | |
485 | + nets_length, k); | |
476 | 486 | #endif |
477 | 487 | ip_set_ext_destroy(set, data); |
478 | 488 | if (j != n->pos - 1) |
... | ... | @@ -658,8 +668,12 @@ |
658 | 668 | /* Fill out reused slot */ |
659 | 669 | data = ahash_data(n, j, set->dsize); |
660 | 670 | #ifdef IP_SET_HASH_WITH_NETS |
661 | - mtype_del_cidr(h, CIDR(data->cidr), NLEN(set->family), 0); | |
662 | - mtype_add_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); | |
671 | + for (i = 0; i < IPSET_NET_COUNT; i++) { | |
672 | + mtype_del_cidr(h, CIDR(data->cidr, i), | |
673 | + NLEN(set->family), i); | |
674 | + mtype_add_cidr(h, CIDR(d->cidr, i), | |
675 | + NLEN(set->family), i); | |
676 | + } | |
663 | 677 | #endif |
664 | 678 | ip_set_ext_destroy(set, data); |
665 | 679 | } else { |
... | ... | @@ -673,7 +687,9 @@ |
673 | 687 | } |
674 | 688 | data = ahash_data(n, n->pos++, set->dsize); |
675 | 689 | #ifdef IP_SET_HASH_WITH_NETS |
676 | - mtype_add_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); | |
690 | + for (i = 0; i < IPSET_NET_COUNT; i++) | |
691 | + mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family), | |
692 | + i); | |
677 | 693 | #endif |
678 | 694 | h->elements++; |
679 | 695 | } |
... | ... | @@ -704,6 +720,9 @@ |
704 | 720 | struct mtype_elem *data; |
705 | 721 | struct hbucket *n; |
706 | 722 | int i, ret = -IPSET_ERR_EXIST; |
723 | +#ifdef IP_SET_HASH_WITH_NETS | |
724 | + u8 j; | |
725 | +#endif | |
707 | 726 | u32 key, multi = 0; |
708 | 727 | |
709 | 728 | rcu_read_lock_bh(); |
... | ... | @@ -725,7 +744,9 @@ |
725 | 744 | n->pos--; |
726 | 745 | h->elements--; |
727 | 746 | #ifdef IP_SET_HASH_WITH_NETS |
728 | - mtype_del_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); | |
747 | + for (j = 0; j < IPSET_NET_COUNT; j++) | |
748 | + mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family), | |
749 | + j); | |
729 | 750 | #endif |
730 | 751 | ip_set_ext_destroy(set, data); |
731 | 752 | if (n->pos + AHASH_INIT_SIZE < n->size) { |
732 | 753 | |
733 | 754 | |
734 | 755 | |
... | ... | @@ -772,13 +793,26 @@ |
772 | 793 | struct htable *t = rcu_dereference_bh(h->table); |
773 | 794 | struct hbucket *n; |
774 | 795 | struct mtype_elem *data; |
796 | +#if IPSET_NET_COUNT == 2 | |
797 | + struct mtype_elem orig = *d; | |
798 | + int i, j = 0, k; | |
799 | +#else | |
775 | 800 | int i, j = 0; |
801 | +#endif | |
776 | 802 | u32 key, multi = 0; |
777 | 803 | u8 nets_length = NLEN(set->family); |
778 | 804 | |
779 | 805 | pr_debug("test by nets\n"); |
780 | 806 | for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) { |
807 | +#if IPSET_NET_COUNT == 2 | |
808 | + mtype_data_reset_elem(d, &orig); | |
809 | + mtype_data_netmask(d, h->nets[j].cidr[0], false); | |
810 | + for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi; | |
811 | + k++) { | |
812 | + mtype_data_netmask(d, h->nets[k].cidr[1], true); | |
813 | +#else | |
781 | 814 | mtype_data_netmask(d, h->nets[j].cidr[0]); |
815 | +#endif | |
782 | 816 | key = HKEY(d, h->initval, t->htable_bits); |
783 | 817 | n = hbucket(t, key); |
784 | 818 | for (i = 0; i < n->pos; i++) { |
... | ... | @@ -798,6 +832,9 @@ |
798 | 832 | return mtype_data_match(data, ext, |
799 | 833 | mext, set, flags); |
800 | 834 | } |
835 | +#if IPSET_NET_COUNT == 2 | |
836 | + } | |
837 | +#endif | |
801 | 838 | } |
802 | 839 | return 0; |
803 | 840 | } |
... | ... | @@ -821,7 +858,10 @@ |
821 | 858 | #ifdef IP_SET_HASH_WITH_NETS |
822 | 859 | /* If we test an IP address and not a network address, |
823 | 860 | * try all possible network sizes */ |
824 | - if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) { | |
861 | + for (i = 0; i < IPSET_NET_COUNT; i++) | |
862 | + if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family)) | |
863 | + break; | |
864 | + if (i == IPSET_NET_COUNT) { | |
825 | 865 | ret = mtype_test_cidrs(set, d, ext, mext, flags); |
826 | 866 | goto out; |
827 | 867 | } |
net/netfilter/ipset/ip_set_hash_netnet.c
1 | +/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
2 | + * Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa> | |
3 | + * | |
4 | + * This program is free software; you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License version 2 as | |
6 | + * published by the Free Software Foundation. | |
7 | + */ | |
8 | + | |
9 | +/* Kernel module implementing an IP set type: the hash:net type */ | |
10 | + | |
11 | +#include <linux/jhash.h> | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/ip.h> | |
14 | +#include <linux/skbuff.h> | |
15 | +#include <linux/errno.h> | |
16 | +#include <linux/random.h> | |
17 | +#include <net/ip.h> | |
18 | +#include <net/ipv6.h> | |
19 | +#include <net/netlink.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_hash.h> | |
25 | + | |
26 | +#define IPSET_TYPE_REV_MIN 0 | |
27 | +#define IPSET_TYPE_REV_MAX 0 | |
28 | + | |
29 | +MODULE_LICENSE("GPL"); | |
30 | +MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>"); | |
31 | +IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); | |
32 | +MODULE_ALIAS("ip_set_hash:net,net"); | |
33 | + | |
34 | +/* Type specific function prefix */ | |
35 | +#define HTYPE hash_netnet | |
36 | +#define IP_SET_HASH_WITH_NETS | |
37 | +#define IPSET_NET_COUNT 2 | |
38 | + | |
39 | +/* IPv4 variants */ | |
40 | + | |
41 | +/* Member elements */ | |
42 | +struct hash_netnet4_elem { | |
43 | + union { | |
44 | + __be32 ip[2]; | |
45 | + __be64 ipcmp; | |
46 | + }; | |
47 | + u8 nomatch; | |
48 | + union { | |
49 | + u8 cidr[2]; | |
50 | + u16 ccmp; | |
51 | + }; | |
52 | +}; | |
53 | + | |
54 | +/* Common functions */ | |
55 | + | |
56 | +static inline bool | |
57 | +hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1, | |
58 | + const struct hash_netnet4_elem *ip2, | |
59 | + u32 *multi) | |
60 | +{ | |
61 | + return ip1->ipcmp == ip2->ipcmp && | |
62 | + ip2->ccmp == ip2->ccmp; | |
63 | +} | |
64 | + | |
65 | +static inline int | |
66 | +hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem) | |
67 | +{ | |
68 | + return elem->nomatch ? -ENOTEMPTY : 1; | |
69 | +} | |
70 | + | |
71 | +static inline void | |
72 | +hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags) | |
73 | +{ | |
74 | + elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; | |
75 | +} | |
76 | + | |
77 | +static inline void | |
78 | +hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags) | |
79 | +{ | |
80 | + swap(*flags, elem->nomatch); | |
81 | +} | |
82 | + | |
83 | +static inline void | |
84 | +hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem, | |
85 | + struct hash_netnet4_elem *orig) | |
86 | +{ | |
87 | + elem->ip[1] = orig->ip[1]; | |
88 | +} | |
89 | + | |
90 | +static inline void | |
91 | +hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner) | |
92 | +{ | |
93 | + if (inner) { | |
94 | + elem->ip[1] &= ip_set_netmask(cidr); | |
95 | + elem->cidr[1] = cidr; | |
96 | + } else { | |
97 | + elem->ip[0] &= ip_set_netmask(cidr); | |
98 | + elem->cidr[0] = cidr; | |
99 | + } | |
100 | +} | |
101 | + | |
102 | +static bool | |
103 | +hash_netnet4_data_list(struct sk_buff *skb, | |
104 | + const struct hash_netnet4_elem *data) | |
105 | +{ | |
106 | + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; | |
107 | + | |
108 | + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || | |
109 | + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || | |
110 | + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || | |
111 | + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || | |
112 | + (flags && | |
113 | + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
114 | + goto nla_put_failure; | |
115 | + return 0; | |
116 | + | |
117 | +nla_put_failure: | |
118 | + return 1; | |
119 | +} | |
120 | + | |
121 | +static inline void | |
122 | +hash_netnet4_data_next(struct hash_netnet4_elem *next, | |
123 | + const struct hash_netnet4_elem *d) | |
124 | +{ | |
125 | + next->ipcmp = d->ipcmp; | |
126 | +} | |
127 | + | |
128 | +#define MTYPE hash_netnet4 | |
129 | +#define PF 4 | |
130 | +#define HOST_MASK 32 | |
131 | +#include "ip_set_hash_gen.h" | |
132 | + | |
133 | +static int | |
134 | +hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
135 | + const struct xt_action_param *par, | |
136 | + enum ipset_adt adt, struct ip_set_adt_opt *opt) | |
137 | +{ | |
138 | + const struct hash_netnet *h = set->data; | |
139 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
140 | + struct hash_netnet4_elem e = { | |
141 | + .cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK, | |
142 | + .cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK, | |
143 | + }; | |
144 | + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); | |
145 | + | |
146 | + if (adt == IPSET_TEST) | |
147 | + e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; | |
148 | + | |
149 | + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); | |
150 | + ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]); | |
151 | + e.ip[0] &= ip_set_netmask(e.cidr[0]); | |
152 | + e.ip[1] &= ip_set_netmask(e.cidr[1]); | |
153 | + | |
154 | + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); | |
155 | +} | |
156 | + | |
157 | +static int | |
158 | +hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], | |
159 | + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
160 | +{ | |
161 | + const struct hash_netnet *h = set->data; | |
162 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
163 | + struct hash_netnet4_elem e = { .cidr[0] = HOST_MASK, | |
164 | + .cidr[1] = HOST_MASK }; | |
165 | + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); | |
166 | + u32 ip = 0, ip_to = 0, last; | |
167 | + u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2; | |
168 | + u8 cidr, cidr2; | |
169 | + int ret; | |
170 | + | |
171 | + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
172 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
173 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || | |
174 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
175 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
176 | + return -IPSET_ERR_PROTOCOL; | |
177 | + | |
178 | + if (tb[IPSET_ATTR_LINENO]) | |
179 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
180 | + | |
181 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || | |
182 | + ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || | |
183 | + ip_set_get_extensions(set, tb, &ext); | |
184 | + if (ret) | |
185 | + return ret; | |
186 | + | |
187 | + if (tb[IPSET_ATTR_CIDR]) { | |
188 | + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
189 | + if (!cidr || cidr > HOST_MASK) | |
190 | + return -IPSET_ERR_INVALID_CIDR; | |
191 | + e.cidr[0] = cidr; | |
192 | + } | |
193 | + | |
194 | + if (tb[IPSET_ATTR_CIDR2]) { | |
195 | + cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | |
196 | + if (!cidr2 || cidr2 > HOST_MASK) | |
197 | + return -IPSET_ERR_INVALID_CIDR; | |
198 | + e.cidr[1] = cidr2; | |
199 | + } | |
200 | + | |
201 | + if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
202 | + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | |
203 | + if (cadt_flags & IPSET_FLAG_NOMATCH) | |
204 | + flags |= (IPSET_FLAG_NOMATCH << 16); | |
205 | + } | |
206 | + | |
207 | + if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] && | |
208 | + tb[IPSET_ATTR_IP2_TO])) { | |
209 | + e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); | |
210 | + e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); | |
211 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
212 | + return ip_set_enomatch(ret, flags, adt, set) ? -ret : | |
213 | + ip_set_eexist(ret, flags) ? 0 : ret; | |
214 | + } | |
215 | + | |
216 | + ip_to = ip; | |
217 | + if (tb[IPSET_ATTR_IP_TO]) { | |
218 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | |
219 | + if (ret) | |
220 | + return ret; | |
221 | + if (ip_to < ip) | |
222 | + swap(ip, ip_to); | |
223 | + if (ip + UINT_MAX == ip_to) | |
224 | + return -IPSET_ERR_HASH_RANGE; | |
225 | + } | |
226 | + | |
227 | + ip2_to = ip2_from; | |
228 | + if (tb[IPSET_ATTR_IP2_TO]) { | |
229 | + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); | |
230 | + if (ret) | |
231 | + return ret; | |
232 | + if (ip2_to < ip2_from) | |
233 | + swap(ip2_from, ip2_to); | |
234 | + if (ip2_from + UINT_MAX == ip2_to) | |
235 | + return -IPSET_ERR_HASH_RANGE; | |
236 | + | |
237 | + } | |
238 | + | |
239 | + if (retried) | |
240 | + ip = ntohl(h->next.ip[0]); | |
241 | + | |
242 | + while (!after(ip, ip_to)) { | |
243 | + e.ip[0] = htonl(ip); | |
244 | + last = ip_set_range_to_cidr(ip, ip_to, &cidr); | |
245 | + e.cidr[0] = cidr; | |
246 | + ip2 = (retried && | |
247 | + ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1]) | |
248 | + : ip2_from; | |
249 | + while (!after(ip2, ip2_to)) { | |
250 | + e.ip[1] = htonl(ip2); | |
251 | + last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2); | |
252 | + e.cidr[1] = cidr2; | |
253 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
254 | + if (ret && !ip_set_eexist(ret, flags)) | |
255 | + return ret; | |
256 | + else | |
257 | + ret = 0; | |
258 | + ip2 = last2 + 1; | |
259 | + } | |
260 | + ip = last + 1; | |
261 | + } | |
262 | + return ret; | |
263 | +} | |
264 | + | |
265 | +/* IPv6 variants */ | |
266 | + | |
267 | +struct hash_netnet6_elem { | |
268 | + union nf_inet_addr ip[2]; | |
269 | + u8 nomatch; | |
270 | + union { | |
271 | + u8 cidr[2]; | |
272 | + u16 ccmp; | |
273 | + }; | |
274 | +}; | |
275 | + | |
276 | +/* Common functions */ | |
277 | + | |
278 | +static inline bool | |
279 | +hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1, | |
280 | + const struct hash_netnet6_elem *ip2, | |
281 | + u32 *multi) | |
282 | +{ | |
283 | + return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && | |
284 | + ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && | |
285 | + ip1->ccmp == ip2->ccmp; | |
286 | +} | |
287 | + | |
288 | +static inline int | |
289 | +hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem) | |
290 | +{ | |
291 | + return elem->nomatch ? -ENOTEMPTY : 1; | |
292 | +} | |
293 | + | |
294 | +static inline void | |
295 | +hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags) | |
296 | +{ | |
297 | + elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; | |
298 | +} | |
299 | + | |
300 | +static inline void | |
301 | +hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags) | |
302 | +{ | |
303 | + swap(*flags, elem->nomatch); | |
304 | +} | |
305 | + | |
306 | +static inline void | |
307 | +hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem, | |
308 | + struct hash_netnet6_elem *orig) | |
309 | +{ | |
310 | + elem->ip[1] = orig->ip[1]; | |
311 | +} | |
312 | + | |
313 | +static inline void | |
314 | +hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner) | |
315 | +{ | |
316 | + if (inner) { | |
317 | + ip6_netmask(&elem->ip[1], cidr); | |
318 | + elem->cidr[1] = cidr; | |
319 | + } else { | |
320 | + ip6_netmask(&elem->ip[0], cidr); | |
321 | + elem->cidr[0] = cidr; | |
322 | + } | |
323 | +} | |
324 | + | |
325 | +static bool | |
326 | +hash_netnet6_data_list(struct sk_buff *skb, | |
327 | + const struct hash_netnet6_elem *data) | |
328 | +{ | |
329 | + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; | |
330 | + | |
331 | + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || | |
332 | + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || | |
333 | + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || | |
334 | + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || | |
335 | + (flags && | |
336 | + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) | |
337 | + goto nla_put_failure; | |
338 | + return 0; | |
339 | + | |
340 | +nla_put_failure: | |
341 | + return 1; | |
342 | +} | |
343 | + | |
344 | +static inline void | |
345 | +hash_netnet6_data_next(struct hash_netnet4_elem *next, | |
346 | + const struct hash_netnet6_elem *d) | |
347 | +{ | |
348 | +} | |
349 | + | |
350 | +#undef MTYPE | |
351 | +#undef PF | |
352 | +#undef HOST_MASK | |
353 | + | |
354 | +#define MTYPE hash_netnet6 | |
355 | +#define PF 6 | |
356 | +#define HOST_MASK 128 | |
357 | +#define IP_SET_EMIT_CREATE | |
358 | +#include "ip_set_hash_gen.h" | |
359 | + | |
360 | +static int | |
361 | +hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, | |
362 | + const struct xt_action_param *par, | |
363 | + enum ipset_adt adt, struct ip_set_adt_opt *opt) | |
364 | +{ | |
365 | + const struct hash_netnet *h = set->data; | |
366 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
367 | + struct hash_netnet6_elem e = { | |
368 | + .cidr[0] = h->nets[0].cidr[0] ? h->nets[0].cidr[0] : HOST_MASK, | |
369 | + .cidr[1] = h->nets[0].cidr[1] ? h->nets[0].cidr[1] : HOST_MASK | |
370 | + }; | |
371 | + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); | |
372 | + | |
373 | + if (adt == IPSET_TEST) | |
374 | + e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK; | |
375 | + | |
376 | + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); | |
377 | + ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6); | |
378 | + ip6_netmask(&e.ip[0], e.cidr[0]); | |
379 | + ip6_netmask(&e.ip[1], e.cidr[1]); | |
380 | + | |
381 | + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); | |
382 | +} | |
383 | + | |
384 | +static int | |
385 | +hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], | |
386 | + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
387 | +{ | |
388 | + ipset_adtfn adtfn = set->variant->adt[adt]; | |
389 | + struct hash_netnet6_elem e = { .cidr[0] = HOST_MASK, | |
390 | + .cidr[1] = HOST_MASK }; | |
391 | + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); | |
392 | + int ret; | |
393 | + | |
394 | + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | |
395 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
396 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || | |
397 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || | |
398 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) | |
399 | + return -IPSET_ERR_PROTOCOL; | |
400 | + if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) | |
401 | + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; | |
402 | + | |
403 | + if (tb[IPSET_ATTR_LINENO]) | |
404 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
405 | + | |
406 | + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || | |
407 | + ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || | |
408 | + ip_set_get_extensions(set, tb, &ext); | |
409 | + if (ret) | |
410 | + return ret; | |
411 | + | |
412 | + if (tb[IPSET_ATTR_CIDR]) | |
413 | + e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); | |
414 | + | |
415 | + if (tb[IPSET_ATTR_CIDR2]) | |
416 | + e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | |
417 | + | |
418 | + if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || | |
419 | + e.cidr[1] > HOST_MASK) | |
420 | + return -IPSET_ERR_INVALID_CIDR; | |
421 | + | |
422 | + ip6_netmask(&e.ip[0], e.cidr[0]); | |
423 | + ip6_netmask(&e.ip[1], e.cidr[1]); | |
424 | + | |
425 | + if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
426 | + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | |
427 | + if (cadt_flags & IPSET_FLAG_NOMATCH) | |
428 | + flags |= (IPSET_FLAG_NOMATCH << 16); | |
429 | + } | |
430 | + | |
431 | + ret = adtfn(set, &e, &ext, &ext, flags); | |
432 | + | |
433 | + return ip_set_enomatch(ret, flags, adt, set) ? -ret : | |
434 | + ip_set_eexist(ret, flags) ? 0 : ret; | |
435 | +} | |
436 | + | |
437 | +static struct ip_set_type hash_netnet_type __read_mostly = { | |
438 | + .name = "hash:net,net", | |
439 | + .protocol = IPSET_PROTOCOL, | |
440 | + .features = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH, | |
441 | + .dimension = IPSET_DIM_TWO, | |
442 | + .family = NFPROTO_UNSPEC, | |
443 | + .revision_min = IPSET_TYPE_REV_MIN, | |
444 | + .revision_max = IPSET_TYPE_REV_MAX, | |
445 | + .create = hash_netnet_create, | |
446 | + .create_policy = { | |
447 | + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
448 | + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
449 | + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | |
450 | + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | |
451 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
452 | + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
453 | + }, | |
454 | + .adt_policy = { | |
455 | + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | |
456 | + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | |
457 | + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, | |
458 | + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, | |
459 | + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | |
460 | + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, | |
461 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
462 | + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
463 | + [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, | |
464 | + [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
465 | + }, | |
466 | + .me = THIS_MODULE, | |
467 | +}; | |
468 | + | |
469 | +static int __init | |
470 | +hash_netnet_init(void) | |
471 | +{ | |
472 | + return ip_set_type_register(&hash_netnet_type); | |
473 | +} | |
474 | + | |
475 | +static void __exit | |
476 | +hash_netnet_fini(void) | |
477 | +{ | |
478 | + ip_set_type_unregister(&hash_netnet_type); | |
479 | +} | |
480 | + | |
481 | +module_init(hash_netnet_init); | |
482 | +module_exit(hash_netnet_fini); |