diff options
40 files changed, 10210 insertions, 34 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 89c0d1e20d72..15e83bf3dd58 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild | |||
@@ -1,3 +1,5 @@ | |||
1 | header-y += ipset/ | ||
2 | |||
1 | header-y += nf_conntrack_common.h | 3 | header-y += nf_conntrack_common.h |
2 | header-y += nf_conntrack_ftp.h | 4 | header-y += nf_conntrack_ftp.h |
3 | header-y += nf_conntrack_sctp.h | 5 | header-y += nf_conntrack_sctp.h |
@@ -35,6 +37,7 @@ header-y += xt_connmark.h | |||
35 | header-y += xt_conntrack.h | 37 | header-y += xt_conntrack.h |
36 | header-y += xt_cpu.h | 38 | header-y += xt_cpu.h |
37 | header-y += xt_dccp.h | 39 | header-y += xt_dccp.h |
40 | header-y += xt_devgroup.h | ||
38 | header-y += xt_dscp.h | 41 | header-y += xt_dscp.h |
39 | header-y += xt_esp.h | 42 | header-y += xt_esp.h |
40 | header-y += xt_hashlimit.h | 43 | header-y += xt_hashlimit.h |
@@ -55,6 +58,7 @@ header-y += xt_quota.h | |||
55 | header-y += xt_rateest.h | 58 | header-y += xt_rateest.h |
56 | header-y += xt_realm.h | 59 | header-y += xt_realm.h |
57 | header-y += xt_recent.h | 60 | header-y += xt_recent.h |
61 | header-y += xt_set.h | ||
58 | header-y += xt_sctp.h | 62 | header-y += xt_sctp.h |
59 | header-y += xt_socket.h | 63 | header-y += xt_socket.h |
60 | header-y += xt_state.h | 64 | header-y += xt_state.h |
diff --git a/include/linux/netfilter/ipset/Kbuild b/include/linux/netfilter/ipset/Kbuild new file mode 100644 index 000000000000..601fe71d34d5 --- /dev/null +++ b/include/linux/netfilter/ipset/Kbuild | |||
@@ -0,0 +1,4 @@ | |||
1 | header-y += ip_set.h | ||
2 | header-y += ip_set_bitmap.h | ||
3 | header-y += ip_set_hash.h | ||
4 | header-y += ip_set_list.h | ||
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h new file mode 100644 index 000000000000..ec333d83f3b4 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set.h | |||
@@ -0,0 +1,452 @@ | |||
1 | #ifndef _IP_SET_H | ||
2 | #define _IP_SET_H | ||
3 | |||
4 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
5 | * Patrick Schaaf <bof@bof.de> | ||
6 | * Martin Josefsson <gandalf@wlug.westbo.se> | ||
7 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | /* The protocol version */ | ||
15 | #define IPSET_PROTOCOL 6 | ||
16 | |||
17 | /* The max length of strings including NUL: set and type identifiers */ | ||
18 | #define IPSET_MAXNAMELEN 32 | ||
19 | |||
20 | /* Message types and commands */ | ||
21 | enum ipset_cmd { | ||
22 | IPSET_CMD_NONE, | ||
23 | IPSET_CMD_PROTOCOL, /* 1: Return protocol version */ | ||
24 | IPSET_CMD_CREATE, /* 2: Create a new (empty) set */ | ||
25 | IPSET_CMD_DESTROY, /* 3: Destroy a (empty) set */ | ||
26 | IPSET_CMD_FLUSH, /* 4: Remove all elements from a set */ | ||
27 | IPSET_CMD_RENAME, /* 5: Rename a set */ | ||
28 | IPSET_CMD_SWAP, /* 6: Swap two sets */ | ||
29 | IPSET_CMD_LIST, /* 7: List sets */ | ||
30 | IPSET_CMD_SAVE, /* 8: Save sets */ | ||
31 | IPSET_CMD_ADD, /* 9: Add an element to a set */ | ||
32 | IPSET_CMD_DEL, /* 10: Delete an element from a set */ | ||
33 | IPSET_CMD_TEST, /* 11: Test an element in a set */ | ||
34 | IPSET_CMD_HEADER, /* 12: Get set header data only */ | ||
35 | IPSET_CMD_TYPE, /* 13: Get set type */ | ||
36 | IPSET_MSG_MAX, /* Netlink message commands */ | ||
37 | |||
38 | /* Commands in userspace: */ | ||
39 | IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */ | ||
40 | IPSET_CMD_HELP, /* 15: Get help */ | ||
41 | IPSET_CMD_VERSION, /* 16: Get program version */ | ||
42 | IPSET_CMD_QUIT, /* 17: Quit from interactive mode */ | ||
43 | |||
44 | IPSET_CMD_MAX, | ||
45 | |||
46 | IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */ | ||
47 | }; | ||
48 | |||
49 | /* Attributes at command level */ | ||
50 | enum { | ||
51 | IPSET_ATTR_UNSPEC, | ||
52 | IPSET_ATTR_PROTOCOL, /* 1: Protocol version */ | ||
53 | IPSET_ATTR_SETNAME, /* 2: Name of the set */ | ||
54 | IPSET_ATTR_TYPENAME, /* 3: Typename */ | ||
55 | IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */ | ||
56 | IPSET_ATTR_REVISION, /* 4: Settype revision */ | ||
57 | IPSET_ATTR_FAMILY, /* 5: Settype family */ | ||
58 | IPSET_ATTR_FLAGS, /* 6: Flags at command level */ | ||
59 | IPSET_ATTR_DATA, /* 7: Nested attributes */ | ||
60 | IPSET_ATTR_ADT, /* 8: Multiple data containers */ | ||
61 | IPSET_ATTR_LINENO, /* 9: Restore lineno */ | ||
62 | IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */ | ||
63 | IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */ | ||
64 | __IPSET_ATTR_CMD_MAX, | ||
65 | }; | ||
66 | #define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1) | ||
67 | |||
68 | /* CADT specific attributes */ | ||
69 | enum { | ||
70 | IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1, | ||
71 | IPSET_ATTR_IP_FROM = IPSET_ATTR_IP, | ||
72 | IPSET_ATTR_IP_TO, /* 2 */ | ||
73 | IPSET_ATTR_CIDR, /* 3 */ | ||
74 | IPSET_ATTR_PORT, /* 4 */ | ||
75 | IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, | ||
76 | IPSET_ATTR_PORT_TO, /* 5 */ | ||
77 | IPSET_ATTR_TIMEOUT, /* 6 */ | ||
78 | IPSET_ATTR_PROTO, /* 7 */ | ||
79 | IPSET_ATTR_CADT_FLAGS, /* 8 */ | ||
80 | IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ | ||
81 | /* Reserve empty slots */ | ||
82 | IPSET_ATTR_CADT_MAX = 16, | ||
83 | /* Create-only specific attributes */ | ||
84 | IPSET_ATTR_GC, | ||
85 | IPSET_ATTR_HASHSIZE, | ||
86 | IPSET_ATTR_MAXELEM, | ||
87 | IPSET_ATTR_NETMASK, | ||
88 | IPSET_ATTR_PROBES, | ||
89 | IPSET_ATTR_RESIZE, | ||
90 | IPSET_ATTR_SIZE, | ||
91 | /* Kernel-only */ | ||
92 | IPSET_ATTR_ELEMENTS, | ||
93 | IPSET_ATTR_REFERENCES, | ||
94 | IPSET_ATTR_MEMSIZE, | ||
95 | |||
96 | __IPSET_ATTR_CREATE_MAX, | ||
97 | }; | ||
98 | #define IPSET_ATTR_CREATE_MAX (__IPSET_ATTR_CREATE_MAX - 1) | ||
99 | |||
100 | /* ADT specific attributes */ | ||
101 | enum { | ||
102 | IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1, | ||
103 | IPSET_ATTR_NAME, | ||
104 | IPSET_ATTR_NAMEREF, | ||
105 | IPSET_ATTR_IP2, | ||
106 | IPSET_ATTR_CIDR2, | ||
107 | __IPSET_ATTR_ADT_MAX, | ||
108 | }; | ||
109 | #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) | ||
110 | |||
111 | /* IP specific attributes */ | ||
112 | enum { | ||
113 | IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1, | ||
114 | IPSET_ATTR_IPADDR_IPV6, | ||
115 | __IPSET_ATTR_IPADDR_MAX, | ||
116 | }; | ||
117 | #define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1) | ||
118 | |||
119 | /* Error codes */ | ||
120 | enum ipset_errno { | ||
121 | IPSET_ERR_PRIVATE = 4096, | ||
122 | IPSET_ERR_PROTOCOL, | ||
123 | IPSET_ERR_FIND_TYPE, | ||
124 | IPSET_ERR_MAX_SETS, | ||
125 | IPSET_ERR_BUSY, | ||
126 | IPSET_ERR_EXIST_SETNAME2, | ||
127 | IPSET_ERR_TYPE_MISMATCH, | ||
128 | IPSET_ERR_EXIST, | ||
129 | IPSET_ERR_INVALID_CIDR, | ||
130 | IPSET_ERR_INVALID_NETMASK, | ||
131 | IPSET_ERR_INVALID_FAMILY, | ||
132 | IPSET_ERR_TIMEOUT, | ||
133 | IPSET_ERR_REFERENCED, | ||
134 | IPSET_ERR_IPADDR_IPV4, | ||
135 | IPSET_ERR_IPADDR_IPV6, | ||
136 | |||
137 | /* Type specific error codes */ | ||
138 | IPSET_ERR_TYPE_SPECIFIC = 4352, | ||
139 | }; | ||
140 | |||
141 | /* Flags at command level */ | ||
142 | enum ipset_cmd_flags { | ||
143 | IPSET_FLAG_BIT_EXIST = 0, | ||
144 | IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), | ||
145 | }; | ||
146 | |||
147 | /* Flags at CADT attribute level */ | ||
148 | enum ipset_cadt_flags { | ||
149 | IPSET_FLAG_BIT_BEFORE = 0, | ||
150 | IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), | ||
151 | }; | ||
152 | |||
153 | /* Commands with settype-specific attributes */ | ||
154 | enum ipset_adt { | ||
155 | IPSET_ADD, | ||
156 | IPSET_DEL, | ||
157 | IPSET_TEST, | ||
158 | IPSET_ADT_MAX, | ||
159 | IPSET_CREATE = IPSET_ADT_MAX, | ||
160 | IPSET_CADT_MAX, | ||
161 | }; | ||
162 | |||
163 | #ifdef __KERNEL__ | ||
164 | #include <linux/ip.h> | ||
165 | #include <linux/ipv6.h> | ||
166 | #include <linux/netlink.h> | ||
167 | #include <linux/netfilter.h> | ||
168 | #include <linux/vmalloc.h> | ||
169 | #include <net/netlink.h> | ||
170 | |||
171 | /* Sets are identified by an index in kernel space. Tweak with ip_set_id_t | ||
172 | * and IPSET_INVALID_ID if you want to increase the max number of sets. | ||
173 | */ | ||
174 | typedef u16 ip_set_id_t; | ||
175 | |||
176 | #define IPSET_INVALID_ID 65535 | ||
177 | |||
178 | enum ip_set_dim { | ||
179 | IPSET_DIM_ZERO = 0, | ||
180 | IPSET_DIM_ONE, | ||
181 | IPSET_DIM_TWO, | ||
182 | IPSET_DIM_THREE, | ||
183 | /* Max dimension in elements. | ||
184 | * If changed, new revision of iptables match/target is required. | ||
185 | */ | ||
186 | IPSET_DIM_MAX = 6, | ||
187 | }; | ||
188 | |||
189 | /* Option flags for kernel operations */ | ||
190 | enum ip_set_kopt { | ||
191 | IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO), | ||
192 | IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE), | ||
193 | IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO), | ||
194 | IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE), | ||
195 | }; | ||
196 | |||
197 | /* Set features */ | ||
198 | enum ip_set_feature { | ||
199 | IPSET_TYPE_IP_FLAG = 0, | ||
200 | IPSET_TYPE_IP = (1 << IPSET_TYPE_IP_FLAG), | ||
201 | IPSET_TYPE_PORT_FLAG = 1, | ||
202 | IPSET_TYPE_PORT = (1 << IPSET_TYPE_PORT_FLAG), | ||
203 | IPSET_TYPE_MAC_FLAG = 2, | ||
204 | IPSET_TYPE_MAC = (1 << IPSET_TYPE_MAC_FLAG), | ||
205 | IPSET_TYPE_IP2_FLAG = 3, | ||
206 | IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG), | ||
207 | IPSET_TYPE_NAME_FLAG = 4, | ||
208 | IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG), | ||
209 | /* Strictly speaking not a feature, but a flag for dumping: | ||
210 | * this settype must be dumped last */ | ||
211 | IPSET_DUMP_LAST_FLAG = 7, | ||
212 | IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG), | ||
213 | }; | ||
214 | |||
215 | struct ip_set; | ||
216 | |||
217 | typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout); | ||
218 | |||
219 | /* Set type, variant-specific part */ | ||
220 | struct ip_set_type_variant { | ||
221 | /* Kernelspace: test/add/del entries | ||
222 | * returns negative error code, | ||
223 | * zero for no match/success to add/delete | ||
224 | * positive for matching element */ | ||
225 | int (*kadt)(struct ip_set *set, const struct sk_buff * skb, | ||
226 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags); | ||
227 | |||
228 | /* Userspace: test/add/del entries | ||
229 | * returns negative error code, | ||
230 | * zero for no match/success to add/delete | ||
231 | * positive for matching element */ | ||
232 | int (*uadt)(struct ip_set *set, struct nlattr *tb[], | ||
233 | enum ipset_adt adt, u32 *lineno, u32 flags); | ||
234 | |||
235 | /* Low level add/del/test functions */ | ||
236 | ipset_adtfn adt[IPSET_ADT_MAX]; | ||
237 | |||
238 | /* When adding entries and set is full, try to resize the set */ | ||
239 | int (*resize)(struct ip_set *set, bool retried); | ||
240 | /* Destroy the set */ | ||
241 | void (*destroy)(struct ip_set *set); | ||
242 | /* Flush the elements */ | ||
243 | void (*flush)(struct ip_set *set); | ||
244 | /* Expire entries before listing */ | ||
245 | void (*expire)(struct ip_set *set); | ||
246 | /* List set header data */ | ||
247 | int (*head)(struct ip_set *set, struct sk_buff *skb); | ||
248 | /* List elements */ | ||
249 | int (*list)(const struct ip_set *set, struct sk_buff *skb, | ||
250 | struct netlink_callback *cb); | ||
251 | |||
252 | /* Return true if "b" set is the same as "a" | ||
253 | * according to the create set parameters */ | ||
254 | bool (*same_set)(const struct ip_set *a, const struct ip_set *b); | ||
255 | }; | ||
256 | |||
257 | /* The core set type structure */ | ||
258 | struct ip_set_type { | ||
259 | struct list_head list; | ||
260 | |||
261 | /* Typename */ | ||
262 | char name[IPSET_MAXNAMELEN]; | ||
263 | /* Protocol version */ | ||
264 | u8 protocol; | ||
265 | /* Set features to control swapping */ | ||
266 | u8 features; | ||
267 | /* Set type dimension */ | ||
268 | u8 dimension; | ||
269 | /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ | ||
270 | u8 family; | ||
271 | /* Type revision */ | ||
272 | u8 revision; | ||
273 | |||
274 | /* Create set */ | ||
275 | int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); | ||
276 | |||
277 | /* Attribute policies */ | ||
278 | const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1]; | ||
279 | const struct nla_policy adt_policy[IPSET_ATTR_ADT_MAX + 1]; | ||
280 | |||
281 | /* Set this to THIS_MODULE if you are a module, otherwise NULL */ | ||
282 | struct module *me; | ||
283 | }; | ||
284 | |||
285 | /* register and unregister set type */ | ||
286 | extern int ip_set_type_register(struct ip_set_type *set_type); | ||
287 | extern void ip_set_type_unregister(struct ip_set_type *set_type); | ||
288 | |||
289 | /* A generic IP set */ | ||
290 | struct ip_set { | ||
291 | /* The name of the set */ | ||
292 | char name[IPSET_MAXNAMELEN]; | ||
293 | /* Lock protecting the set data */ | ||
294 | rwlock_t lock; | ||
295 | /* References to the set */ | ||
296 | atomic_t ref; | ||
297 | /* The core set type */ | ||
298 | struct ip_set_type *type; | ||
299 | /* The type variant doing the real job */ | ||
300 | const struct ip_set_type_variant *variant; | ||
301 | /* The actual INET family of the set */ | ||
302 | u8 family; | ||
303 | /* The type specific data */ | ||
304 | void *data; | ||
305 | }; | ||
306 | |||
307 | /* register and unregister set references */ | ||
308 | extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); | ||
309 | extern void ip_set_put_byindex(ip_set_id_t index); | ||
310 | extern const char * ip_set_name_byindex(ip_set_id_t index); | ||
311 | extern ip_set_id_t ip_set_nfnl_get(const char *name); | ||
312 | extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); | ||
313 | extern void ip_set_nfnl_put(ip_set_id_t index); | ||
314 | |||
315 | /* API for iptables set match, and SET target */ | ||
316 | extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, | ||
317 | u8 family, u8 dim, u8 flags); | ||
318 | extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, | ||
319 | u8 family, u8 dim, u8 flags); | ||
320 | extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, | ||
321 | u8 family, u8 dim, u8 flags); | ||
322 | |||
323 | /* Utility functions */ | ||
324 | extern void * ip_set_alloc(size_t size); | ||
325 | extern void ip_set_free(void *members); | ||
326 | extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); | ||
327 | extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); | ||
328 | |||
329 | static inline int | ||
330 | ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr) | ||
331 | { | ||
332 | __be32 ip; | ||
333 | int ret = ip_set_get_ipaddr4(nla, &ip); | ||
334 | |||
335 | if (ret) | ||
336 | return ret; | ||
337 | *ipaddr = ntohl(ip); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | /* Ignore IPSET_ERR_EXIST errors if asked to do so? */ | ||
342 | static inline bool | ||
343 | ip_set_eexist(int ret, u32 flags) | ||
344 | { | ||
345 | return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST); | ||
346 | } | ||
347 | |||
348 | /* Check the NLA_F_NET_BYTEORDER flag */ | ||
349 | static inline bool | ||
350 | ip_set_attr_netorder(struct nlattr *tb[], int type) | ||
351 | { | ||
352 | return tb[type] && (tb[type]->nla_type & NLA_F_NET_BYTEORDER); | ||
353 | } | ||
354 | |||
355 | static inline bool | ||
356 | ip_set_optattr_netorder(struct nlattr *tb[], int type) | ||
357 | { | ||
358 | return !tb[type] || (tb[type]->nla_type & NLA_F_NET_BYTEORDER); | ||
359 | } | ||
360 | |||
361 | /* Useful converters */ | ||
362 | static inline u32 | ||
363 | ip_set_get_h32(const struct nlattr *attr) | ||
364 | { | ||
365 | return ntohl(nla_get_be32(attr)); | ||
366 | } | ||
367 | |||
368 | static inline u16 | ||
369 | ip_set_get_h16(const struct nlattr *attr) | ||
370 | { | ||
371 | return ntohs(nla_get_be16(attr)); | ||
372 | } | ||
373 | |||
374 | #define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED) | ||
375 | #define ipset_nest_end(skb, start) nla_nest_end(skb, start) | ||
376 | |||
377 | #define NLA_PUT_IPADDR4(skb, type, ipaddr) \ | ||
378 | do { \ | ||
379 | struct nlattr *__nested = ipset_nest_start(skb, type); \ | ||
380 | \ | ||
381 | if (!__nested) \ | ||
382 | goto nla_put_failure; \ | ||
383 | NLA_PUT_NET32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); \ | ||
384 | ipset_nest_end(skb, __nested); \ | ||
385 | } while (0) | ||
386 | |||
387 | #define NLA_PUT_IPADDR6(skb, type, ipaddrptr) \ | ||
388 | do { \ | ||
389 | struct nlattr *__nested = ipset_nest_start(skb, type); \ | ||
390 | \ | ||
391 | if (!__nested) \ | ||
392 | goto nla_put_failure; \ | ||
393 | NLA_PUT(skb, IPSET_ATTR_IPADDR_IPV6, \ | ||
394 | sizeof(struct in6_addr), ipaddrptr); \ | ||
395 | ipset_nest_end(skb, __nested); \ | ||
396 | } while (0) | ||
397 | |||
398 | /* Get address from skbuff */ | ||
399 | static inline __be32 | ||
400 | ip4addr(const struct sk_buff *skb, bool src) | ||
401 | { | ||
402 | return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; | ||
403 | } | ||
404 | |||
405 | static inline void | ||
406 | ip4addrptr(const struct sk_buff *skb, bool src, __be32 *addr) | ||
407 | { | ||
408 | *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; | ||
409 | } | ||
410 | |||
411 | static inline void | ||
412 | ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) | ||
413 | { | ||
414 | memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr, | ||
415 | sizeof(*addr)); | ||
416 | } | ||
417 | |||
418 | /* Calculate the bytes required to store the inclusive range of a-b */ | ||
419 | static inline int | ||
420 | bitmap_bytes(u32 a, u32 b) | ||
421 | { | ||
422 | return 4 * ((((b - a + 8) / 8) + 3) / 4); | ||
423 | } | ||
424 | |||
425 | /* Interface to iptables/ip6tables */ | ||
426 | |||
427 | #define SO_IP_SET 83 | ||
428 | |||
429 | union ip_set_name_index { | ||
430 | char name[IPSET_MAXNAMELEN]; | ||
431 | ip_set_id_t index; | ||
432 | }; | ||
433 | |||
434 | #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ | ||
435 | struct ip_set_req_get_set { | ||
436 | unsigned op; | ||
437 | unsigned version; | ||
438 | union ip_set_name_index set; | ||
439 | }; | ||
440 | |||
441 | #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ | ||
442 | /* Uses ip_set_req_get_set */ | ||
443 | |||
444 | #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ | ||
445 | struct ip_set_req_version { | ||
446 | unsigned op; | ||
447 | unsigned version; | ||
448 | }; | ||
449 | |||
450 | #endif /* __KERNEL__ */ | ||
451 | |||
452 | #endif /*_IP_SET_H */ | ||
diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h new file mode 100644 index 000000000000..ec9d9bea1e37 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_ahash.h | |||
@@ -0,0 +1,1074 @@ | |||
1 | #ifndef _IP_SET_AHASH_H | ||
2 | #define _IP_SET_AHASH_H | ||
3 | |||
4 | #include <linux/rcupdate.h> | ||
5 | #include <linux/jhash.h> | ||
6 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
7 | |||
8 | /* Hashing which uses arrays to resolve clashing. The hash table is resized | ||
9 | * (doubled) when searching becomes too long. | ||
10 | * Internally jhash is used with the assumption that the size of the | ||
11 | * stored data is a multiple of sizeof(u32). If storage supports timeout, | ||
12 | * the timeout field must be the last one in the data structure - that field | ||
13 | * is ignored when computing the hash key. | ||
14 | * | ||
15 | * Readers and resizing | ||
16 | * | ||
17 | * Resizing can be triggered by userspace command only, and those | ||
18 | * are serialized by the nfnl mutex. During resizing the set is | ||
19 | * read-locked, so the only possible concurrent operations are | ||
20 | * the kernel side readers. Those must be protected by proper RCU locking. | ||
21 | */ | ||
22 | |||
23 | /* Number of elements to store in an initial array block */ | ||
24 | #define AHASH_INIT_SIZE 4 | ||
25 | /* Max number of elements to store in an array block */ | ||
26 | #define AHASH_MAX_SIZE (3*4) | ||
27 | |||
28 | /* A hash bucket */ | ||
29 | struct hbucket { | ||
30 | void *value; /* the array of the values */ | ||
31 | u8 size; /* size of the array */ | ||
32 | u8 pos; /* position of the first free entry */ | ||
33 | }; | ||
34 | |||
35 | /* The hash table: the table size stored here in order to make resizing easy */ | ||
36 | struct htable { | ||
37 | u8 htable_bits; /* size of hash table == 2^htable_bits */ | ||
38 | struct hbucket bucket[0]; /* hashtable buckets */ | ||
39 | }; | ||
40 | |||
41 | #define hbucket(h, i) &((h)->bucket[i]) | ||
42 | |||
43 | /* Book-keeping of the prefixes added to the set */ | ||
44 | struct ip_set_hash_nets { | ||
45 | u8 cidr; /* the different cidr values in the set */ | ||
46 | u32 nets; /* number of elements per cidr */ | ||
47 | }; | ||
48 | |||
49 | /* The generic ip_set hash structure */ | ||
50 | struct ip_set_hash { | ||
51 | struct htable *table; /* the hash table */ | ||
52 | u32 maxelem; /* max elements in the hash */ | ||
53 | u32 elements; /* current element (vs timeout) */ | ||
54 | u32 initval; /* random jhash init value */ | ||
55 | u32 timeout; /* timeout value, if enabled */ | ||
56 | struct timer_list gc; /* garbage collection when timeout enabled */ | ||
57 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
58 | u8 netmask; /* netmask value for subnets to store */ | ||
59 | #endif | ||
60 | #ifdef IP_SET_HASH_WITH_NETS | ||
61 | struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */ | ||
62 | #endif | ||
63 | }; | ||
64 | |||
65 | /* Compute htable_bits from the user input parameter hashsize */ | ||
66 | static u8 | ||
67 | htable_bits(u32 hashsize) | ||
68 | { | ||
69 | /* Assume that hashsize == 2^htable_bits */ | ||
70 | u8 bits = fls(hashsize - 1); | ||
71 | if (jhash_size(bits) != hashsize) | ||
72 | /* Round up to the first 2^n value */ | ||
73 | bits = fls(hashsize); | ||
74 | |||
75 | return bits; | ||
76 | } | ||
77 | |||
78 | #ifdef IP_SET_HASH_WITH_NETS | ||
79 | |||
80 | #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) | ||
81 | |||
82 | /* Network cidr size book keeping when the hash stores different | ||
83 | * sized networks */ | ||
84 | static void | ||
85 | add_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask) | ||
86 | { | ||
87 | u8 i; | ||
88 | |||
89 | ++h->nets[cidr-1].nets; | ||
90 | |||
91 | pr_debug("add_cidr added %u: %u\n", cidr, h->nets[cidr-1].nets); | ||
92 | |||
93 | if (h->nets[cidr-1].nets > 1) | ||
94 | return; | ||
95 | |||
96 | /* New cidr size */ | ||
97 | for (i = 0; i < host_mask && h->nets[i].cidr; i++) { | ||
98 | /* Add in increasing prefix order, so larger cidr first */ | ||
99 | if (h->nets[i].cidr < cidr) | ||
100 | swap(h->nets[i].cidr, cidr); | ||
101 | } | ||
102 | if (i < host_mask) | ||
103 | h->nets[i].cidr = cidr; | ||
104 | } | ||
105 | |||
106 | static void | ||
107 | del_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask) | ||
108 | { | ||
109 | u8 i; | ||
110 | |||
111 | --h->nets[cidr-1].nets; | ||
112 | |||
113 | pr_debug("del_cidr deleted %u: %u\n", cidr, h->nets[cidr-1].nets); | ||
114 | |||
115 | if (h->nets[cidr-1].nets != 0) | ||
116 | return; | ||
117 | |||
118 | /* All entries with this cidr size deleted, so cleanup h->cidr[] */ | ||
119 | for (i = 0; i < host_mask - 1 && h->nets[i].cidr; i++) { | ||
120 | if (h->nets[i].cidr == cidr) | ||
121 | h->nets[i].cidr = cidr = h->nets[i+1].cidr; | ||
122 | } | ||
123 | h->nets[i - 1].cidr = 0; | ||
124 | } | ||
125 | #endif | ||
126 | |||
127 | /* Destroy the hashtable part of the set */ | ||
128 | static void | ||
129 | ahash_destroy(struct htable *t) | ||
130 | { | ||
131 | struct hbucket *n; | ||
132 | u32 i; | ||
133 | |||
134 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
135 | n = hbucket(t, i); | ||
136 | if (n->size) | ||
137 | /* FIXME: use slab cache */ | ||
138 | kfree(n->value); | ||
139 | } | ||
140 | |||
141 | ip_set_free(t); | ||
142 | } | ||
143 | |||
144 | /* Calculate the actual memory size of the set data */ | ||
145 | static size_t | ||
146 | ahash_memsize(const struct ip_set_hash *h, size_t dsize, u8 host_mask) | ||
147 | { | ||
148 | u32 i; | ||
149 | struct htable *t = h->table; | ||
150 | size_t memsize = sizeof(*h) | ||
151 | + sizeof(*t) | ||
152 | #ifdef IP_SET_HASH_WITH_NETS | ||
153 | + sizeof(struct ip_set_hash_nets) * host_mask | ||
154 | #endif | ||
155 | + jhash_size(t->htable_bits) * sizeof(struct hbucket); | ||
156 | |||
157 | for (i = 0; i < jhash_size(t->htable_bits); i++) | ||
158 | memsize += t->bucket[i].size * dsize; | ||
159 | |||
160 | return memsize; | ||
161 | } | ||
162 | |||
163 | /* Flush a hash type of set: destroy all elements */ | ||
164 | static void | ||
165 | ip_set_hash_flush(struct ip_set *set) | ||
166 | { | ||
167 | struct ip_set_hash *h = set->data; | ||
168 | struct htable *t = h->table; | ||
169 | struct hbucket *n; | ||
170 | u32 i; | ||
171 | |||
172 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
173 | n = hbucket(t, i); | ||
174 | if (n->size) { | ||
175 | n->size = n->pos = 0; | ||
176 | /* FIXME: use slab cache */ | ||
177 | kfree(n->value); | ||
178 | } | ||
179 | } | ||
180 | #ifdef IP_SET_HASH_WITH_NETS | ||
181 | memset(h->nets, 0, sizeof(struct ip_set_hash_nets) | ||
182 | * SET_HOST_MASK(set->family)); | ||
183 | #endif | ||
184 | h->elements = 0; | ||
185 | } | ||
186 | |||
187 | /* Destroy a hash type of set */ | ||
188 | static void | ||
189 | ip_set_hash_destroy(struct ip_set *set) | ||
190 | { | ||
191 | struct ip_set_hash *h = set->data; | ||
192 | |||
193 | if (with_timeout(h->timeout)) | ||
194 | del_timer_sync(&h->gc); | ||
195 | |||
196 | ahash_destroy(h->table); | ||
197 | kfree(h); | ||
198 | |||
199 | set->data = NULL; | ||
200 | } | ||
201 | |||
202 | #define HKEY(data, initval, htable_bits) \ | ||
203 | (jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ | ||
204 | & jhash_mask(htable_bits)) | ||
205 | |||
206 | #endif /* _IP_SET_AHASH_H */ | ||
207 | |||
208 | #define CONCAT(a, b, c) a##b##c | ||
209 | #define TOKEN(a, b, c) CONCAT(a, b, c) | ||
210 | |||
211 | /* Type/family dependent function prototypes */ | ||
212 | |||
213 | #define type_pf_data_equal TOKEN(TYPE, PF, _data_equal) | ||
214 | #define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull) | ||
215 | #define type_pf_data_copy TOKEN(TYPE, PF, _data_copy) | ||
216 | #define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out) | ||
217 | #define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) | ||
218 | #define type_pf_data_list TOKEN(TYPE, PF, _data_list) | ||
219 | #define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) | ||
220 | |||
221 | #define type_pf_elem TOKEN(TYPE, PF, _elem) | ||
222 | #define type_pf_telem TOKEN(TYPE, PF, _telem) | ||
223 | #define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout) | ||
224 | #define type_pf_data_expired TOKEN(TYPE, PF, _data_expired) | ||
225 | #define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set) | ||
226 | |||
227 | #define type_pf_elem_add TOKEN(TYPE, PF, _elem_add) | ||
228 | #define type_pf_add TOKEN(TYPE, PF, _add) | ||
229 | #define type_pf_del TOKEN(TYPE, PF, _del) | ||
230 | #define type_pf_test_cidrs TOKEN(TYPE, PF, _test_cidrs) | ||
231 | #define type_pf_test TOKEN(TYPE, PF, _test) | ||
232 | |||
233 | #define type_pf_elem_tadd TOKEN(TYPE, PF, _elem_tadd) | ||
234 | #define type_pf_del_telem TOKEN(TYPE, PF, _ahash_del_telem) | ||
235 | #define type_pf_expire TOKEN(TYPE, PF, _expire) | ||
236 | #define type_pf_tadd TOKEN(TYPE, PF, _tadd) | ||
237 | #define type_pf_tdel TOKEN(TYPE, PF, _tdel) | ||
238 | #define type_pf_ttest_cidrs TOKEN(TYPE, PF, _ahash_ttest_cidrs) | ||
239 | #define type_pf_ttest TOKEN(TYPE, PF, _ahash_ttest) | ||
240 | |||
241 | #define type_pf_resize TOKEN(TYPE, PF, _resize) | ||
242 | #define type_pf_tresize TOKEN(TYPE, PF, _tresize) | ||
243 | #define type_pf_flush ip_set_hash_flush | ||
244 | #define type_pf_destroy ip_set_hash_destroy | ||
245 | #define type_pf_head TOKEN(TYPE, PF, _head) | ||
246 | #define type_pf_list TOKEN(TYPE, PF, _list) | ||
247 | #define type_pf_tlist TOKEN(TYPE, PF, _tlist) | ||
248 | #define type_pf_same_set TOKEN(TYPE, PF, _same_set) | ||
249 | #define type_pf_kadt TOKEN(TYPE, PF, _kadt) | ||
250 | #define type_pf_uadt TOKEN(TYPE, PF, _uadt) | ||
251 | #define type_pf_gc TOKEN(TYPE, PF, _gc) | ||
252 | #define type_pf_gc_init TOKEN(TYPE, PF, _gc_init) | ||
253 | #define type_pf_variant TOKEN(TYPE, PF, _variant) | ||
254 | #define type_pf_tvariant TOKEN(TYPE, PF, _tvariant) | ||
255 | |||
256 | /* Flavour without timeout */ | ||
257 | |||
258 | /* Get the ith element from the array block n */ | ||
259 | #define ahash_data(n, i) \ | ||
260 | ((struct type_pf_elem *)((n)->value) + (i)) | ||
261 | |||
262 | /* Add an element to the hash table when resizing the set: | ||
263 | * we spare the maintenance of the internal counters. */ | ||
264 | static int | ||
265 | type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value) | ||
266 | { | ||
267 | if (n->pos >= n->size) { | ||
268 | void *tmp; | ||
269 | |||
270 | if (n->size >= AHASH_MAX_SIZE) | ||
271 | /* Trigger rehashing */ | ||
272 | return -EAGAIN; | ||
273 | |||
274 | tmp = kzalloc((n->size + AHASH_INIT_SIZE) | ||
275 | * sizeof(struct type_pf_elem), | ||
276 | GFP_ATOMIC); | ||
277 | if (!tmp) | ||
278 | return -ENOMEM; | ||
279 | if (n->size) { | ||
280 | memcpy(tmp, n->value, | ||
281 | sizeof(struct type_pf_elem) * n->size); | ||
282 | kfree(n->value); | ||
283 | } | ||
284 | n->value = tmp; | ||
285 | n->size += AHASH_INIT_SIZE; | ||
286 | } | ||
287 | type_pf_data_copy(ahash_data(n, n->pos++), value); | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* Resize a hash: create a new hash table with doubling the hashsize | ||
292 | * and inserting the elements to it. Repeat until we succeed or | ||
293 | * fail due to memory pressures. */ | ||
294 | static int | ||
295 | type_pf_resize(struct ip_set *set, bool retried) | ||
296 | { | ||
297 | struct ip_set_hash *h = set->data; | ||
298 | struct htable *t, *orig = h->table; | ||
299 | u8 htable_bits = orig->htable_bits; | ||
300 | const struct type_pf_elem *data; | ||
301 | struct hbucket *n, *m; | ||
302 | u32 i, j; | ||
303 | int ret; | ||
304 | |||
305 | retry: | ||
306 | ret = 0; | ||
307 | htable_bits++; | ||
308 | pr_debug("attempt to resize set %s from %u to %u, t %p\n", | ||
309 | set->name, orig->htable_bits, htable_bits, orig); | ||
310 | if (!htable_bits) | ||
311 | /* In case we have plenty of memory :-) */ | ||
312 | return -IPSET_ERR_HASH_FULL; | ||
313 | t = ip_set_alloc(sizeof(*t) | ||
314 | + jhash_size(htable_bits) * sizeof(struct hbucket)); | ||
315 | if (!t) | ||
316 | return -ENOMEM; | ||
317 | t->htable_bits = htable_bits; | ||
318 | |||
319 | read_lock_bh(&set->lock); | ||
320 | for (i = 0; i < jhash_size(orig->htable_bits); i++) { | ||
321 | n = hbucket(orig, i); | ||
322 | for (j = 0; j < n->pos; j++) { | ||
323 | data = ahash_data(n, j); | ||
324 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); | ||
325 | ret = type_pf_elem_add(m, data); | ||
326 | if (ret < 0) { | ||
327 | read_unlock_bh(&set->lock); | ||
328 | ahash_destroy(t); | ||
329 | if (ret == -EAGAIN) | ||
330 | goto retry; | ||
331 | return ret; | ||
332 | } | ||
333 | } | ||
334 | } | ||
335 | |||
336 | rcu_assign_pointer(h->table, t); | ||
337 | read_unlock_bh(&set->lock); | ||
338 | |||
339 | /* Give time to other readers of the set */ | ||
340 | synchronize_rcu_bh(); | ||
341 | |||
342 | pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, | ||
343 | orig->htable_bits, orig, t->htable_bits, t); | ||
344 | ahash_destroy(orig); | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | /* Add an element to a hash and update the internal counters when succeeded, | ||
350 | * otherwise report the proper error code. */ | ||
351 | static int | ||
352 | type_pf_add(struct ip_set *set, void *value, u32 timeout) | ||
353 | { | ||
354 | struct ip_set_hash *h = set->data; | ||
355 | struct htable *t; | ||
356 | const struct type_pf_elem *d = value; | ||
357 | struct hbucket *n; | ||
358 | int i, ret = 0; | ||
359 | u32 key; | ||
360 | |||
361 | if (h->elements >= h->maxelem) | ||
362 | return -IPSET_ERR_HASH_FULL; | ||
363 | |||
364 | rcu_read_lock_bh(); | ||
365 | t = rcu_dereference_bh(h->table); | ||
366 | key = HKEY(value, h->initval, t->htable_bits); | ||
367 | n = hbucket(t, key); | ||
368 | for (i = 0; i < n->pos; i++) | ||
369 | if (type_pf_data_equal(ahash_data(n, i), d)) { | ||
370 | ret = -IPSET_ERR_EXIST; | ||
371 | goto out; | ||
372 | } | ||
373 | |||
374 | ret = type_pf_elem_add(n, value); | ||
375 | if (ret != 0) | ||
376 | goto out; | ||
377 | |||
378 | #ifdef IP_SET_HASH_WITH_NETS | ||
379 | add_cidr(h, d->cidr, HOST_MASK); | ||
380 | #endif | ||
381 | h->elements++; | ||
382 | out: | ||
383 | rcu_read_unlock_bh(); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | /* Delete an element from the hash: swap it with the last element | ||
388 | * and free up space if possible. | ||
389 | */ | ||
390 | static int | ||
391 | type_pf_del(struct ip_set *set, void *value, u32 timeout) | ||
392 | { | ||
393 | struct ip_set_hash *h = set->data; | ||
394 | struct htable *t = h->table; | ||
395 | const struct type_pf_elem *d = value; | ||
396 | struct hbucket *n; | ||
397 | int i; | ||
398 | struct type_pf_elem *data; | ||
399 | u32 key; | ||
400 | |||
401 | key = HKEY(value, h->initval, t->htable_bits); | ||
402 | n = hbucket(t, key); | ||
403 | for (i = 0; i < n->pos; i++) { | ||
404 | data = ahash_data(n, i); | ||
405 | if (!type_pf_data_equal(data, d)) | ||
406 | continue; | ||
407 | if (i != n->pos - 1) | ||
408 | /* Not last one */ | ||
409 | type_pf_data_copy(data, ahash_data(n, n->pos - 1)); | ||
410 | |||
411 | n->pos--; | ||
412 | h->elements--; | ||
413 | #ifdef IP_SET_HASH_WITH_NETS | ||
414 | del_cidr(h, d->cidr, HOST_MASK); | ||
415 | #endif | ||
416 | if (n->pos + AHASH_INIT_SIZE < n->size) { | ||
417 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | ||
418 | * sizeof(struct type_pf_elem), | ||
419 | GFP_ATOMIC); | ||
420 | if (!tmp) | ||
421 | return 0; | ||
422 | n->size -= AHASH_INIT_SIZE; | ||
423 | memcpy(tmp, n->value, | ||
424 | n->size * sizeof(struct type_pf_elem)); | ||
425 | kfree(n->value); | ||
426 | n->value = tmp; | ||
427 | } | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | return -IPSET_ERR_EXIST; | ||
432 | } | ||
433 | |||
434 | #ifdef IP_SET_HASH_WITH_NETS | ||
435 | |||
436 | /* Special test function which takes into account the different network | ||
437 | * sizes added to the set */ | ||
438 | static int | ||
439 | type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) | ||
440 | { | ||
441 | struct ip_set_hash *h = set->data; | ||
442 | struct htable *t = h->table; | ||
443 | struct hbucket *n; | ||
444 | const struct type_pf_elem *data; | ||
445 | int i, j = 0; | ||
446 | u32 key; | ||
447 | u8 host_mask = SET_HOST_MASK(set->family); | ||
448 | |||
449 | pr_debug("test by nets\n"); | ||
450 | for (; j < host_mask && h->nets[j].cidr; j++) { | ||
451 | type_pf_data_netmask(d, h->nets[j].cidr); | ||
452 | key = HKEY(d, h->initval, t->htable_bits); | ||
453 | n = hbucket(t, key); | ||
454 | for (i = 0; i < n->pos; i++) { | ||
455 | data = ahash_data(n, i); | ||
456 | if (type_pf_data_equal(data, d)) | ||
457 | return 1; | ||
458 | } | ||
459 | } | ||
460 | return 0; | ||
461 | } | ||
462 | #endif | ||
463 | |||
464 | /* Test whether the element is added to the set */ | ||
465 | static int | ||
466 | type_pf_test(struct ip_set *set, void *value, u32 timeout) | ||
467 | { | ||
468 | struct ip_set_hash *h = set->data; | ||
469 | struct htable *t = h->table; | ||
470 | struct type_pf_elem *d = value; | ||
471 | struct hbucket *n; | ||
472 | const struct type_pf_elem *data; | ||
473 | int i; | ||
474 | u32 key; | ||
475 | |||
476 | #ifdef IP_SET_HASH_WITH_NETS | ||
477 | /* If we test an IP address and not a network address, | ||
478 | * try all possible network sizes */ | ||
479 | if (d->cidr == SET_HOST_MASK(set->family)) | ||
480 | return type_pf_test_cidrs(set, d, timeout); | ||
481 | #endif | ||
482 | |||
483 | key = HKEY(d, h->initval, t->htable_bits); | ||
484 | n = hbucket(t, key); | ||
485 | for (i = 0; i < n->pos; i++) { | ||
486 | data = ahash_data(n, i); | ||
487 | if (type_pf_data_equal(data, d)) | ||
488 | return 1; | ||
489 | } | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | /* Reply a HEADER request: fill out the header part of the set */ | ||
494 | static int | ||
495 | type_pf_head(struct ip_set *set, struct sk_buff *skb) | ||
496 | { | ||
497 | const struct ip_set_hash *h = set->data; | ||
498 | struct nlattr *nested; | ||
499 | size_t memsize; | ||
500 | |||
501 | read_lock_bh(&set->lock); | ||
502 | memsize = ahash_memsize(h, with_timeout(h->timeout) | ||
503 | ? sizeof(struct type_pf_telem) | ||
504 | : sizeof(struct type_pf_elem), | ||
505 | set->family == AF_INET ? 32 : 128); | ||
506 | read_unlock_bh(&set->lock); | ||
507 | |||
508 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
509 | if (!nested) | ||
510 | goto nla_put_failure; | ||
511 | NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, | ||
512 | htonl(jhash_size(h->table->htable_bits))); | ||
513 | NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)); | ||
514 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
515 | if (h->netmask != HOST_MASK) | ||
516 | NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); | ||
517 | #endif | ||
518 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | ||
519 | htonl(atomic_read(&set->ref) - 1)); | ||
520 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)); | ||
521 | if (with_timeout(h->timeout)) | ||
522 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)); | ||
523 | ipset_nest_end(skb, nested); | ||
524 | |||
525 | return 0; | ||
526 | nla_put_failure: | ||
527 | return -EMSGSIZE; | ||
528 | } | ||
529 | |||
530 | /* Reply a LIST/SAVE request: dump the elements of the specified set */ | ||
531 | static int | ||
532 | type_pf_list(const struct ip_set *set, | ||
533 | struct sk_buff *skb, struct netlink_callback *cb) | ||
534 | { | ||
535 | const struct ip_set_hash *h = set->data; | ||
536 | const struct htable *t = h->table; | ||
537 | struct nlattr *atd, *nested; | ||
538 | const struct hbucket *n; | ||
539 | const struct type_pf_elem *data; | ||
540 | u32 first = cb->args[2]; | ||
541 | /* We assume that one hash bucket fills into one page */ | ||
542 | void *incomplete; | ||
543 | int i; | ||
544 | |||
545 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
546 | if (!atd) | ||
547 | return -EMSGSIZE; | ||
548 | pr_debug("list hash set %s\n", set->name); | ||
549 | for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { | ||
550 | incomplete = skb_tail_pointer(skb); | ||
551 | n = hbucket(t, cb->args[2]); | ||
552 | pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); | ||
553 | for (i = 0; i < n->pos; i++) { | ||
554 | data = ahash_data(n, i); | ||
555 | pr_debug("list hash %lu hbucket %p i %u, data %p\n", | ||
556 | cb->args[2], n, i, data); | ||
557 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
558 | if (!nested) { | ||
559 | if (cb->args[2] == first) { | ||
560 | nla_nest_cancel(skb, atd); | ||
561 | return -EMSGSIZE; | ||
562 | } else | ||
563 | goto nla_put_failure; | ||
564 | } | ||
565 | if (type_pf_data_list(skb, data)) | ||
566 | goto nla_put_failure; | ||
567 | ipset_nest_end(skb, nested); | ||
568 | } | ||
569 | } | ||
570 | ipset_nest_end(skb, atd); | ||
571 | /* Set listing finished */ | ||
572 | cb->args[2] = 0; | ||
573 | |||
574 | return 0; | ||
575 | |||
576 | nla_put_failure: | ||
577 | nlmsg_trim(skb, incomplete); | ||
578 | ipset_nest_end(skb, atd); | ||
579 | if (unlikely(first == cb->args[2])) { | ||
580 | pr_warning("Can't list set %s: one bucket does not fit into " | ||
581 | "a message. Please report it!\n", set->name); | ||
582 | cb->args[2] = 0; | ||
583 | return -EMSGSIZE; | ||
584 | } | ||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static int | ||
589 | type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, | ||
590 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags); | ||
591 | static int | ||
592 | type_pf_uadt(struct ip_set *set, struct nlattr *tb[], | ||
593 | enum ipset_adt adt, u32 *lineno, u32 flags); | ||
594 | |||
595 | static const struct ip_set_type_variant type_pf_variant = { | ||
596 | .kadt = type_pf_kadt, | ||
597 | .uadt = type_pf_uadt, | ||
598 | .adt = { | ||
599 | [IPSET_ADD] = type_pf_add, | ||
600 | [IPSET_DEL] = type_pf_del, | ||
601 | [IPSET_TEST] = type_pf_test, | ||
602 | }, | ||
603 | .destroy = type_pf_destroy, | ||
604 | .flush = type_pf_flush, | ||
605 | .head = type_pf_head, | ||
606 | .list = type_pf_list, | ||
607 | .resize = type_pf_resize, | ||
608 | .same_set = type_pf_same_set, | ||
609 | }; | ||
610 | |||
611 | /* Flavour with timeout support */ | ||
612 | |||
613 | #define ahash_tdata(n, i) \ | ||
614 | (struct type_pf_elem *)((struct type_pf_telem *)((n)->value) + (i)) | ||
615 | |||
616 | static inline u32 | ||
617 | type_pf_data_timeout(const struct type_pf_elem *data) | ||
618 | { | ||
619 | const struct type_pf_telem *tdata = | ||
620 | (const struct type_pf_telem *) data; | ||
621 | |||
622 | return tdata->timeout; | ||
623 | } | ||
624 | |||
625 | static inline bool | ||
626 | type_pf_data_expired(const struct type_pf_elem *data) | ||
627 | { | ||
628 | const struct type_pf_telem *tdata = | ||
629 | (const struct type_pf_telem *) data; | ||
630 | |||
631 | return ip_set_timeout_expired(tdata->timeout); | ||
632 | } | ||
633 | |||
634 | static inline void | ||
635 | type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) | ||
636 | { | ||
637 | struct type_pf_telem *tdata = (struct type_pf_telem *) data; | ||
638 | |||
639 | tdata->timeout = ip_set_timeout_set(timeout); | ||
640 | } | ||
641 | |||
642 | static int | ||
643 | type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, | ||
644 | u32 timeout) | ||
645 | { | ||
646 | struct type_pf_elem *data; | ||
647 | |||
648 | if (n->pos >= n->size) { | ||
649 | void *tmp; | ||
650 | |||
651 | if (n->size >= AHASH_MAX_SIZE) | ||
652 | /* Trigger rehashing */ | ||
653 | return -EAGAIN; | ||
654 | |||
655 | tmp = kzalloc((n->size + AHASH_INIT_SIZE) | ||
656 | * sizeof(struct type_pf_telem), | ||
657 | GFP_ATOMIC); | ||
658 | if (!tmp) | ||
659 | return -ENOMEM; | ||
660 | if (n->size) { | ||
661 | memcpy(tmp, n->value, | ||
662 | sizeof(struct type_pf_telem) * n->size); | ||
663 | kfree(n->value); | ||
664 | } | ||
665 | n->value = tmp; | ||
666 | n->size += AHASH_INIT_SIZE; | ||
667 | } | ||
668 | data = ahash_tdata(n, n->pos++); | ||
669 | type_pf_data_copy(data, value); | ||
670 | type_pf_data_timeout_set(data, timeout); | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | /* Delete expired elements from the hashtable */ | ||
675 | static void | ||
676 | type_pf_expire(struct ip_set_hash *h) | ||
677 | { | ||
678 | struct htable *t = h->table; | ||
679 | struct hbucket *n; | ||
680 | struct type_pf_elem *data; | ||
681 | u32 i; | ||
682 | int j; | ||
683 | |||
684 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
685 | n = hbucket(t, i); | ||
686 | for (j = 0; j < n->pos; j++) { | ||
687 | data = ahash_tdata(n, j); | ||
688 | if (type_pf_data_expired(data)) { | ||
689 | pr_debug("expired %u/%u\n", i, j); | ||
690 | #ifdef IP_SET_HASH_WITH_NETS | ||
691 | del_cidr(h, data->cidr, HOST_MASK); | ||
692 | #endif | ||
693 | if (j != n->pos - 1) | ||
694 | /* Not last one */ | ||
695 | type_pf_data_copy(data, | ||
696 | ahash_tdata(n, n->pos - 1)); | ||
697 | n->pos--; | ||
698 | h->elements--; | ||
699 | } | ||
700 | } | ||
701 | if (n->pos + AHASH_INIT_SIZE < n->size) { | ||
702 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | ||
703 | * sizeof(struct type_pf_telem), | ||
704 | GFP_ATOMIC); | ||
705 | if (!tmp) | ||
706 | /* Still try to delete expired elements */ | ||
707 | continue; | ||
708 | n->size -= AHASH_INIT_SIZE; | ||
709 | memcpy(tmp, n->value, | ||
710 | n->size * sizeof(struct type_pf_telem)); | ||
711 | kfree(n->value); | ||
712 | n->value = tmp; | ||
713 | } | ||
714 | } | ||
715 | } | ||
716 | |||
717 | static int | ||
718 | type_pf_tresize(struct ip_set *set, bool retried) | ||
719 | { | ||
720 | struct ip_set_hash *h = set->data; | ||
721 | struct htable *t, *orig = h->table; | ||
722 | u8 htable_bits = orig->htable_bits; | ||
723 | const struct type_pf_elem *data; | ||
724 | struct hbucket *n, *m; | ||
725 | u32 i, j; | ||
726 | int ret; | ||
727 | |||
728 | /* Try to cleanup once */ | ||
729 | if (!retried) { | ||
730 | i = h->elements; | ||
731 | write_lock_bh(&set->lock); | ||
732 | type_pf_expire(set->data); | ||
733 | write_unlock_bh(&set->lock); | ||
734 | if (h->elements < i) | ||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | retry: | ||
739 | ret = 0; | ||
740 | htable_bits++; | ||
741 | if (!htable_bits) | ||
742 | /* In case we have plenty of memory :-) */ | ||
743 | return -IPSET_ERR_HASH_FULL; | ||
744 | t = ip_set_alloc(sizeof(*t) | ||
745 | + jhash_size(htable_bits) * sizeof(struct hbucket)); | ||
746 | if (!t) | ||
747 | return -ENOMEM; | ||
748 | t->htable_bits = htable_bits; | ||
749 | |||
750 | read_lock_bh(&set->lock); | ||
751 | for (i = 0; i < jhash_size(orig->htable_bits); i++) { | ||
752 | n = hbucket(orig, i); | ||
753 | for (j = 0; j < n->pos; j++) { | ||
754 | data = ahash_tdata(n, j); | ||
755 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); | ||
756 | ret = type_pf_elem_tadd(m, data, | ||
757 | type_pf_data_timeout(data)); | ||
758 | if (ret < 0) { | ||
759 | read_unlock_bh(&set->lock); | ||
760 | ahash_destroy(t); | ||
761 | if (ret == -EAGAIN) | ||
762 | goto retry; | ||
763 | return ret; | ||
764 | } | ||
765 | } | ||
766 | } | ||
767 | |||
768 | rcu_assign_pointer(h->table, t); | ||
769 | read_unlock_bh(&set->lock); | ||
770 | |||
771 | /* Give time to other readers of the set */ | ||
772 | synchronize_rcu_bh(); | ||
773 | |||
774 | ahash_destroy(orig); | ||
775 | |||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int | ||
780 | type_pf_tadd(struct ip_set *set, void *value, u32 timeout) | ||
781 | { | ||
782 | struct ip_set_hash *h = set->data; | ||
783 | struct htable *t = h->table; | ||
784 | const struct type_pf_elem *d = value; | ||
785 | struct hbucket *n; | ||
786 | struct type_pf_elem *data; | ||
787 | int ret = 0, i, j = AHASH_MAX_SIZE + 1; | ||
788 | u32 key; | ||
789 | |||
790 | if (h->elements >= h->maxelem) | ||
791 | /* FIXME: when set is full, we slow down here */ | ||
792 | type_pf_expire(h); | ||
793 | if (h->elements >= h->maxelem) | ||
794 | return -IPSET_ERR_HASH_FULL; | ||
795 | |||
796 | rcu_read_lock_bh(); | ||
797 | t = rcu_dereference_bh(h->table); | ||
798 | key = HKEY(d, h->initval, t->htable_bits); | ||
799 | n = hbucket(t, key); | ||
800 | for (i = 0; i < n->pos; i++) { | ||
801 | data = ahash_tdata(n, i); | ||
802 | if (type_pf_data_equal(data, d)) { | ||
803 | if (type_pf_data_expired(data)) | ||
804 | j = i; | ||
805 | else { | ||
806 | ret = -IPSET_ERR_EXIST; | ||
807 | goto out; | ||
808 | } | ||
809 | } else if (j == AHASH_MAX_SIZE + 1 && | ||
810 | type_pf_data_expired(data)) | ||
811 | j = i; | ||
812 | } | ||
813 | if (j != AHASH_MAX_SIZE + 1) { | ||
814 | data = ahash_tdata(n, j); | ||
815 | #ifdef IP_SET_HASH_WITH_NETS | ||
816 | del_cidr(h, data->cidr, HOST_MASK); | ||
817 | add_cidr(h, d->cidr, HOST_MASK); | ||
818 | #endif | ||
819 | type_pf_data_copy(data, d); | ||
820 | type_pf_data_timeout_set(data, timeout); | ||
821 | goto out; | ||
822 | } | ||
823 | ret = type_pf_elem_tadd(n, d, timeout); | ||
824 | if (ret != 0) | ||
825 | goto out; | ||
826 | |||
827 | #ifdef IP_SET_HASH_WITH_NETS | ||
828 | add_cidr(h, d->cidr, HOST_MASK); | ||
829 | #endif | ||
830 | h->elements++; | ||
831 | out: | ||
832 | rcu_read_unlock_bh(); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | static int | ||
837 | type_pf_tdel(struct ip_set *set, void *value, u32 timeout) | ||
838 | { | ||
839 | struct ip_set_hash *h = set->data; | ||
840 | struct htable *t = h->table; | ||
841 | const struct type_pf_elem *d = value; | ||
842 | struct hbucket *n; | ||
843 | int i, ret = 0; | ||
844 | struct type_pf_elem *data; | ||
845 | u32 key; | ||
846 | |||
847 | key = HKEY(value, h->initval, t->htable_bits); | ||
848 | n = hbucket(t, key); | ||
849 | for (i = 0; i < n->pos; i++) { | ||
850 | data = ahash_tdata(n, i); | ||
851 | if (!type_pf_data_equal(data, d)) | ||
852 | continue; | ||
853 | if (type_pf_data_expired(data)) | ||
854 | ret = -IPSET_ERR_EXIST; | ||
855 | if (i != n->pos - 1) | ||
856 | /* Not last one */ | ||
857 | type_pf_data_copy(data, ahash_tdata(n, n->pos - 1)); | ||
858 | |||
859 | n->pos--; | ||
860 | h->elements--; | ||
861 | #ifdef IP_SET_HASH_WITH_NETS | ||
862 | del_cidr(h, d->cidr, HOST_MASK); | ||
863 | #endif | ||
864 | if (n->pos + AHASH_INIT_SIZE < n->size) { | ||
865 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | ||
866 | * sizeof(struct type_pf_telem), | ||
867 | GFP_ATOMIC); | ||
868 | if (!tmp) | ||
869 | return 0; | ||
870 | n->size -= AHASH_INIT_SIZE; | ||
871 | memcpy(tmp, n->value, | ||
872 | n->size * sizeof(struct type_pf_telem)); | ||
873 | kfree(n->value); | ||
874 | n->value = tmp; | ||
875 | } | ||
876 | return 0; | ||
877 | } | ||
878 | |||
879 | return -IPSET_ERR_EXIST; | ||
880 | } | ||
881 | |||
882 | #ifdef IP_SET_HASH_WITH_NETS | ||
883 | static int | ||
884 | type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) | ||
885 | { | ||
886 | struct ip_set_hash *h = set->data; | ||
887 | struct htable *t = h->table; | ||
888 | struct type_pf_elem *data; | ||
889 | struct hbucket *n; | ||
890 | int i, j = 0; | ||
891 | u32 key; | ||
892 | u8 host_mask = SET_HOST_MASK(set->family); | ||
893 | |||
894 | for (; j < host_mask && h->nets[j].cidr; j++) { | ||
895 | type_pf_data_netmask(d, h->nets[j].cidr); | ||
896 | key = HKEY(d, h->initval, t->htable_bits); | ||
897 | n = hbucket(t, key); | ||
898 | for (i = 0; i < n->pos; i++) { | ||
899 | data = ahash_tdata(n, i); | ||
900 | if (type_pf_data_equal(data, d)) | ||
901 | return !type_pf_data_expired(data); | ||
902 | } | ||
903 | } | ||
904 | return 0; | ||
905 | } | ||
906 | #endif | ||
907 | |||
908 | static int | ||
909 | type_pf_ttest(struct ip_set *set, void *value, u32 timeout) | ||
910 | { | ||
911 | struct ip_set_hash *h = set->data; | ||
912 | struct htable *t = h->table; | ||
913 | struct type_pf_elem *data, *d = value; | ||
914 | struct hbucket *n; | ||
915 | int i; | ||
916 | u32 key; | ||
917 | |||
918 | #ifdef IP_SET_HASH_WITH_NETS | ||
919 | if (d->cidr == SET_HOST_MASK(set->family)) | ||
920 | return type_pf_ttest_cidrs(set, d, timeout); | ||
921 | #endif | ||
922 | key = HKEY(d, h->initval, t->htable_bits); | ||
923 | n = hbucket(t, key); | ||
924 | for (i = 0; i < n->pos; i++) { | ||
925 | data = ahash_tdata(n, i); | ||
926 | if (type_pf_data_equal(data, d)) | ||
927 | return !type_pf_data_expired(data); | ||
928 | } | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | static int | ||
933 | type_pf_tlist(const struct ip_set *set, | ||
934 | struct sk_buff *skb, struct netlink_callback *cb) | ||
935 | { | ||
936 | const struct ip_set_hash *h = set->data; | ||
937 | const struct htable *t = h->table; | ||
938 | struct nlattr *atd, *nested; | ||
939 | const struct hbucket *n; | ||
940 | const struct type_pf_elem *data; | ||
941 | u32 first = cb->args[2]; | ||
942 | /* We assume that one hash bucket fills into one page */ | ||
943 | void *incomplete; | ||
944 | int i; | ||
945 | |||
946 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
947 | if (!atd) | ||
948 | return -EMSGSIZE; | ||
949 | for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { | ||
950 | incomplete = skb_tail_pointer(skb); | ||
951 | n = hbucket(t, cb->args[2]); | ||
952 | for (i = 0; i < n->pos; i++) { | ||
953 | data = ahash_tdata(n, i); | ||
954 | pr_debug("list %p %u\n", n, i); | ||
955 | if (type_pf_data_expired(data)) | ||
956 | continue; | ||
957 | pr_debug("do list %p %u\n", n, i); | ||
958 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
959 | if (!nested) { | ||
960 | if (cb->args[2] == first) { | ||
961 | nla_nest_cancel(skb, atd); | ||
962 | return -EMSGSIZE; | ||
963 | } else | ||
964 | goto nla_put_failure; | ||
965 | } | ||
966 | if (type_pf_data_tlist(skb, data)) | ||
967 | goto nla_put_failure; | ||
968 | ipset_nest_end(skb, nested); | ||
969 | } | ||
970 | } | ||
971 | ipset_nest_end(skb, atd); | ||
972 | /* Set listing finished */ | ||
973 | cb->args[2] = 0; | ||
974 | |||
975 | return 0; | ||
976 | |||
977 | nla_put_failure: | ||
978 | nlmsg_trim(skb, incomplete); | ||
979 | ipset_nest_end(skb, atd); | ||
980 | if (unlikely(first == cb->args[2])) { | ||
981 | pr_warning("Can't list set %s: one bucket does not fit into " | ||
982 | "a message. Please report it!\n", set->name); | ||
983 | cb->args[2] = 0; | ||
984 | return -EMSGSIZE; | ||
985 | } | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | static const struct ip_set_type_variant type_pf_tvariant = { | ||
990 | .kadt = type_pf_kadt, | ||
991 | .uadt = type_pf_uadt, | ||
992 | .adt = { | ||
993 | [IPSET_ADD] = type_pf_tadd, | ||
994 | [IPSET_DEL] = type_pf_tdel, | ||
995 | [IPSET_TEST] = type_pf_ttest, | ||
996 | }, | ||
997 | .destroy = type_pf_destroy, | ||
998 | .flush = type_pf_flush, | ||
999 | .head = type_pf_head, | ||
1000 | .list = type_pf_tlist, | ||
1001 | .resize = type_pf_tresize, | ||
1002 | .same_set = type_pf_same_set, | ||
1003 | }; | ||
1004 | |||
1005 | static void | ||
1006 | type_pf_gc(unsigned long ul_set) | ||
1007 | { | ||
1008 | struct ip_set *set = (struct ip_set *) ul_set; | ||
1009 | struct ip_set_hash *h = set->data; | ||
1010 | |||
1011 | pr_debug("called\n"); | ||
1012 | write_lock_bh(&set->lock); | ||
1013 | type_pf_expire(h); | ||
1014 | write_unlock_bh(&set->lock); | ||
1015 | |||
1016 | h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; | ||
1017 | add_timer(&h->gc); | ||
1018 | } | ||
1019 | |||
1020 | static void | ||
1021 | type_pf_gc_init(struct ip_set *set) | ||
1022 | { | ||
1023 | struct ip_set_hash *h = set->data; | ||
1024 | |||
1025 | init_timer(&h->gc); | ||
1026 | h->gc.data = (unsigned long) set; | ||
1027 | h->gc.function = type_pf_gc; | ||
1028 | h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; | ||
1029 | add_timer(&h->gc); | ||
1030 | pr_debug("gc initialized, run in every %u\n", | ||
1031 | IPSET_GC_PERIOD(h->timeout)); | ||
1032 | } | ||
1033 | |||
1034 | #undef type_pf_data_equal | ||
1035 | #undef type_pf_data_isnull | ||
1036 | #undef type_pf_data_copy | ||
1037 | #undef type_pf_data_zero_out | ||
1038 | #undef type_pf_data_list | ||
1039 | #undef type_pf_data_tlist | ||
1040 | |||
1041 | #undef type_pf_elem | ||
1042 | #undef type_pf_telem | ||
1043 | #undef type_pf_data_timeout | ||
1044 | #undef type_pf_data_expired | ||
1045 | #undef type_pf_data_netmask | ||
1046 | #undef type_pf_data_timeout_set | ||
1047 | |||
1048 | #undef type_pf_elem_add | ||
1049 | #undef type_pf_add | ||
1050 | #undef type_pf_del | ||
1051 | #undef type_pf_test_cidrs | ||
1052 | #undef type_pf_test | ||
1053 | |||
1054 | #undef type_pf_elem_tadd | ||
1055 | #undef type_pf_expire | ||
1056 | #undef type_pf_tadd | ||
1057 | #undef type_pf_tdel | ||
1058 | #undef type_pf_ttest_cidrs | ||
1059 | #undef type_pf_ttest | ||
1060 | |||
1061 | #undef type_pf_resize | ||
1062 | #undef type_pf_tresize | ||
1063 | #undef type_pf_flush | ||
1064 | #undef type_pf_destroy | ||
1065 | #undef type_pf_head | ||
1066 | #undef type_pf_list | ||
1067 | #undef type_pf_tlist | ||
1068 | #undef type_pf_same_set | ||
1069 | #undef type_pf_kadt | ||
1070 | #undef type_pf_uadt | ||
1071 | #undef type_pf_gc | ||
1072 | #undef type_pf_gc_init | ||
1073 | #undef type_pf_variant | ||
1074 | #undef type_pf_tvariant | ||
diff --git a/include/linux/netfilter/ipset/ip_set_bitmap.h b/include/linux/netfilter/ipset/ip_set_bitmap.h new file mode 100644 index 000000000000..61a9e8746c83 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_bitmap.h | |||
@@ -0,0 +1,31 @@ | |||
1 | #ifndef __IP_SET_BITMAP_H | ||
2 | #define __IP_SET_BITMAP_H | ||
3 | |||
4 | /* Bitmap type specific error codes */ | ||
5 | enum { | ||
6 | /* The element is out of the range of the set */ | ||
7 | IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC, | ||
8 | /* The range exceeds the size limit of the set type */ | ||
9 | IPSET_ERR_BITMAP_RANGE_SIZE, | ||
10 | }; | ||
11 | |||
12 | #ifdef __KERNEL__ | ||
13 | #define IPSET_BITMAP_MAX_RANGE 0x0000FFFF | ||
14 | |||
15 | /* Common functions */ | ||
16 | |||
17 | static inline u32 | ||
18 | range_to_mask(u32 from, u32 to, u8 *bits) | ||
19 | { | ||
20 | u32 mask = 0xFFFFFFFE; | ||
21 | |||
22 | *bits = 32; | ||
23 | while (--(*bits) > 0 && mask && (to & mask) != from) | ||
24 | mask <<= 1; | ||
25 | |||
26 | return mask; | ||
27 | } | ||
28 | |||
29 | #endif /* __KERNEL__ */ | ||
30 | |||
31 | #endif /* __IP_SET_BITMAP_H */ | ||
diff --git a/include/linux/netfilter/ipset/ip_set_getport.h b/include/linux/netfilter/ipset/ip_set_getport.h new file mode 100644 index 000000000000..3882a81a3b3c --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_getport.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef _IP_SET_GETPORT_H | ||
2 | #define _IP_SET_GETPORT_H | ||
3 | |||
4 | extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src, | ||
5 | __be16 *port, u8 *proto); | ||
6 | |||
7 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
8 | extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, | ||
9 | __be16 *port, u8 *proto); | ||
10 | #else | ||
11 | static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, | ||
12 | __be16 *port, u8 *proto) | ||
13 | { | ||
14 | return false; | ||
15 | } | ||
16 | #endif | ||
17 | |||
18 | extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, | ||
19 | __be16 *port); | ||
20 | |||
21 | #endif /*_IP_SET_GETPORT_H*/ | ||
diff --git a/include/linux/netfilter/ipset/ip_set_hash.h b/include/linux/netfilter/ipset/ip_set_hash.h new file mode 100644 index 000000000000..b86f15c04524 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_hash.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef __IP_SET_HASH_H | ||
2 | #define __IP_SET_HASH_H | ||
3 | |||
4 | /* Hash type specific error codes */ | ||
5 | enum { | ||
6 | /* Hash is full */ | ||
7 | IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, | ||
8 | /* Null-valued element */ | ||
9 | IPSET_ERR_HASH_ELEM, | ||
10 | /* Invalid protocol */ | ||
11 | IPSET_ERR_INVALID_PROTO, | ||
12 | /* Protocol missing but must be specified */ | ||
13 | IPSET_ERR_MISSING_PROTO, | ||
14 | }; | ||
15 | |||
16 | #ifdef __KERNEL__ | ||
17 | |||
18 | #define IPSET_DEFAULT_HASHSIZE 1024 | ||
19 | #define IPSET_MIMINAL_HASHSIZE 64 | ||
20 | #define IPSET_DEFAULT_MAXELEM 65536 | ||
21 | #define IPSET_DEFAULT_PROBES 4 | ||
22 | #define IPSET_DEFAULT_RESIZE 100 | ||
23 | |||
24 | #endif /* __KERNEL__ */ | ||
25 | |||
26 | #endif /* __IP_SET_HASH_H */ | ||
diff --git a/include/linux/netfilter/ipset/ip_set_list.h b/include/linux/netfilter/ipset/ip_set_list.h new file mode 100644 index 000000000000..40a63f302613 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_list.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef __IP_SET_LIST_H | ||
2 | #define __IP_SET_LIST_H | ||
3 | |||
4 | /* List type specific error codes */ | ||
5 | enum { | ||
6 | /* Set name to be added/deleted/tested does not exist. */ | ||
7 | IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, | ||
8 | /* list:set type is not permitted to add */ | ||
9 | IPSET_ERR_LOOP, | ||
10 | /* Missing reference set */ | ||
11 | IPSET_ERR_BEFORE, | ||
12 | /* Reference set does not exist */ | ||
13 | IPSET_ERR_NAMEREF, | ||
14 | /* Set is full */ | ||
15 | IPSET_ERR_LIST_FULL, | ||
16 | /* Reference set is not added to the set */ | ||
17 | IPSET_ERR_REF_EXIST, | ||
18 | }; | ||
19 | |||
20 | #ifdef __KERNEL__ | ||
21 | |||
22 | #define IP_SET_LIST_DEFAULT_SIZE 8 | ||
23 | #define IP_SET_LIST_MIN_SIZE 4 | ||
24 | |||
25 | #endif /* __KERNEL__ */ | ||
26 | |||
27 | #endif /* __IP_SET_LIST_H */ | ||
diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h new file mode 100644 index 000000000000..9f30c5f2ec1c --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_timeout.h | |||
@@ -0,0 +1,127 @@ | |||
1 | #ifndef _IP_SET_TIMEOUT_H | ||
2 | #define _IP_SET_TIMEOUT_H | ||
3 | |||
4 | /* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifdef __KERNEL__ | ||
12 | |||
13 | /* How often should the gc be run by default */ | ||
14 | #define IPSET_GC_TIME (3 * 60) | ||
15 | |||
16 | /* Timeout period depending on the timeout value of the given set */ | ||
17 | #define IPSET_GC_PERIOD(timeout) \ | ||
18 | ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1) | ||
19 | |||
20 | /* Set is defined without timeout support: timeout value may be 0 */ | ||
21 | #define IPSET_NO_TIMEOUT UINT_MAX | ||
22 | |||
23 | #define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) | ||
24 | |||
25 | static inline unsigned int | ||
26 | ip_set_timeout_uget(struct nlattr *tb) | ||
27 | { | ||
28 | unsigned int timeout = ip_set_get_h32(tb); | ||
29 | |||
30 | /* Userspace supplied TIMEOUT parameter: adjust crazy size */ | ||
31 | return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout; | ||
32 | } | ||
33 | |||
34 | #ifdef IP_SET_BITMAP_TIMEOUT | ||
35 | |||
36 | /* Bitmap specific timeout constants and macros for the entries */ | ||
37 | |||
38 | /* Bitmap entry is unset */ | ||
39 | #define IPSET_ELEM_UNSET 0 | ||
40 | /* Bitmap entry is set with no timeout value */ | ||
41 | #define IPSET_ELEM_PERMANENT (UINT_MAX/2) | ||
42 | |||
43 | static inline bool | ||
44 | ip_set_timeout_test(unsigned long timeout) | ||
45 | { | ||
46 | return timeout != IPSET_ELEM_UNSET && | ||
47 | (timeout == IPSET_ELEM_PERMANENT || | ||
48 | time_after(timeout, jiffies)); | ||
49 | } | ||
50 | |||
51 | static inline bool | ||
52 | ip_set_timeout_expired(unsigned long timeout) | ||
53 | { | ||
54 | return timeout != IPSET_ELEM_UNSET && | ||
55 | timeout != IPSET_ELEM_PERMANENT && | ||
56 | time_before(timeout, jiffies); | ||
57 | } | ||
58 | |||
59 | static inline unsigned long | ||
60 | ip_set_timeout_set(u32 timeout) | ||
61 | { | ||
62 | unsigned long t; | ||
63 | |||
64 | if (!timeout) | ||
65 | return IPSET_ELEM_PERMANENT; | ||
66 | |||
67 | t = timeout * HZ + jiffies; | ||
68 | if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT) | ||
69 | /* Bingo! */ | ||
70 | t++; | ||
71 | |||
72 | return t; | ||
73 | } | ||
74 | |||
75 | static inline u32 | ||
76 | ip_set_timeout_get(unsigned long timeout) | ||
77 | { | ||
78 | return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; | ||
79 | } | ||
80 | |||
81 | #else | ||
82 | |||
83 | /* Hash specific timeout constants and macros for the entries */ | ||
84 | |||
85 | /* Hash entry is set with no timeout value */ | ||
86 | #define IPSET_ELEM_PERMANENT 0 | ||
87 | |||
88 | static inline bool | ||
89 | ip_set_timeout_test(unsigned long timeout) | ||
90 | { | ||
91 | return timeout == IPSET_ELEM_PERMANENT || | ||
92 | time_after(timeout, jiffies); | ||
93 | } | ||
94 | |||
95 | static inline bool | ||
96 | ip_set_timeout_expired(unsigned long timeout) | ||
97 | { | ||
98 | return timeout != IPSET_ELEM_PERMANENT && | ||
99 | time_before(timeout, jiffies); | ||
100 | } | ||
101 | |||
102 | static inline unsigned long | ||
103 | ip_set_timeout_set(u32 timeout) | ||
104 | { | ||
105 | unsigned long t; | ||
106 | |||
107 | if (!timeout) | ||
108 | return IPSET_ELEM_PERMANENT; | ||
109 | |||
110 | t = timeout * HZ + jiffies; | ||
111 | if (t == IPSET_ELEM_PERMANENT) | ||
112 | /* Bingo! :-) */ | ||
113 | t++; | ||
114 | |||
115 | return t; | ||
116 | } | ||
117 | |||
118 | static inline u32 | ||
119 | ip_set_timeout_get(unsigned long timeout) | ||
120 | { | ||
121 | return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; | ||
122 | } | ||
123 | #endif /* ! IP_SET_BITMAP_TIMEOUT */ | ||
124 | |||
125 | #endif /* __KERNEL__ */ | ||
126 | |||
127 | #endif /* _IP_SET_TIMEOUT_H */ | ||
diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h new file mode 100644 index 000000000000..0e1fb50da562 --- /dev/null +++ b/include/linux/netfilter/ipset/pfxlen.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef _PFXLEN_H | ||
2 | #define _PFXLEN_H | ||
3 | |||
4 | #include <asm/byteorder.h> | ||
5 | #include <linux/netfilter.h> | ||
6 | |||
7 | /* Prefixlen maps, by Jan Engelhardt */ | ||
8 | extern const union nf_inet_addr ip_set_netmask_map[]; | ||
9 | extern const union nf_inet_addr ip_set_hostmask_map[]; | ||
10 | |||
11 | static inline __be32 | ||
12 | ip_set_netmask(u8 pfxlen) | ||
13 | { | ||
14 | return ip_set_netmask_map[pfxlen].ip; | ||
15 | } | ||
16 | |||
17 | static inline const __be32 * | ||
18 | ip_set_netmask6(u8 pfxlen) | ||
19 | { | ||
20 | return &ip_set_netmask_map[pfxlen].ip6[0]; | ||
21 | } | ||
22 | |||
23 | static inline u32 | ||
24 | ip_set_hostmask(u8 pfxlen) | ||
25 | { | ||
26 | return (__force u32) ip_set_hostmask_map[pfxlen].ip; | ||
27 | } | ||
28 | |||
29 | static inline const __be32 * | ||
30 | ip_set_hostmask6(u8 pfxlen) | ||
31 | { | ||
32 | return &ip_set_hostmask_map[pfxlen].ip6[0]; | ||
33 | } | ||
34 | |||
35 | #endif /*_PFXLEN_H */ | ||
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 361d6b5630ee..2b11fc1a86be 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h | |||
@@ -47,7 +47,8 @@ struct nfgenmsg { | |||
47 | #define NFNL_SUBSYS_QUEUE 3 | 47 | #define NFNL_SUBSYS_QUEUE 3 |
48 | #define NFNL_SUBSYS_ULOG 4 | 48 | #define NFNL_SUBSYS_ULOG 4 |
49 | #define NFNL_SUBSYS_OSF 5 | 49 | #define NFNL_SUBSYS_OSF 5 |
50 | #define NFNL_SUBSYS_COUNT 6 | 50 | #define NFNL_SUBSYS_IPSET 6 |
51 | #define NFNL_SUBSYS_COUNT 7 | ||
51 | 52 | ||
52 | #ifdef __KERNEL__ | 53 | #ifdef __KERNEL__ |
53 | 54 | ||
diff --git a/include/linux/netfilter/xt_devgroup.h b/include/linux/netfilter/xt_devgroup.h new file mode 100644 index 000000000000..1babde0ec900 --- /dev/null +++ b/include/linux/netfilter/xt_devgroup.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef _XT_DEVGROUP_H | ||
2 | #define _XT_DEVGROUP_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | enum xt_devgroup_flags { | ||
7 | XT_DEVGROUP_MATCH_SRC = 0x1, | ||
8 | XT_DEVGROUP_INVERT_SRC = 0x2, | ||
9 | XT_DEVGROUP_MATCH_DST = 0x4, | ||
10 | XT_DEVGROUP_INVERT_DST = 0x8, | ||
11 | }; | ||
12 | |||
13 | struct xt_devgroup_info { | ||
14 | __u32 flags; | ||
15 | __u32 src_group; | ||
16 | __u32 src_mask; | ||
17 | __u32 dst_group; | ||
18 | __u32 dst_mask; | ||
19 | }; | ||
20 | |||
21 | #endif /* _XT_DEVGROUP_H */ | ||
diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h new file mode 100644 index 000000000000..081f1ded2842 --- /dev/null +++ b/include/linux/netfilter/xt_set.h | |||
@@ -0,0 +1,56 @@ | |||
1 | #ifndef _XT_SET_H | ||
2 | #define _XT_SET_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <linux/netfilter/ipset/ip_set.h> | ||
6 | |||
7 | /* Revision 0 interface: backward compatible with netfilter/iptables */ | ||
8 | |||
9 | /* | ||
10 | * Option flags for kernel operations (xt_set_info_v0) | ||
11 | */ | ||
12 | #define IPSET_SRC 0x01 /* Source match/add */ | ||
13 | #define IPSET_DST 0x02 /* Destination match/add */ | ||
14 | #define IPSET_MATCH_INV 0x04 /* Inverse matching */ | ||
15 | |||
16 | struct xt_set_info_v0 { | ||
17 | ip_set_id_t index; | ||
18 | union { | ||
19 | __u32 flags[IPSET_DIM_MAX + 1]; | ||
20 | struct { | ||
21 | __u32 __flags[IPSET_DIM_MAX]; | ||
22 | __u8 dim; | ||
23 | __u8 flags; | ||
24 | } compat; | ||
25 | } u; | ||
26 | }; | ||
27 | |||
28 | /* match and target infos */ | ||
29 | struct xt_set_info_match_v0 { | ||
30 | struct xt_set_info_v0 match_set; | ||
31 | }; | ||
32 | |||
33 | struct xt_set_info_target_v0 { | ||
34 | struct xt_set_info_v0 add_set; | ||
35 | struct xt_set_info_v0 del_set; | ||
36 | }; | ||
37 | |||
38 | /* Revision 1: current interface to netfilter/iptables */ | ||
39 | |||
40 | struct xt_set_info { | ||
41 | ip_set_id_t index; | ||
42 | __u8 dim; | ||
43 | __u8 flags; | ||
44 | }; | ||
45 | |||
46 | /* match and target infos */ | ||
47 | struct xt_set_info_match { | ||
48 | struct xt_set_info match_set; | ||
49 | }; | ||
50 | |||
51 | struct xt_set_info_target { | ||
52 | struct xt_set_info add_set; | ||
53 | struct xt_set_info del_set; | ||
54 | }; | ||
55 | |||
56 | #endif /*_XT_SET_H*/ | ||
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index b23bea62f708..5d75feadf4f4 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h | |||
@@ -1109,8 +1109,6 @@ extern int ip_vs_icmp_xmit_v6 | |||
1109 | * we are loaded. Just set ip_vs_drop_rate to 'n' and | 1109 | * we are loaded. Just set ip_vs_drop_rate to 'n' and |
1110 | * we start to drop 1/rate of the packets | 1110 | * we start to drop 1/rate of the packets |
1111 | */ | 1111 | */ |
1112 | extern int ip_vs_drop_rate; | ||
1113 | extern int ip_vs_drop_counter; | ||
1114 | 1112 | ||
1115 | static inline int ip_vs_todrop(struct netns_ipvs *ipvs) | 1113 | static inline int ip_vs_todrop(struct netns_ipvs *ipvs) |
1116 | { | 1114 | { |
diff --git a/include/net/netlink.h b/include/net/netlink.h index 373f1a900cf4..8a3906a08f5f 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h | |||
@@ -856,18 +856,27 @@ static inline int nla_put_msecs(struct sk_buff *skb, int attrtype, | |||
856 | #define NLA_PUT_BE16(skb, attrtype, value) \ | 856 | #define NLA_PUT_BE16(skb, attrtype, value) \ |
857 | NLA_PUT_TYPE(skb, __be16, attrtype, value) | 857 | NLA_PUT_TYPE(skb, __be16, attrtype, value) |
858 | 858 | ||
859 | #define NLA_PUT_NET16(skb, attrtype, value) \ | ||
860 | NLA_PUT_BE16(skb, attrtype | NLA_F_NET_BYTEORDER, value) | ||
861 | |||
859 | #define NLA_PUT_U32(skb, attrtype, value) \ | 862 | #define NLA_PUT_U32(skb, attrtype, value) \ |
860 | NLA_PUT_TYPE(skb, u32, attrtype, value) | 863 | NLA_PUT_TYPE(skb, u32, attrtype, value) |
861 | 864 | ||
862 | #define NLA_PUT_BE32(skb, attrtype, value) \ | 865 | #define NLA_PUT_BE32(skb, attrtype, value) \ |
863 | NLA_PUT_TYPE(skb, __be32, attrtype, value) | 866 | NLA_PUT_TYPE(skb, __be32, attrtype, value) |
864 | 867 | ||
868 | #define NLA_PUT_NET32(skb, attrtype, value) \ | ||
869 | NLA_PUT_BE32(skb, attrtype | NLA_F_NET_BYTEORDER, value) | ||
870 | |||
865 | #define NLA_PUT_U64(skb, attrtype, value) \ | 871 | #define NLA_PUT_U64(skb, attrtype, value) \ |
866 | NLA_PUT_TYPE(skb, u64, attrtype, value) | 872 | NLA_PUT_TYPE(skb, u64, attrtype, value) |
867 | 873 | ||
868 | #define NLA_PUT_BE64(skb, attrtype, value) \ | 874 | #define NLA_PUT_BE64(skb, attrtype, value) \ |
869 | NLA_PUT_TYPE(skb, __be64, attrtype, value) | 875 | NLA_PUT_TYPE(skb, __be64, attrtype, value) |
870 | 876 | ||
877 | #define NLA_PUT_NET64(skb, attrtype, value) \ | ||
878 | NLA_PUT_BE64(skb, attrtype | NLA_F_NET_BYTEORDER, value) | ||
879 | |||
871 | #define NLA_PUT_STRING(skb, attrtype, value) \ | 880 | #define NLA_PUT_STRING(skb, attrtype, value) \ |
872 | NLA_PUT(skb, attrtype, strlen(value) + 1, value) | 881 | NLA_PUT(skb, attrtype, strlen(value) + 1, value) |
873 | 882 | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index faf7412ea453..82a6e0d80f05 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -352,6 +352,18 @@ config NETFILTER_XT_CONNMARK | |||
352 | ctmark), similarly to the packet mark (nfmark). Using this | 352 | ctmark), similarly to the packet mark (nfmark). Using this |
353 | target and match, you can set and match on this mark. | 353 | target and match, you can set and match on this mark. |
354 | 354 | ||
355 | config NETFILTER_XT_SET | ||
356 | tristate 'set target and match support' | ||
357 | depends on IP_SET | ||
358 | depends on NETFILTER_ADVANCED | ||
359 | help | ||
360 | This option adds the "SET" target and "set" match. | ||
361 | |||
362 | Using this target and match, you can add/delete and match | ||
363 | elements in the sets created by ipset(8). | ||
364 | |||
365 | To compile it as a module, choose M here. If unsure, say N. | ||
366 | |||
355 | # alphabetically ordered list of targets | 367 | # alphabetically ordered list of targets |
356 | 368 | ||
357 | comment "Xtables targets" | 369 | comment "Xtables targets" |
@@ -726,6 +738,15 @@ config NETFILTER_XT_MATCH_DCCP | |||
726 | If you want to compile it as a module, say M here and read | 738 | If you want to compile it as a module, say M here and read |
727 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | 739 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. |
728 | 740 | ||
741 | config NETFILTER_XT_MATCH_DEVGROUP | ||
742 | tristate '"devgroup" match support' | ||
743 | depends on NETFILTER_ADVANCED | ||
744 | help | ||
745 | This options adds a `devgroup' match, which allows to match on the | ||
746 | device group a network device is assigned to. | ||
747 | |||
748 | To compile it as a module, choose M here. If unsure, say N. | ||
749 | |||
729 | config NETFILTER_XT_MATCH_DSCP | 750 | config NETFILTER_XT_MATCH_DSCP |
730 | tristate '"dscp" and "tos" match support' | 751 | tristate '"dscp" and "tos" match support' |
731 | depends on NETFILTER_ADVANCED | 752 | depends on NETFILTER_ADVANCED |
@@ -1052,4 +1073,6 @@ endif # NETFILTER_XTABLES | |||
1052 | 1073 | ||
1053 | endmenu | 1074 | endmenu |
1054 | 1075 | ||
1076 | source "net/netfilter/ipset/Kconfig" | ||
1077 | |||
1055 | source "net/netfilter/ipvs/Kconfig" | 1078 | source "net/netfilter/ipvs/Kconfig" |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9ae6878a85b1..d57a890eaee5 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o | |||
46 | # combos | 46 | # combos |
47 | obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o | 47 | obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o |
48 | obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o | 48 | obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o |
49 | obj-$(CONFIG_NETFILTER_XT_SET) += xt_set.o | ||
49 | 50 | ||
50 | # targets | 51 | # targets |
51 | obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o | 52 | obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o |
@@ -76,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o | |||
76 | obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o | 77 | obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o |
77 | obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o | 78 | obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o |
78 | obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o | 79 | obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o |
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_DEVGROUP) += xt_devgroup.o | ||
79 | obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o | 81 | obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o |
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o | 82 | obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o |
81 | obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o | 83 | obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o |
@@ -105,5 +107,8 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o | |||
105 | obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o | 107 | obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o |
106 | obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o | 108 | obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o |
107 | 109 | ||
110 | # ipset | ||
111 | obj-$(CONFIG_IP_SET) += ipset/ | ||
112 | |||
108 | # IPVS | 113 | # IPVS |
109 | obj-$(CONFIG_IP_VS) += ipvs/ | 114 | obj-$(CONFIG_IP_VS) += ipvs/ |
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig new file mode 100644 index 000000000000..3b970d343023 --- /dev/null +++ b/net/netfilter/ipset/Kconfig | |||
@@ -0,0 +1,121 @@ | |||
1 | menuconfig IP_SET | ||
2 | tristate "IP set support" | ||
3 | depends on INET && NETFILTER | ||
4 | help | ||
5 | This option adds IP set support to the kernel. | ||
6 | In order to define and use the sets, you need the userspace utility | ||
7 | ipset(8). You can use the sets in netfilter via the "set" match | ||
8 | and "SET" target. | ||
9 | |||
10 | To compile it as a module, choose M here. If unsure, say N. | ||
11 | |||
12 | if IP_SET | ||
13 | |||
14 | config IP_SET_MAX | ||
15 | int "Maximum number of IP sets" | ||
16 | default 256 | ||
17 | range 2 65534 | ||
18 | depends on IP_SET | ||
19 | help | ||
20 | You can define here default value of the maximum number | ||
21 | of IP sets for the kernel. | ||
22 | |||
23 | The value can be overriden by the 'max_sets' module | ||
24 | parameter of the 'ip_set' module. | ||
25 | |||
26 | config IP_SET_BITMAP_IP | ||
27 | tristate "bitmap:ip set support" | ||
28 | depends on IP_SET | ||
29 | help | ||
30 | This option adds the bitmap:ip set type support, by which one | ||
31 | can store IPv4 addresses (or network addresse) from a range. | ||
32 | |||
33 | To compile it as a module, choose M here. If unsure, say N. | ||
34 | |||
35 | config IP_SET_BITMAP_IPMAC | ||
36 | tristate "bitmap:ip,mac set support" | ||
37 | depends on IP_SET | ||
38 | help | ||
39 | This option adds the bitmap:ip,mac set type support, by which one | ||
40 | can store IPv4 address and (source) MAC address pairs from a range. | ||
41 | |||
42 | To compile it as a module, choose M here. If unsure, say N. | ||
43 | |||
44 | config IP_SET_BITMAP_PORT | ||
45 | tristate "bitmap:port set support" | ||
46 | depends on IP_SET | ||
47 | help | ||
48 | This option adds the bitmap:port set type support, by which one | ||
49 | can store TCP/UDP port numbers from a range. | ||
50 | |||
51 | To compile it as a module, choose M here. If unsure, say N. | ||
52 | |||
53 | config IP_SET_HASH_IP | ||
54 | tristate "hash:ip set support" | ||
55 | depends on IP_SET | ||
56 | help | ||
57 | This option adds the hash:ip set type support, by which one | ||
58 | can store arbitrary IPv4 or IPv6 addresses (or network addresses) | ||
59 | in a set. | ||
60 | |||
61 | To compile it as a module, choose M here. If unsure, say N. | ||
62 | |||
63 | config IP_SET_HASH_IPPORT | ||
64 | tristate "hash:ip,port set support" | ||
65 | depends on IP_SET | ||
66 | help | ||
67 | This option adds the hash:ip,port set type support, by which one | ||
68 | can store IPv4/IPv6 address and protocol/port pairs. | ||
69 | |||
70 | To compile it as a module, choose M here. If unsure, say N. | ||
71 | |||
72 | config IP_SET_HASH_IPPORTIP | ||
73 | tristate "hash:ip,port,ip set support" | ||
74 | depends on IP_SET | ||
75 | help | ||
76 | This option adds the hash:ip,port,ip set type support, by which | ||
77 | one can store IPv4/IPv6 address, protocol/port, and IPv4/IPv6 | ||
78 | address triples in a set. | ||
79 | |||
80 | To compile it as a module, choose M here. If unsure, say N. | ||
81 | |||
82 | config IP_SET_HASH_IPPORTNET | ||
83 | tristate "hash:ip,port,net set support" | ||
84 | depends on IP_SET | ||
85 | help | ||
86 | This option adds the hash:ip,port,net set type support, by which | ||
87 | one can store IPv4/IPv6 address, protocol/port, and IPv4/IPv6 | ||
88 | network address/prefix triples in a set. | ||
89 | |||
90 | To compile it as a module, choose M here. If unsure, say N. | ||
91 | |||
92 | config IP_SET_HASH_NET | ||
93 | tristate "hash:net set support" | ||
94 | depends on IP_SET | ||
95 | help | ||
96 | This option adds the hash:net set type support, by which | ||
97 | one can store IPv4/IPv6 network address/prefix elements in a set. | ||
98 | |||
99 | To compile it as a module, choose M here. If unsure, say N. | ||
100 | |||
101 | config IP_SET_HASH_NETPORT | ||
102 | tristate "hash:net,port set support" | ||
103 | depends on IP_SET | ||
104 | help | ||
105 | This option adds the hash:net,port set type support, by which | ||
106 | one can store IPv4/IPv6 network address/prefix and | ||
107 | protocol/port pairs as elements in a set. | ||
108 | |||
109 | To compile it as a module, choose M here. If unsure, say N. | ||
110 | |||
111 | config IP_SET_LIST_SET | ||
112 | tristate "list:set set support" | ||
113 | depends on IP_SET | ||
114 | help | ||
115 | This option adds the list:set set type support. In this | ||
116 | kind of set one can store the name of other sets and it forms | ||
117 | an ordered union of the member sets. | ||
118 | |||
119 | To compile it as a module, choose M here. If unsure, say N. | ||
120 | |||
121 | endif # IP_SET | ||
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile new file mode 100644 index 000000000000..5adbdab67bd2 --- /dev/null +++ b/net/netfilter/ipset/Makefile | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Makefile for the ipset modules | ||
3 | # | ||
4 | |||
5 | ip_set-y := ip_set_core.o ip_set_getport.o pfxlen.o | ||
6 | |||
7 | # ipset core | ||
8 | obj-$(CONFIG_IP_SET) += ip_set.o | ||
9 | |||
10 | # bitmap types | ||
11 | obj-$(CONFIG_IP_SET_BITMAP_IP) += ip_set_bitmap_ip.o | ||
12 | obj-$(CONFIG_IP_SET_BITMAP_IPMAC) += ip_set_bitmap_ipmac.o | ||
13 | obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o | ||
14 | |||
15 | # hash types | ||
16 | obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o | ||
17 | obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o | ||
18 | obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o | ||
19 | obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o | ||
20 | obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o | ||
21 | obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o | ||
22 | |||
23 | # list types | ||
24 | obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o | ||
diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c new file mode 100644 index 000000000000..bca96990218d --- /dev/null +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c | |||
@@ -0,0 +1,587 @@ | |||
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
2 | * Patrick Schaaf <bof@bof.de> | ||
3 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | /* Kernel module implementing an IP set type: the bitmap:ip type */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/ip.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/bitops.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/netlink.h> | ||
19 | #include <linux/jiffies.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <net/netlink.h> | ||
22 | #include <net/tcp.h> | ||
23 | |||
24 | #include <linux/netfilter/ipset/pfxlen.h> | ||
25 | #include <linux/netfilter/ipset/ip_set.h> | ||
26 | #include <linux/netfilter/ipset/ip_set_bitmap.h> | ||
27 | #define IP_SET_BITMAP_TIMEOUT | ||
28 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
29 | |||
30 | MODULE_LICENSE("GPL"); | ||
31 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
32 | MODULE_DESCRIPTION("bitmap:ip type of IP sets"); | ||
33 | MODULE_ALIAS("ip_set_bitmap:ip"); | ||
34 | |||
35 | /* Type structure */ | ||
36 | struct bitmap_ip { | ||
37 | void *members; /* the set members */ | ||
38 | u32 first_ip; /* host byte order, included in range */ | ||
39 | u32 last_ip; /* host byte order, included in range */ | ||
40 | u32 elements; /* number of max elements in the set */ | ||
41 | u32 hosts; /* number of hosts in a subnet */ | ||
42 | size_t memsize; /* members size */ | ||
43 | u8 netmask; /* subnet netmask */ | ||
44 | u32 timeout; /* timeout parameter */ | ||
45 | struct timer_list gc; /* garbage collection */ | ||
46 | }; | ||
47 | |||
48 | /* Base variant */ | ||
49 | |||
50 | static inline u32 | ||
51 | ip_to_id(const struct bitmap_ip *m, u32 ip) | ||
52 | { | ||
53 | return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts; | ||
54 | } | ||
55 | |||
56 | static int | ||
57 | bitmap_ip_test(struct ip_set *set, void *value, u32 timeout) | ||
58 | { | ||
59 | const struct bitmap_ip *map = set->data; | ||
60 | u16 id = *(u16 *)value; | ||
61 | |||
62 | return !!test_bit(id, map->members); | ||
63 | } | ||
64 | |||
65 | static int | ||
66 | bitmap_ip_add(struct ip_set *set, void *value, u32 timeout) | ||
67 | { | ||
68 | struct bitmap_ip *map = set->data; | ||
69 | u16 id = *(u16 *)value; | ||
70 | |||
71 | if (test_and_set_bit(id, map->members)) | ||
72 | return -IPSET_ERR_EXIST; | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int | ||
78 | bitmap_ip_del(struct ip_set *set, void *value, u32 timeout) | ||
79 | { | ||
80 | struct bitmap_ip *map = set->data; | ||
81 | u16 id = *(u16 *)value; | ||
82 | |||
83 | if (!test_and_clear_bit(id, map->members)) | ||
84 | return -IPSET_ERR_EXIST; | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int | ||
90 | bitmap_ip_list(const struct ip_set *set, | ||
91 | struct sk_buff *skb, struct netlink_callback *cb) | ||
92 | { | ||
93 | const struct bitmap_ip *map = set->data; | ||
94 | struct nlattr *atd, *nested; | ||
95 | u32 id, first = cb->args[2]; | ||
96 | |||
97 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
98 | if (!atd) | ||
99 | return -EMSGSIZE; | ||
100 | for (; cb->args[2] < map->elements; cb->args[2]++) { | ||
101 | id = cb->args[2]; | ||
102 | if (!test_bit(id, map->members)) | ||
103 | continue; | ||
104 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
105 | if (!nested) { | ||
106 | if (id == first) { | ||
107 | nla_nest_cancel(skb, atd); | ||
108 | return -EMSGSIZE; | ||
109 | } else | ||
110 | goto nla_put_failure; | ||
111 | } | ||
112 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, | ||
113 | htonl(map->first_ip + id * map->hosts)); | ||
114 | ipset_nest_end(skb, nested); | ||
115 | } | ||
116 | ipset_nest_end(skb, atd); | ||
117 | /* Set listing finished */ | ||
118 | cb->args[2] = 0; | ||
119 | return 0; | ||
120 | |||
121 | nla_put_failure: | ||
122 | nla_nest_cancel(skb, nested); | ||
123 | ipset_nest_end(skb, atd); | ||
124 | if (unlikely(id == first)) { | ||
125 | cb->args[2] = 0; | ||
126 | return -EMSGSIZE; | ||
127 | } | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* Timeout variant */ | ||
132 | |||
133 | static int | ||
134 | bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout) | ||
135 | { | ||
136 | const struct bitmap_ip *map = set->data; | ||
137 | const unsigned long *members = map->members; | ||
138 | u16 id = *(u16 *)value; | ||
139 | |||
140 | return ip_set_timeout_test(members[id]); | ||
141 | } | ||
142 | |||
143 | static int | ||
144 | bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout) | ||
145 | { | ||
146 | struct bitmap_ip *map = set->data; | ||
147 | unsigned long *members = map->members; | ||
148 | u16 id = *(u16 *)value; | ||
149 | |||
150 | if (ip_set_timeout_test(members[id])) | ||
151 | return -IPSET_ERR_EXIST; | ||
152 | |||
153 | members[id] = ip_set_timeout_set(timeout); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int | ||
159 | bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout) | ||
160 | { | ||
161 | struct bitmap_ip *map = set->data; | ||
162 | unsigned long *members = map->members; | ||
163 | u16 id = *(u16 *)value; | ||
164 | int ret = -IPSET_ERR_EXIST; | ||
165 | |||
166 | if (ip_set_timeout_test(members[id])) | ||
167 | ret = 0; | ||
168 | |||
169 | members[id] = IPSET_ELEM_UNSET; | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | bitmap_ip_tlist(const struct ip_set *set, | ||
175 | struct sk_buff *skb, struct netlink_callback *cb) | ||
176 | { | ||
177 | const struct bitmap_ip *map = set->data; | ||
178 | struct nlattr *adt, *nested; | ||
179 | u32 id, first = cb->args[2]; | ||
180 | const unsigned long *members = map->members; | ||
181 | |||
182 | adt = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
183 | if (!adt) | ||
184 | return -EMSGSIZE; | ||
185 | for (; cb->args[2] < map->elements; cb->args[2]++) { | ||
186 | id = cb->args[2]; | ||
187 | if (!ip_set_timeout_test(members[id])) | ||
188 | continue; | ||
189 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
190 | if (!nested) { | ||
191 | if (id == first) { | ||
192 | nla_nest_cancel(skb, adt); | ||
193 | return -EMSGSIZE; | ||
194 | } else | ||
195 | goto nla_put_failure; | ||
196 | } | ||
197 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, | ||
198 | htonl(map->first_ip + id * map->hosts)); | ||
199 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
200 | htonl(ip_set_timeout_get(members[id]))); | ||
201 | ipset_nest_end(skb, nested); | ||
202 | } | ||
203 | ipset_nest_end(skb, adt); | ||
204 | |||
205 | /* Set listing finished */ | ||
206 | cb->args[2] = 0; | ||
207 | |||
208 | return 0; | ||
209 | |||
210 | nla_put_failure: | ||
211 | nla_nest_cancel(skb, nested); | ||
212 | ipset_nest_end(skb, adt); | ||
213 | if (unlikely(id == first)) { | ||
214 | cb->args[2] = 0; | ||
215 | return -EMSGSIZE; | ||
216 | } | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int | ||
221 | bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
222 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
223 | { | ||
224 | struct bitmap_ip *map = set->data; | ||
225 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
226 | u32 ip; | ||
227 | |||
228 | ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); | ||
229 | if (ip < map->first_ip || ip > map->last_ip) | ||
230 | return -IPSET_ERR_BITMAP_RANGE; | ||
231 | |||
232 | ip = ip_to_id(map, ip); | ||
233 | |||
234 | return adtfn(set, &ip, map->timeout); | ||
235 | } | ||
236 | |||
237 | static int | ||
238 | bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], | ||
239 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
240 | { | ||
241 | struct bitmap_ip *map = set->data; | ||
242 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
243 | u32 timeout = map->timeout; | ||
244 | u32 ip, ip_to, id; | ||
245 | int ret = 0; | ||
246 | |||
247 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
248 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
249 | return -IPSET_ERR_PROTOCOL; | ||
250 | |||
251 | if (tb[IPSET_ATTR_LINENO]) | ||
252 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
253 | |||
254 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); | ||
255 | if (ret) | ||
256 | return ret; | ||
257 | |||
258 | if (ip < map->first_ip || ip > map->last_ip) | ||
259 | return -IPSET_ERR_BITMAP_RANGE; | ||
260 | |||
261 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
262 | if (!with_timeout(map->timeout)) | ||
263 | return -IPSET_ERR_TIMEOUT; | ||
264 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
265 | } | ||
266 | |||
267 | if (adt == IPSET_TEST) { | ||
268 | id = ip_to_id(map, ip); | ||
269 | return adtfn(set, &id, timeout); | ||
270 | } | ||
271 | |||
272 | if (tb[IPSET_ATTR_IP_TO]) { | ||
273 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | ||
274 | if (ret) | ||
275 | return ret; | ||
276 | if (ip > ip_to) { | ||
277 | swap(ip, ip_to); | ||
278 | if (ip < map->first_ip) | ||
279 | return -IPSET_ERR_BITMAP_RANGE; | ||
280 | } | ||
281 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
282 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
283 | |||
284 | if (cidr > 32) | ||
285 | return -IPSET_ERR_INVALID_CIDR; | ||
286 | ip &= ip_set_hostmask(cidr); | ||
287 | ip_to = ip | ~ip_set_hostmask(cidr); | ||
288 | } else | ||
289 | ip_to = ip; | ||
290 | |||
291 | if (ip_to > map->last_ip) | ||
292 | return -IPSET_ERR_BITMAP_RANGE; | ||
293 | |||
294 | for (; !before(ip_to, ip); ip += map->hosts) { | ||
295 | id = ip_to_id(map, ip); | ||
296 | ret = adtfn(set, &id, timeout);; | ||
297 | |||
298 | if (ret && !ip_set_eexist(ret, flags)) | ||
299 | return ret; | ||
300 | else | ||
301 | ret = 0; | ||
302 | } | ||
303 | return ret; | ||
304 | } | ||
305 | |||
306 | static void | ||
307 | bitmap_ip_destroy(struct ip_set *set) | ||
308 | { | ||
309 | struct bitmap_ip *map = set->data; | ||
310 | |||
311 | if (with_timeout(map->timeout)) | ||
312 | del_timer_sync(&map->gc); | ||
313 | |||
314 | ip_set_free(map->members); | ||
315 | kfree(map); | ||
316 | |||
317 | set->data = NULL; | ||
318 | } | ||
319 | |||
320 | static void | ||
321 | bitmap_ip_flush(struct ip_set *set) | ||
322 | { | ||
323 | struct bitmap_ip *map = set->data; | ||
324 | |||
325 | memset(map->members, 0, map->memsize); | ||
326 | } | ||
327 | |||
328 | static int | ||
329 | bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) | ||
330 | { | ||
331 | const struct bitmap_ip *map = set->data; | ||
332 | struct nlattr *nested; | ||
333 | |||
334 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
335 | if (!nested) | ||
336 | goto nla_put_failure; | ||
337 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); | ||
338 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); | ||
339 | if (map->netmask != 32) | ||
340 | NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); | ||
341 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | ||
342 | htonl(atomic_read(&set->ref) - 1)); | ||
343 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | ||
344 | htonl(sizeof(*map) + map->memsize)); | ||
345 | if (with_timeout(map->timeout)) | ||
346 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | ||
347 | ipset_nest_end(skb, nested); | ||
348 | |||
349 | return 0; | ||
350 | nla_put_failure: | ||
351 | return -EMSGSIZE; | ||
352 | } | ||
353 | |||
354 | static bool | ||
355 | bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) | ||
356 | { | ||
357 | const struct bitmap_ip *x = a->data; | ||
358 | const struct bitmap_ip *y = b->data; | ||
359 | |||
360 | return x->first_ip == y->first_ip && | ||
361 | x->last_ip == y->last_ip && | ||
362 | x->netmask == y->netmask && | ||
363 | x->timeout == y->timeout; | ||
364 | } | ||
365 | |||
366 | static const struct ip_set_type_variant bitmap_ip = { | ||
367 | .kadt = bitmap_ip_kadt, | ||
368 | .uadt = bitmap_ip_uadt, | ||
369 | .adt = { | ||
370 | [IPSET_ADD] = bitmap_ip_add, | ||
371 | [IPSET_DEL] = bitmap_ip_del, | ||
372 | [IPSET_TEST] = bitmap_ip_test, | ||
373 | }, | ||
374 | .destroy = bitmap_ip_destroy, | ||
375 | .flush = bitmap_ip_flush, | ||
376 | .head = bitmap_ip_head, | ||
377 | .list = bitmap_ip_list, | ||
378 | .same_set = bitmap_ip_same_set, | ||
379 | }; | ||
380 | |||
381 | static const struct ip_set_type_variant bitmap_tip = { | ||
382 | .kadt = bitmap_ip_kadt, | ||
383 | .uadt = bitmap_ip_uadt, | ||
384 | .adt = { | ||
385 | [IPSET_ADD] = bitmap_ip_tadd, | ||
386 | [IPSET_DEL] = bitmap_ip_tdel, | ||
387 | [IPSET_TEST] = bitmap_ip_ttest, | ||
388 | }, | ||
389 | .destroy = bitmap_ip_destroy, | ||
390 | .flush = bitmap_ip_flush, | ||
391 | .head = bitmap_ip_head, | ||
392 | .list = bitmap_ip_tlist, | ||
393 | .same_set = bitmap_ip_same_set, | ||
394 | }; | ||
395 | |||
396 | static void | ||
397 | bitmap_ip_gc(unsigned long ul_set) | ||
398 | { | ||
399 | struct ip_set *set = (struct ip_set *) ul_set; | ||
400 | struct bitmap_ip *map = set->data; | ||
401 | unsigned long *table = map->members; | ||
402 | u32 id; | ||
403 | |||
404 | /* We run parallel with other readers (test element) | ||
405 | * but adding/deleting new entries is locked out */ | ||
406 | read_lock_bh(&set->lock); | ||
407 | for (id = 0; id < map->elements; id++) | ||
408 | if (ip_set_timeout_expired(table[id])) | ||
409 | table[id] = IPSET_ELEM_UNSET; | ||
410 | read_unlock_bh(&set->lock); | ||
411 | |||
412 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
413 | add_timer(&map->gc); | ||
414 | } | ||
415 | |||
416 | static void | ||
417 | bitmap_ip_gc_init(struct ip_set *set) | ||
418 | { | ||
419 | struct bitmap_ip *map = set->data; | ||
420 | |||
421 | init_timer(&map->gc); | ||
422 | map->gc.data = (unsigned long) set; | ||
423 | map->gc.function = bitmap_ip_gc; | ||
424 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
425 | add_timer(&map->gc); | ||
426 | } | ||
427 | |||
428 | /* Create bitmap:ip type of sets */ | ||
429 | |||
430 | static bool | ||
431 | init_map_ip(struct ip_set *set, struct bitmap_ip *map, | ||
432 | u32 first_ip, u32 last_ip, | ||
433 | u32 elements, u32 hosts, u8 netmask) | ||
434 | { | ||
435 | map->members = ip_set_alloc(map->memsize); | ||
436 | if (!map->members) | ||
437 | return false; | ||
438 | map->first_ip = first_ip; | ||
439 | map->last_ip = last_ip; | ||
440 | map->elements = elements; | ||
441 | map->hosts = hosts; | ||
442 | map->netmask = netmask; | ||
443 | map->timeout = IPSET_NO_TIMEOUT; | ||
444 | |||
445 | set->data = map; | ||
446 | set->family = AF_INET; | ||
447 | |||
448 | return true; | ||
449 | } | ||
450 | |||
451 | static int | ||
452 | bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
453 | { | ||
454 | struct bitmap_ip *map; | ||
455 | u32 first_ip, last_ip, hosts, elements; | ||
456 | u8 netmask = 32; | ||
457 | int ret; | ||
458 | |||
459 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
460 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
461 | return -IPSET_ERR_PROTOCOL; | ||
462 | |||
463 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip); | ||
464 | if (ret) | ||
465 | return ret; | ||
466 | |||
467 | if (tb[IPSET_ATTR_IP_TO]) { | ||
468 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip); | ||
469 | if (ret) | ||
470 | return ret; | ||
471 | if (first_ip > last_ip) { | ||
472 | u32 tmp = first_ip; | ||
473 | |||
474 | first_ip = last_ip; | ||
475 | last_ip = tmp; | ||
476 | } | ||
477 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
478 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
479 | |||
480 | if (cidr >= 32) | ||
481 | return -IPSET_ERR_INVALID_CIDR; | ||
482 | last_ip = first_ip | ~ip_set_hostmask(cidr); | ||
483 | } else | ||
484 | return -IPSET_ERR_PROTOCOL; | ||
485 | |||
486 | if (tb[IPSET_ATTR_NETMASK]) { | ||
487 | netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); | ||
488 | |||
489 | if (netmask > 32) | ||
490 | return -IPSET_ERR_INVALID_NETMASK; | ||
491 | |||
492 | first_ip &= ip_set_hostmask(netmask); | ||
493 | last_ip |= ~ip_set_hostmask(netmask); | ||
494 | } | ||
495 | |||
496 | if (netmask == 32) { | ||
497 | hosts = 1; | ||
498 | elements = last_ip - first_ip + 1; | ||
499 | } else { | ||
500 | u8 mask_bits; | ||
501 | u32 mask; | ||
502 | |||
503 | mask = range_to_mask(first_ip, last_ip, &mask_bits); | ||
504 | |||
505 | if ((!mask && (first_ip || last_ip != 0xFFFFFFFF)) || | ||
506 | netmask <= mask_bits) | ||
507 | return -IPSET_ERR_BITMAP_RANGE; | ||
508 | |||
509 | pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask); | ||
510 | hosts = 2 << (32 - netmask - 1); | ||
511 | elements = 2 << (netmask - mask_bits - 1); | ||
512 | } | ||
513 | if (elements > IPSET_BITMAP_MAX_RANGE + 1) | ||
514 | return -IPSET_ERR_BITMAP_RANGE_SIZE; | ||
515 | |||
516 | pr_debug("hosts %u, elements %u\n", hosts, elements); | ||
517 | |||
518 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
519 | if (!map) | ||
520 | return -ENOMEM; | ||
521 | |||
522 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
523 | map->memsize = elements * sizeof(unsigned long); | ||
524 | |||
525 | if (!init_map_ip(set, map, first_ip, last_ip, | ||
526 | elements, hosts, netmask)) { | ||
527 | kfree(map); | ||
528 | return -ENOMEM; | ||
529 | } | ||
530 | |||
531 | map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
532 | set->variant = &bitmap_tip; | ||
533 | |||
534 | bitmap_ip_gc_init(set); | ||
535 | } else { | ||
536 | map->memsize = bitmap_bytes(0, elements - 1); | ||
537 | |||
538 | if (!init_map_ip(set, map, first_ip, last_ip, | ||
539 | elements, hosts, netmask)) { | ||
540 | kfree(map); | ||
541 | return -ENOMEM; | ||
542 | } | ||
543 | |||
544 | set->variant = &bitmap_ip; | ||
545 | } | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static struct ip_set_type bitmap_ip_type __read_mostly = { | ||
550 | .name = "bitmap:ip", | ||
551 | .protocol = IPSET_PROTOCOL, | ||
552 | .features = IPSET_TYPE_IP, | ||
553 | .dimension = IPSET_DIM_ONE, | ||
554 | .family = AF_INET, | ||
555 | .revision = 0, | ||
556 | .create = bitmap_ip_create, | ||
557 | .create_policy = { | ||
558 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
559 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
560 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
561 | [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, | ||
562 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
563 | }, | ||
564 | .adt_policy = { | ||
565 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
566 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
567 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
568 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
569 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
570 | }, | ||
571 | .me = THIS_MODULE, | ||
572 | }; | ||
573 | |||
574 | static int __init | ||
575 | bitmap_ip_init(void) | ||
576 | { | ||
577 | return ip_set_type_register(&bitmap_ip_type); | ||
578 | } | ||
579 | |||
580 | static void __exit | ||
581 | bitmap_ip_fini(void) | ||
582 | { | ||
583 | ip_set_type_unregister(&bitmap_ip_type); | ||
584 | } | ||
585 | |||
586 | module_init(bitmap_ip_init); | ||
587 | module_exit(bitmap_ip_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c new file mode 100644 index 000000000000..5e790172deff --- /dev/null +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c | |||
@@ -0,0 +1,652 @@ | |||
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
2 | * Patrick Schaaf <bof@bof.de> | ||
3 | * Martin Josefsson <gandalf@wlug.westbo.se> | ||
4 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | /* Kernel module implementing an IP set type: the bitmap:ip,mac type */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/ip.h> | ||
15 | #include <linux/etherdevice.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/if_ether.h> | ||
19 | #include <linux/netlink.h> | ||
20 | #include <linux/jiffies.h> | ||
21 | #include <linux/timer.h> | ||
22 | #include <net/netlink.h> | ||
23 | |||
24 | #include <linux/netfilter/ipset/pfxlen.h> | ||
25 | #include <linux/netfilter/ipset/ip_set.h> | ||
26 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
27 | #include <linux/netfilter/ipset/ip_set_bitmap.h> | ||
28 | |||
29 | MODULE_LICENSE("GPL"); | ||
30 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
31 | MODULE_DESCRIPTION("bitmap:ip,mac type of IP sets"); | ||
32 | MODULE_ALIAS("ip_set_bitmap:ip,mac"); | ||
33 | |||
34 | enum { | ||
35 | MAC_EMPTY, /* element is not set */ | ||
36 | MAC_FILLED, /* element is set with MAC */ | ||
37 | MAC_UNSET, /* element is set, without MAC */ | ||
38 | }; | ||
39 | |||
40 | /* Type structure */ | ||
41 | struct bitmap_ipmac { | ||
42 | void *members; /* the set members */ | ||
43 | u32 first_ip; /* host byte order, included in range */ | ||
44 | u32 last_ip; /* host byte order, included in range */ | ||
45 | u32 timeout; /* timeout value */ | ||
46 | struct timer_list gc; /* garbage collector */ | ||
47 | size_t dsize; /* size of element */ | ||
48 | }; | ||
49 | |||
50 | /* ADT structure for generic function args */ | ||
51 | struct ipmac { | ||
52 | u32 id; /* id in array */ | ||
53 | unsigned char *ether; /* ethernet address */ | ||
54 | }; | ||
55 | |||
56 | /* Member element without and with timeout */ | ||
57 | |||
58 | struct ipmac_elem { | ||
59 | unsigned char ether[ETH_ALEN]; | ||
60 | unsigned char match; | ||
61 | } __attribute__ ((aligned)); | ||
62 | |||
63 | struct ipmac_telem { | ||
64 | unsigned char ether[ETH_ALEN]; | ||
65 | unsigned char match; | ||
66 | unsigned long timeout; | ||
67 | } __attribute__ ((aligned)); | ||
68 | |||
69 | static inline void * | ||
70 | bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id) | ||
71 | { | ||
72 | return (void *)((char *)map->members + id * map->dsize); | ||
73 | } | ||
74 | |||
75 | static inline bool | ||
76 | bitmap_timeout(const struct bitmap_ipmac *map, u32 id) | ||
77 | { | ||
78 | const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id); | ||
79 | |||
80 | return ip_set_timeout_test(elem->timeout); | ||
81 | } | ||
82 | |||
83 | static inline bool | ||
84 | bitmap_expired(const struct bitmap_ipmac *map, u32 id) | ||
85 | { | ||
86 | const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id); | ||
87 | |||
88 | return ip_set_timeout_expired(elem->timeout); | ||
89 | } | ||
90 | |||
91 | static inline int | ||
92 | bitmap_ipmac_exist(const struct ipmac_telem *elem) | ||
93 | { | ||
94 | return elem->match == MAC_UNSET || | ||
95 | (elem->match == MAC_FILLED && | ||
96 | !ip_set_timeout_expired(elem->timeout)); | ||
97 | } | ||
98 | |||
99 | /* Base variant */ | ||
100 | |||
101 | static int | ||
102 | bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout) | ||
103 | { | ||
104 | const struct bitmap_ipmac *map = set->data; | ||
105 | const struct ipmac *data = value; | ||
106 | const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); | ||
107 | |||
108 | switch (elem->match) { | ||
109 | case MAC_UNSET: | ||
110 | /* Trigger kernel to fill out the ethernet address */ | ||
111 | return -EAGAIN; | ||
112 | case MAC_FILLED: | ||
113 | return data->ether == NULL || | ||
114 | compare_ether_addr(data->ether, elem->ether) == 0; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int | ||
120 | bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout) | ||
121 | { | ||
122 | struct bitmap_ipmac *map = set->data; | ||
123 | const struct ipmac *data = value; | ||
124 | struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); | ||
125 | |||
126 | switch (elem->match) { | ||
127 | case MAC_UNSET: | ||
128 | if (!data->ether) | ||
129 | /* Already added without ethernet address */ | ||
130 | return -IPSET_ERR_EXIST; | ||
131 | /* Fill the MAC address */ | ||
132 | memcpy(elem->ether, data->ether, ETH_ALEN); | ||
133 | elem->match = MAC_FILLED; | ||
134 | break; | ||
135 | case MAC_FILLED: | ||
136 | return -IPSET_ERR_EXIST; | ||
137 | case MAC_EMPTY: | ||
138 | if (data->ether) { | ||
139 | memcpy(elem->ether, data->ether, ETH_ALEN); | ||
140 | elem->match = MAC_FILLED; | ||
141 | } else | ||
142 | elem->match = MAC_UNSET; | ||
143 | } | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static int | ||
149 | bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout) | ||
150 | { | ||
151 | struct bitmap_ipmac *map = set->data; | ||
152 | const struct ipmac *data = value; | ||
153 | struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); | ||
154 | |||
155 | if (elem->match == MAC_EMPTY) | ||
156 | return -IPSET_ERR_EXIST; | ||
157 | |||
158 | elem->match = MAC_EMPTY; | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int | ||
164 | bitmap_ipmac_list(const struct ip_set *set, | ||
165 | struct sk_buff *skb, struct netlink_callback *cb) | ||
166 | { | ||
167 | const struct bitmap_ipmac *map = set->data; | ||
168 | const struct ipmac_elem *elem; | ||
169 | struct nlattr *atd, *nested; | ||
170 | u32 id, first = cb->args[2]; | ||
171 | u32 last = map->last_ip - map->first_ip; | ||
172 | |||
173 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
174 | if (!atd) | ||
175 | return -EMSGSIZE; | ||
176 | for (; cb->args[2] <= last; cb->args[2]++) { | ||
177 | id = cb->args[2]; | ||
178 | elem = bitmap_ipmac_elem(map, id); | ||
179 | if (elem->match == MAC_EMPTY) | ||
180 | continue; | ||
181 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
182 | if (!nested) { | ||
183 | if (id == first) { | ||
184 | nla_nest_cancel(skb, atd); | ||
185 | return -EMSGSIZE; | ||
186 | } else | ||
187 | goto nla_put_failure; | ||
188 | } | ||
189 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, | ||
190 | htonl(map->first_ip + id)); | ||
191 | if (elem->match == MAC_FILLED) | ||
192 | NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, | ||
193 | elem->ether); | ||
194 | ipset_nest_end(skb, nested); | ||
195 | } | ||
196 | ipset_nest_end(skb, atd); | ||
197 | /* Set listing finished */ | ||
198 | cb->args[2] = 0; | ||
199 | |||
200 | return 0; | ||
201 | |||
202 | nla_put_failure: | ||
203 | nla_nest_cancel(skb, nested); | ||
204 | ipset_nest_end(skb, atd); | ||
205 | if (unlikely(id == first)) { | ||
206 | cb->args[2] = 0; | ||
207 | return -EMSGSIZE; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /* Timeout variant */ | ||
213 | |||
214 | static int | ||
215 | bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout) | ||
216 | { | ||
217 | const struct bitmap_ipmac *map = set->data; | ||
218 | const struct ipmac *data = value; | ||
219 | const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id); | ||
220 | |||
221 | switch (elem->match) { | ||
222 | case MAC_UNSET: | ||
223 | /* Trigger kernel to fill out the ethernet address */ | ||
224 | return -EAGAIN; | ||
225 | case MAC_FILLED: | ||
226 | return (data->ether == NULL || | ||
227 | compare_ether_addr(data->ether, elem->ether) == 0) && | ||
228 | !bitmap_expired(map, data->id); | ||
229 | } | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int | ||
234 | bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout) | ||
235 | { | ||
236 | struct bitmap_ipmac *map = set->data; | ||
237 | const struct ipmac *data = value; | ||
238 | struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); | ||
239 | |||
240 | switch (elem->match) { | ||
241 | case MAC_UNSET: | ||
242 | if (!data->ether) | ||
243 | /* Already added without ethernet address */ | ||
244 | return -IPSET_ERR_EXIST; | ||
245 | /* Fill the MAC address and activate the timer */ | ||
246 | memcpy(elem->ether, data->ether, ETH_ALEN); | ||
247 | elem->match = MAC_FILLED; | ||
248 | if (timeout == map->timeout) | ||
249 | /* Timeout was not specified, get stored one */ | ||
250 | timeout = elem->timeout; | ||
251 | elem->timeout = ip_set_timeout_set(timeout); | ||
252 | break; | ||
253 | case MAC_FILLED: | ||
254 | if (!bitmap_expired(map, data->id)) | ||
255 | return -IPSET_ERR_EXIST; | ||
256 | /* Fall through */ | ||
257 | case MAC_EMPTY: | ||
258 | if (data->ether) { | ||
259 | memcpy(elem->ether, data->ether, ETH_ALEN); | ||
260 | elem->match = MAC_FILLED; | ||
261 | } else | ||
262 | elem->match = MAC_UNSET; | ||
263 | /* If MAC is unset yet, we store plain timeout value | ||
264 | * because the timer is not activated yet | ||
265 | * and we can reuse it later when MAC is filled out, | ||
266 | * possibly by the kernel */ | ||
267 | elem->timeout = data->ether ? ip_set_timeout_set(timeout) | ||
268 | : timeout; | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int | ||
276 | bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout) | ||
277 | { | ||
278 | struct bitmap_ipmac *map = set->data; | ||
279 | const struct ipmac *data = value; | ||
280 | struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id); | ||
281 | |||
282 | if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id)) | ||
283 | return -IPSET_ERR_EXIST; | ||
284 | |||
285 | elem->match = MAC_EMPTY; | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int | ||
291 | bitmap_ipmac_tlist(const struct ip_set *set, | ||
292 | struct sk_buff *skb, struct netlink_callback *cb) | ||
293 | { | ||
294 | const struct bitmap_ipmac *map = set->data; | ||
295 | const struct ipmac_telem *elem; | ||
296 | struct nlattr *atd, *nested; | ||
297 | u32 id, first = cb->args[2]; | ||
298 | u32 timeout, last = map->last_ip - map->first_ip; | ||
299 | |||
300 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
301 | if (!atd) | ||
302 | return -EMSGSIZE; | ||
303 | for (; cb->args[2] <= last; cb->args[2]++) { | ||
304 | id = cb->args[2]; | ||
305 | elem = bitmap_ipmac_elem(map, id); | ||
306 | if (!bitmap_ipmac_exist(elem)) | ||
307 | continue; | ||
308 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
309 | if (!nested) { | ||
310 | if (id == first) { | ||
311 | nla_nest_cancel(skb, atd); | ||
312 | return -EMSGSIZE; | ||
313 | } else | ||
314 | goto nla_put_failure; | ||
315 | } | ||
316 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, | ||
317 | htonl(map->first_ip + id)); | ||
318 | if (elem->match == MAC_FILLED) | ||
319 | NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, | ||
320 | elem->ether); | ||
321 | timeout = elem->match == MAC_UNSET ? elem->timeout | ||
322 | : ip_set_timeout_get(elem->timeout); | ||
323 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout)); | ||
324 | ipset_nest_end(skb, nested); | ||
325 | } | ||
326 | ipset_nest_end(skb, atd); | ||
327 | /* Set listing finished */ | ||
328 | cb->args[2] = 0; | ||
329 | |||
330 | return 0; | ||
331 | |||
332 | nla_put_failure: | ||
333 | nla_nest_cancel(skb, nested); | ||
334 | ipset_nest_end(skb, atd); | ||
335 | return -EMSGSIZE; | ||
336 | } | ||
337 | |||
338 | static int | ||
339 | bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
340 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
341 | { | ||
342 | struct bitmap_ipmac *map = set->data; | ||
343 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
344 | struct ipmac data; | ||
345 | |||
346 | data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); | ||
347 | if (data.id < map->first_ip || data.id > map->last_ip) | ||
348 | return -IPSET_ERR_BITMAP_RANGE; | ||
349 | |||
350 | /* Backward compatibility: we don't check the second flag */ | ||
351 | if (skb_mac_header(skb) < skb->head || | ||
352 | (skb_mac_header(skb) + ETH_HLEN) > skb->data) | ||
353 | return -EINVAL; | ||
354 | |||
355 | data.id -= map->first_ip; | ||
356 | data.ether = eth_hdr(skb)->h_source; | ||
357 | |||
358 | return adtfn(set, &data, map->timeout); | ||
359 | } | ||
360 | |||
361 | static int | ||
362 | bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], | ||
363 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
364 | { | ||
365 | const struct bitmap_ipmac *map = set->data; | ||
366 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
367 | struct ipmac data; | ||
368 | u32 timeout = map->timeout; | ||
369 | int ret = 0; | ||
370 | |||
371 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
372 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
373 | return -IPSET_ERR_PROTOCOL; | ||
374 | |||
375 | if (tb[IPSET_ATTR_LINENO]) | ||
376 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
377 | |||
378 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &data.id); | ||
379 | if (ret) | ||
380 | return ret; | ||
381 | |||
382 | if (data.id < map->first_ip || data.id > map->last_ip) | ||
383 | return -IPSET_ERR_BITMAP_RANGE; | ||
384 | |||
385 | if (tb[IPSET_ATTR_ETHER]) | ||
386 | data.ether = nla_data(tb[IPSET_ATTR_ETHER]); | ||
387 | else | ||
388 | data.ether = NULL; | ||
389 | |||
390 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
391 | if (!with_timeout(map->timeout)) | ||
392 | return -IPSET_ERR_TIMEOUT; | ||
393 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
394 | } | ||
395 | |||
396 | data.id -= map->first_ip; | ||
397 | |||
398 | ret = adtfn(set, &data, timeout); | ||
399 | |||
400 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
401 | } | ||
402 | |||
403 | static void | ||
404 | bitmap_ipmac_destroy(struct ip_set *set) | ||
405 | { | ||
406 | struct bitmap_ipmac *map = set->data; | ||
407 | |||
408 | if (with_timeout(map->timeout)) | ||
409 | del_timer_sync(&map->gc); | ||
410 | |||
411 | ip_set_free(map->members); | ||
412 | kfree(map); | ||
413 | |||
414 | set->data = NULL; | ||
415 | } | ||
416 | |||
417 | static void | ||
418 | bitmap_ipmac_flush(struct ip_set *set) | ||
419 | { | ||
420 | struct bitmap_ipmac *map = set->data; | ||
421 | |||
422 | memset(map->members, 0, | ||
423 | (map->last_ip - map->first_ip + 1) * map->dsize); | ||
424 | } | ||
425 | |||
426 | static int | ||
427 | bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb) | ||
428 | { | ||
429 | const struct bitmap_ipmac *map = set->data; | ||
430 | struct nlattr *nested; | ||
431 | |||
432 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
433 | if (!nested) | ||
434 | goto nla_put_failure; | ||
435 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); | ||
436 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); | ||
437 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | ||
438 | htonl(atomic_read(&set->ref) - 1)); | ||
439 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | ||
440 | htonl(sizeof(*map) | ||
441 | + (map->last_ip - map->first_ip + 1) * map->dsize)); | ||
442 | if (with_timeout(map->timeout)) | ||
443 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | ||
444 | ipset_nest_end(skb, nested); | ||
445 | |||
446 | return 0; | ||
447 | nla_put_failure: | ||
448 | return -EMSGSIZE; | ||
449 | } | ||
450 | |||
451 | static bool | ||
452 | bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b) | ||
453 | { | ||
454 | const struct bitmap_ipmac *x = a->data; | ||
455 | const struct bitmap_ipmac *y = b->data; | ||
456 | |||
457 | return x->first_ip == y->first_ip && | ||
458 | x->last_ip == y->last_ip && | ||
459 | x->timeout == y->timeout; | ||
460 | } | ||
461 | |||
462 | static const struct ip_set_type_variant bitmap_ipmac = { | ||
463 | .kadt = bitmap_ipmac_kadt, | ||
464 | .uadt = bitmap_ipmac_uadt, | ||
465 | .adt = { | ||
466 | [IPSET_ADD] = bitmap_ipmac_add, | ||
467 | [IPSET_DEL] = bitmap_ipmac_del, | ||
468 | [IPSET_TEST] = bitmap_ipmac_test, | ||
469 | }, | ||
470 | .destroy = bitmap_ipmac_destroy, | ||
471 | .flush = bitmap_ipmac_flush, | ||
472 | .head = bitmap_ipmac_head, | ||
473 | .list = bitmap_ipmac_list, | ||
474 | .same_set = bitmap_ipmac_same_set, | ||
475 | }; | ||
476 | |||
477 | static const struct ip_set_type_variant bitmap_tipmac = { | ||
478 | .kadt = bitmap_ipmac_kadt, | ||
479 | .uadt = bitmap_ipmac_uadt, | ||
480 | .adt = { | ||
481 | [IPSET_ADD] = bitmap_ipmac_tadd, | ||
482 | [IPSET_DEL] = bitmap_ipmac_tdel, | ||
483 | [IPSET_TEST] = bitmap_ipmac_ttest, | ||
484 | }, | ||
485 | .destroy = bitmap_ipmac_destroy, | ||
486 | .flush = bitmap_ipmac_flush, | ||
487 | .head = bitmap_ipmac_head, | ||
488 | .list = bitmap_ipmac_tlist, | ||
489 | .same_set = bitmap_ipmac_same_set, | ||
490 | }; | ||
491 | |||
492 | static void | ||
493 | bitmap_ipmac_gc(unsigned long ul_set) | ||
494 | { | ||
495 | struct ip_set *set = (struct ip_set *) ul_set; | ||
496 | struct bitmap_ipmac *map = set->data; | ||
497 | struct ipmac_telem *elem; | ||
498 | u32 id, last = map->last_ip - map->first_ip; | ||
499 | |||
500 | /* We run parallel with other readers (test element) | ||
501 | * but adding/deleting new entries is locked out */ | ||
502 | read_lock_bh(&set->lock); | ||
503 | for (id = 0; id <= last; id++) { | ||
504 | elem = bitmap_ipmac_elem(map, id); | ||
505 | if (elem->match == MAC_FILLED && | ||
506 | ip_set_timeout_expired(elem->timeout)) | ||
507 | elem->match = MAC_EMPTY; | ||
508 | } | ||
509 | read_unlock_bh(&set->lock); | ||
510 | |||
511 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
512 | add_timer(&map->gc); | ||
513 | } | ||
514 | |||
515 | static void | ||
516 | bitmap_ipmac_gc_init(struct ip_set *set) | ||
517 | { | ||
518 | struct bitmap_ipmac *map = set->data; | ||
519 | |||
520 | init_timer(&map->gc); | ||
521 | map->gc.data = (unsigned long) set; | ||
522 | map->gc.function = bitmap_ipmac_gc; | ||
523 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
524 | add_timer(&map->gc); | ||
525 | } | ||
526 | |||
527 | /* Create bitmap:ip,mac type of sets */ | ||
528 | |||
529 | static bool | ||
530 | init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, | ||
531 | u32 first_ip, u32 last_ip) | ||
532 | { | ||
533 | map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize); | ||
534 | if (!map->members) | ||
535 | return false; | ||
536 | map->first_ip = first_ip; | ||
537 | map->last_ip = last_ip; | ||
538 | map->timeout = IPSET_NO_TIMEOUT; | ||
539 | |||
540 | set->data = map; | ||
541 | set->family = AF_INET; | ||
542 | |||
543 | return true; | ||
544 | } | ||
545 | |||
546 | static int | ||
547 | bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], | ||
548 | u32 flags) | ||
549 | { | ||
550 | u32 first_ip, last_ip, elements; | ||
551 | struct bitmap_ipmac *map; | ||
552 | int ret; | ||
553 | |||
554 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
555 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
556 | return -IPSET_ERR_PROTOCOL; | ||
557 | |||
558 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip); | ||
559 | if (ret) | ||
560 | return ret; | ||
561 | |||
562 | if (tb[IPSET_ATTR_IP_TO]) { | ||
563 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip); | ||
564 | if (ret) | ||
565 | return ret; | ||
566 | if (first_ip > last_ip) { | ||
567 | u32 tmp = first_ip; | ||
568 | |||
569 | first_ip = last_ip; | ||
570 | last_ip = tmp; | ||
571 | } | ||
572 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
573 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
574 | |||
575 | if (cidr >= 32) | ||
576 | return -IPSET_ERR_INVALID_CIDR; | ||
577 | last_ip = first_ip | ~ip_set_hostmask(cidr); | ||
578 | } else | ||
579 | return -IPSET_ERR_PROTOCOL; | ||
580 | |||
581 | elements = last_ip - first_ip + 1; | ||
582 | |||
583 | if (elements > IPSET_BITMAP_MAX_RANGE + 1) | ||
584 | return -IPSET_ERR_BITMAP_RANGE_SIZE; | ||
585 | |||
586 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
587 | if (!map) | ||
588 | return -ENOMEM; | ||
589 | |||
590 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
591 | map->dsize = sizeof(struct ipmac_telem); | ||
592 | |||
593 | if (!init_map_ipmac(set, map, first_ip, last_ip)) { | ||
594 | kfree(map); | ||
595 | return -ENOMEM; | ||
596 | } | ||
597 | |||
598 | map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
599 | |||
600 | set->variant = &bitmap_tipmac; | ||
601 | |||
602 | bitmap_ipmac_gc_init(set); | ||
603 | } else { | ||
604 | map->dsize = sizeof(struct ipmac_elem); | ||
605 | |||
606 | if (!init_map_ipmac(set, map, first_ip, last_ip)) { | ||
607 | kfree(map); | ||
608 | return -ENOMEM; | ||
609 | } | ||
610 | set->variant = &bitmap_ipmac; | ||
611 | |||
612 | } | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | static struct ip_set_type bitmap_ipmac_type = { | ||
617 | .name = "bitmap:ip,mac", | ||
618 | .protocol = IPSET_PROTOCOL, | ||
619 | .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, | ||
620 | .dimension = IPSET_DIM_TWO, | ||
621 | .family = AF_INET, | ||
622 | .revision = 0, | ||
623 | .create = bitmap_ipmac_create, | ||
624 | .create_policy = { | ||
625 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
626 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
627 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
628 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
629 | }, | ||
630 | .adt_policy = { | ||
631 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
632 | [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN }, | ||
633 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
634 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
635 | }, | ||
636 | .me = THIS_MODULE, | ||
637 | }; | ||
638 | |||
639 | static int __init | ||
640 | bitmap_ipmac_init(void) | ||
641 | { | ||
642 | return ip_set_type_register(&bitmap_ipmac_type); | ||
643 | } | ||
644 | |||
645 | static void __exit | ||
646 | bitmap_ipmac_fini(void) | ||
647 | { | ||
648 | ip_set_type_unregister(&bitmap_ipmac_type); | ||
649 | } | ||
650 | |||
651 | module_init(bitmap_ipmac_init); | ||
652 | module_exit(bitmap_ipmac_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c new file mode 100644 index 000000000000..165f09b1a9cb --- /dev/null +++ b/net/netfilter/ipset/ip_set_bitmap_port.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* 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 bitmap:port type */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/netlink.h> | ||
15 | #include <linux/jiffies.h> | ||
16 | #include <linux/timer.h> | ||
17 | #include <net/netlink.h> | ||
18 | |||
19 | #include <linux/netfilter/ipset/ip_set.h> | ||
20 | #include <linux/netfilter/ipset/ip_set_bitmap.h> | ||
21 | #include <linux/netfilter/ipset/ip_set_getport.h> | ||
22 | #define IP_SET_BITMAP_TIMEOUT | ||
23 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
24 | |||
25 | MODULE_LICENSE("GPL"); | ||
26 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
27 | MODULE_DESCRIPTION("bitmap:port type of IP sets"); | ||
28 | MODULE_ALIAS("ip_set_bitmap:port"); | ||
29 | |||
30 | /* Type structure */ | ||
31 | struct bitmap_port { | ||
32 | void *members; /* the set members */ | ||
33 | u16 first_port; /* host byte order, included in range */ | ||
34 | u16 last_port; /* host byte order, included in range */ | ||
35 | size_t memsize; /* members size */ | ||
36 | u32 timeout; /* timeout parameter */ | ||
37 | struct timer_list gc; /* garbage collection */ | ||
38 | }; | ||
39 | |||
40 | /* Base variant */ | ||
41 | |||
42 | static int | ||
43 | bitmap_port_test(struct ip_set *set, void *value, u32 timeout) | ||
44 | { | ||
45 | const struct bitmap_port *map = set->data; | ||
46 | u16 id = *(u16 *)value; | ||
47 | |||
48 | return !!test_bit(id, map->members); | ||
49 | } | ||
50 | |||
51 | static int | ||
52 | bitmap_port_add(struct ip_set *set, void *value, u32 timeout) | ||
53 | { | ||
54 | struct bitmap_port *map = set->data; | ||
55 | u16 id = *(u16 *)value; | ||
56 | |||
57 | if (test_and_set_bit(id, map->members)) | ||
58 | return -IPSET_ERR_EXIST; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static int | ||
64 | bitmap_port_del(struct ip_set *set, void *value, u32 timeout) | ||
65 | { | ||
66 | struct bitmap_port *map = set->data; | ||
67 | u16 id = *(u16 *)value; | ||
68 | |||
69 | if (!test_and_clear_bit(id, map->members)) | ||
70 | return -IPSET_ERR_EXIST; | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int | ||
76 | bitmap_port_list(const struct ip_set *set, | ||
77 | struct sk_buff *skb, struct netlink_callback *cb) | ||
78 | { | ||
79 | const struct bitmap_port *map = set->data; | ||
80 | struct nlattr *atd, *nested; | ||
81 | u16 id, first = cb->args[2]; | ||
82 | u16 last = map->last_port - map->first_port; | ||
83 | |||
84 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
85 | if (!atd) | ||
86 | return -EMSGSIZE; | ||
87 | for (; cb->args[2] <= last; cb->args[2]++) { | ||
88 | id = cb->args[2]; | ||
89 | if (!test_bit(id, map->members)) | ||
90 | continue; | ||
91 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
92 | if (!nested) { | ||
93 | if (id == first) { | ||
94 | nla_nest_cancel(skb, atd); | ||
95 | return -EMSGSIZE; | ||
96 | } else | ||
97 | goto nla_put_failure; | ||
98 | } | ||
99 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, | ||
100 | htons(map->first_port + id)); | ||
101 | ipset_nest_end(skb, nested); | ||
102 | } | ||
103 | ipset_nest_end(skb, atd); | ||
104 | /* Set listing finished */ | ||
105 | cb->args[2] = 0; | ||
106 | |||
107 | return 0; | ||
108 | |||
109 | nla_put_failure: | ||
110 | nla_nest_cancel(skb, nested); | ||
111 | ipset_nest_end(skb, atd); | ||
112 | if (unlikely(id == first)) { | ||
113 | cb->args[2] = 0; | ||
114 | return -EMSGSIZE; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* Timeout variant */ | ||
120 | |||
121 | static int | ||
122 | bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout) | ||
123 | { | ||
124 | const struct bitmap_port *map = set->data; | ||
125 | const unsigned long *members = map->members; | ||
126 | u16 id = *(u16 *)value; | ||
127 | |||
128 | return ip_set_timeout_test(members[id]); | ||
129 | } | ||
130 | |||
131 | static int | ||
132 | bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout) | ||
133 | { | ||
134 | struct bitmap_port *map = set->data; | ||
135 | unsigned long *members = map->members; | ||
136 | u16 id = *(u16 *)value; | ||
137 | |||
138 | if (ip_set_timeout_test(members[id])) | ||
139 | return -IPSET_ERR_EXIST; | ||
140 | |||
141 | members[id] = ip_set_timeout_set(timeout); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int | ||
147 | bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout) | ||
148 | { | ||
149 | struct bitmap_port *map = set->data; | ||
150 | unsigned long *members = map->members; | ||
151 | u16 id = *(u16 *)value; | ||
152 | int ret = -IPSET_ERR_EXIST; | ||
153 | |||
154 | if (ip_set_timeout_test(members[id])) | ||
155 | ret = 0; | ||
156 | |||
157 | members[id] = IPSET_ELEM_UNSET; | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | static int | ||
162 | bitmap_port_tlist(const struct ip_set *set, | ||
163 | struct sk_buff *skb, struct netlink_callback *cb) | ||
164 | { | ||
165 | const struct bitmap_port *map = set->data; | ||
166 | struct nlattr *adt, *nested; | ||
167 | u16 id, first = cb->args[2]; | ||
168 | u16 last = map->last_port - map->first_port; | ||
169 | const unsigned long *members = map->members; | ||
170 | |||
171 | adt = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
172 | if (!adt) | ||
173 | return -EMSGSIZE; | ||
174 | for (; cb->args[2] <= last; cb->args[2]++) { | ||
175 | id = cb->args[2]; | ||
176 | if (!ip_set_timeout_test(members[id])) | ||
177 | continue; | ||
178 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
179 | if (!nested) { | ||
180 | if (id == first) { | ||
181 | nla_nest_cancel(skb, adt); | ||
182 | return -EMSGSIZE; | ||
183 | } else | ||
184 | goto nla_put_failure; | ||
185 | } | ||
186 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, | ||
187 | htons(map->first_port + id)); | ||
188 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
189 | htonl(ip_set_timeout_get(members[id]))); | ||
190 | ipset_nest_end(skb, nested); | ||
191 | } | ||
192 | ipset_nest_end(skb, adt); | ||
193 | |||
194 | /* Set listing finished */ | ||
195 | cb->args[2] = 0; | ||
196 | |||
197 | return 0; | ||
198 | |||
199 | nla_put_failure: | ||
200 | nla_nest_cancel(skb, nested); | ||
201 | ipset_nest_end(skb, adt); | ||
202 | if (unlikely(id == first)) { | ||
203 | cb->args[2] = 0; | ||
204 | return -EMSGSIZE; | ||
205 | } | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int | ||
210 | bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
211 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
212 | { | ||
213 | struct bitmap_port *map = set->data; | ||
214 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
215 | __be16 __port; | ||
216 | u16 port = 0; | ||
217 | |||
218 | if (!ip_set_get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &__port)) | ||
219 | return -EINVAL; | ||
220 | |||
221 | port = ntohs(__port); | ||
222 | |||
223 | if (port < map->first_port || port > map->last_port) | ||
224 | return -IPSET_ERR_BITMAP_RANGE; | ||
225 | |||
226 | port -= map->first_port; | ||
227 | |||
228 | return adtfn(set, &port, map->timeout); | ||
229 | } | ||
230 | |||
231 | static int | ||
232 | bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], | ||
233 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
234 | { | ||
235 | struct bitmap_port *map = set->data; | ||
236 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
237 | u32 timeout = map->timeout; | ||
238 | u32 port; /* wraparound */ | ||
239 | u16 id, port_to; | ||
240 | int ret = 0; | ||
241 | |||
242 | if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
243 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
244 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
245 | return -IPSET_ERR_PROTOCOL; | ||
246 | |||
247 | if (tb[IPSET_ATTR_LINENO]) | ||
248 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
249 | |||
250 | port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); | ||
251 | if (port < map->first_port || port > map->last_port) | ||
252 | return -IPSET_ERR_BITMAP_RANGE; | ||
253 | |||
254 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
255 | if (!with_timeout(map->timeout)) | ||
256 | return -IPSET_ERR_TIMEOUT; | ||
257 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
258 | } | ||
259 | |||
260 | if (adt == IPSET_TEST) { | ||
261 | id = port - map->first_port; | ||
262 | return adtfn(set, &id, timeout); | ||
263 | } | ||
264 | |||
265 | if (tb[IPSET_ATTR_PORT_TO]) { | ||
266 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
267 | if (port > port_to) { | ||
268 | swap(port, port_to); | ||
269 | if (port < map->first_port) | ||
270 | return -IPSET_ERR_BITMAP_RANGE; | ||
271 | } | ||
272 | } else | ||
273 | port_to = port; | ||
274 | |||
275 | if (port_to > map->last_port) | ||
276 | return -IPSET_ERR_BITMAP_RANGE; | ||
277 | |||
278 | for (; port <= port_to; port++) { | ||
279 | id = port - map->first_port; | ||
280 | ret = adtfn(set, &id, timeout); | ||
281 | |||
282 | if (ret && !ip_set_eexist(ret, flags)) | ||
283 | return ret; | ||
284 | else | ||
285 | ret = 0; | ||
286 | } | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | static void | ||
291 | bitmap_port_destroy(struct ip_set *set) | ||
292 | { | ||
293 | struct bitmap_port *map = set->data; | ||
294 | |||
295 | if (with_timeout(map->timeout)) | ||
296 | del_timer_sync(&map->gc); | ||
297 | |||
298 | ip_set_free(map->members); | ||
299 | kfree(map); | ||
300 | |||
301 | set->data = NULL; | ||
302 | } | ||
303 | |||
304 | static void | ||
305 | bitmap_port_flush(struct ip_set *set) | ||
306 | { | ||
307 | struct bitmap_port *map = set->data; | ||
308 | |||
309 | memset(map->members, 0, map->memsize); | ||
310 | } | ||
311 | |||
312 | static int | ||
313 | bitmap_port_head(struct ip_set *set, struct sk_buff *skb) | ||
314 | { | ||
315 | const struct bitmap_port *map = set->data; | ||
316 | struct nlattr *nested; | ||
317 | |||
318 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
319 | if (!nested) | ||
320 | goto nla_put_failure; | ||
321 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); | ||
322 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); | ||
323 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | ||
324 | htonl(atomic_read(&set->ref) - 1)); | ||
325 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | ||
326 | htonl(sizeof(*map) + map->memsize)); | ||
327 | if (with_timeout(map->timeout)) | ||
328 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | ||
329 | ipset_nest_end(skb, nested); | ||
330 | |||
331 | return 0; | ||
332 | nla_put_failure: | ||
333 | return -EMSGSIZE; | ||
334 | } | ||
335 | |||
336 | static bool | ||
337 | bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) | ||
338 | { | ||
339 | const struct bitmap_port *x = a->data; | ||
340 | const struct bitmap_port *y = b->data; | ||
341 | |||
342 | return x->first_port == y->first_port && | ||
343 | x->last_port == y->last_port && | ||
344 | x->timeout == y->timeout; | ||
345 | } | ||
346 | |||
347 | static const struct ip_set_type_variant bitmap_port = { | ||
348 | .kadt = bitmap_port_kadt, | ||
349 | .uadt = bitmap_port_uadt, | ||
350 | .adt = { | ||
351 | [IPSET_ADD] = bitmap_port_add, | ||
352 | [IPSET_DEL] = bitmap_port_del, | ||
353 | [IPSET_TEST] = bitmap_port_test, | ||
354 | }, | ||
355 | .destroy = bitmap_port_destroy, | ||
356 | .flush = bitmap_port_flush, | ||
357 | .head = bitmap_port_head, | ||
358 | .list = bitmap_port_list, | ||
359 | .same_set = bitmap_port_same_set, | ||
360 | }; | ||
361 | |||
362 | static const struct ip_set_type_variant bitmap_tport = { | ||
363 | .kadt = bitmap_port_kadt, | ||
364 | .uadt = bitmap_port_uadt, | ||
365 | .adt = { | ||
366 | [IPSET_ADD] = bitmap_port_tadd, | ||
367 | [IPSET_DEL] = bitmap_port_tdel, | ||
368 | [IPSET_TEST] = bitmap_port_ttest, | ||
369 | }, | ||
370 | .destroy = bitmap_port_destroy, | ||
371 | .flush = bitmap_port_flush, | ||
372 | .head = bitmap_port_head, | ||
373 | .list = bitmap_port_tlist, | ||
374 | .same_set = bitmap_port_same_set, | ||
375 | }; | ||
376 | |||
377 | static void | ||
378 | bitmap_port_gc(unsigned long ul_set) | ||
379 | { | ||
380 | struct ip_set *set = (struct ip_set *) ul_set; | ||
381 | struct bitmap_port *map = set->data; | ||
382 | unsigned long *table = map->members; | ||
383 | u32 id; /* wraparound */ | ||
384 | u16 last = map->last_port - map->first_port; | ||
385 | |||
386 | /* We run parallel with other readers (test element) | ||
387 | * but adding/deleting new entries is locked out */ | ||
388 | read_lock_bh(&set->lock); | ||
389 | for (id = 0; id <= last; id++) | ||
390 | if (ip_set_timeout_expired(table[id])) | ||
391 | table[id] = IPSET_ELEM_UNSET; | ||
392 | read_unlock_bh(&set->lock); | ||
393 | |||
394 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
395 | add_timer(&map->gc); | ||
396 | } | ||
397 | |||
398 | static void | ||
399 | bitmap_port_gc_init(struct ip_set *set) | ||
400 | { | ||
401 | struct bitmap_port *map = set->data; | ||
402 | |||
403 | init_timer(&map->gc); | ||
404 | map->gc.data = (unsigned long) set; | ||
405 | map->gc.function = bitmap_port_gc; | ||
406 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
407 | add_timer(&map->gc); | ||
408 | } | ||
409 | |||
410 | /* Create bitmap:ip type of sets */ | ||
411 | |||
412 | static bool | ||
413 | init_map_port(struct ip_set *set, struct bitmap_port *map, | ||
414 | u16 first_port, u16 last_port) | ||
415 | { | ||
416 | map->members = ip_set_alloc(map->memsize); | ||
417 | if (!map->members) | ||
418 | return false; | ||
419 | map->first_port = first_port; | ||
420 | map->last_port = last_port; | ||
421 | map->timeout = IPSET_NO_TIMEOUT; | ||
422 | |||
423 | set->data = map; | ||
424 | set->family = AF_UNSPEC; | ||
425 | |||
426 | return true; | ||
427 | } | ||
428 | |||
429 | static int | ||
430 | bitmap_port_create(struct ip_set *set, struct nlattr *tb[], | ||
431 | u32 flags) | ||
432 | { | ||
433 | struct bitmap_port *map; | ||
434 | u16 first_port, last_port; | ||
435 | |||
436 | if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
437 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
438 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
439 | return -IPSET_ERR_PROTOCOL; | ||
440 | |||
441 | first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); | ||
442 | last_port = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
443 | if (first_port > last_port) { | ||
444 | u16 tmp = first_port; | ||
445 | |||
446 | first_port = last_port; | ||
447 | last_port = tmp; | ||
448 | } | ||
449 | |||
450 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
451 | if (!map) | ||
452 | return -ENOMEM; | ||
453 | |||
454 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
455 | map->memsize = (last_port - first_port + 1) | ||
456 | * sizeof(unsigned long); | ||
457 | |||
458 | if (!init_map_port(set, map, first_port, last_port)) { | ||
459 | kfree(map); | ||
460 | return -ENOMEM; | ||
461 | } | ||
462 | |||
463 | map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
464 | set->variant = &bitmap_tport; | ||
465 | |||
466 | bitmap_port_gc_init(set); | ||
467 | } else { | ||
468 | map->memsize = bitmap_bytes(0, last_port - first_port); | ||
469 | pr_debug("memsize: %zu\n", map->memsize); | ||
470 | if (!init_map_port(set, map, first_port, last_port)) { | ||
471 | kfree(map); | ||
472 | return -ENOMEM; | ||
473 | } | ||
474 | |||
475 | set->variant = &bitmap_port; | ||
476 | } | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static struct ip_set_type bitmap_port_type = { | ||
481 | .name = "bitmap:port", | ||
482 | .protocol = IPSET_PROTOCOL, | ||
483 | .features = IPSET_TYPE_PORT, | ||
484 | .dimension = IPSET_DIM_ONE, | ||
485 | .family = AF_UNSPEC, | ||
486 | .revision = 0, | ||
487 | .create = bitmap_port_create, | ||
488 | .create_policy = { | ||
489 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
490 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
491 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
492 | }, | ||
493 | .adt_policy = { | ||
494 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
495 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
496 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
497 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
498 | }, | ||
499 | .me = THIS_MODULE, | ||
500 | }; | ||
501 | |||
502 | static int __init | ||
503 | bitmap_port_init(void) | ||
504 | { | ||
505 | return ip_set_type_register(&bitmap_port_type); | ||
506 | } | ||
507 | |||
508 | static void __exit | ||
509 | bitmap_port_fini(void) | ||
510 | { | ||
511 | ip_set_type_unregister(&bitmap_port_type); | ||
512 | } | ||
513 | |||
514 | module_init(bitmap_port_init); | ||
515 | module_exit(bitmap_port_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c new file mode 100644 index 000000000000..8b1a54c1e400 --- /dev/null +++ b/net/netfilter/ipset/ip_set_core.c | |||
@@ -0,0 +1,1671 @@ | |||
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
2 | * Patrick Schaaf <bof@bof.de> | ||
3 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | /* Kernel module for IP set management */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/ip.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/netlink.h> | ||
19 | #include <linux/rculist.h> | ||
20 | #include <linux/version.h> | ||
21 | #include <net/netlink.h> | ||
22 | |||
23 | #include <linux/netfilter.h> | ||
24 | #include <linux/netfilter/nfnetlink.h> | ||
25 | #include <linux/netfilter/ipset/ip_set.h> | ||
26 | |||
27 | static LIST_HEAD(ip_set_type_list); /* all registered set types */ | ||
28 | static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ | ||
29 | |||
30 | static struct ip_set **ip_set_list; /* all individual sets */ | ||
31 | static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ | ||
32 | |||
33 | #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) | ||
34 | |||
35 | static unsigned int max_sets; | ||
36 | |||
37 | module_param(max_sets, int, 0600); | ||
38 | MODULE_PARM_DESC(max_sets, "maximal number of sets"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
41 | MODULE_DESCRIPTION("core IP set support"); | ||
42 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); | ||
43 | |||
44 | /* | ||
45 | * The set types are implemented in modules and registered set types | ||
46 | * can be found in ip_set_type_list. Adding/deleting types is | ||
47 | * serialized by ip_set_type_mutex. | ||
48 | */ | ||
49 | |||
50 | static inline void | ||
51 | ip_set_type_lock(void) | ||
52 | { | ||
53 | mutex_lock(&ip_set_type_mutex); | ||
54 | } | ||
55 | |||
56 | static inline void | ||
57 | ip_set_type_unlock(void) | ||
58 | { | ||
59 | mutex_unlock(&ip_set_type_mutex); | ||
60 | } | ||
61 | |||
62 | /* Register and deregister settype */ | ||
63 | |||
64 | static struct ip_set_type * | ||
65 | find_set_type(const char *name, u8 family, u8 revision) | ||
66 | { | ||
67 | struct ip_set_type *type; | ||
68 | |||
69 | list_for_each_entry_rcu(type, &ip_set_type_list, list) | ||
70 | if (STREQ(type->name, name) && | ||
71 | (type->family == family || type->family == AF_UNSPEC) && | ||
72 | type->revision == revision) | ||
73 | return type; | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | /* Unlock, try to load a set type module and lock again */ | ||
78 | static int | ||
79 | try_to_load_type(const char *name) | ||
80 | { | ||
81 | nfnl_unlock(); | ||
82 | pr_debug("try to load ip_set_%s\n", name); | ||
83 | if (request_module("ip_set_%s", name) < 0) { | ||
84 | pr_warning("Can't find ip_set type %s\n", name); | ||
85 | nfnl_lock(); | ||
86 | return -IPSET_ERR_FIND_TYPE; | ||
87 | } | ||
88 | nfnl_lock(); | ||
89 | return -EAGAIN; | ||
90 | } | ||
91 | |||
92 | /* Find a set type and reference it */ | ||
93 | static int | ||
94 | find_set_type_get(const char *name, u8 family, u8 revision, | ||
95 | struct ip_set_type **found) | ||
96 | { | ||
97 | rcu_read_lock(); | ||
98 | *found = find_set_type(name, family, revision); | ||
99 | if (*found) { | ||
100 | int err = !try_module_get((*found)->me); | ||
101 | rcu_read_unlock(); | ||
102 | return err ? -EFAULT : 0; | ||
103 | } | ||
104 | rcu_read_unlock(); | ||
105 | |||
106 | return try_to_load_type(name); | ||
107 | } | ||
108 | |||
109 | /* Find a given set type by name and family. | ||
110 | * If we succeeded, the supported minimal and maximum revisions are | ||
111 | * filled out. | ||
112 | */ | ||
113 | static int | ||
114 | find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) | ||
115 | { | ||
116 | struct ip_set_type *type; | ||
117 | bool found = false; | ||
118 | |||
119 | *min = *max = 0; | ||
120 | rcu_read_lock(); | ||
121 | list_for_each_entry_rcu(type, &ip_set_type_list, list) | ||
122 | if (STREQ(type->name, name) && | ||
123 | (type->family == family || type->family == AF_UNSPEC)) { | ||
124 | found = true; | ||
125 | if (type->revision < *min) | ||
126 | *min = type->revision; | ||
127 | else if (type->revision > *max) | ||
128 | *max = type->revision; | ||
129 | } | ||
130 | rcu_read_unlock(); | ||
131 | if (found) | ||
132 | return 0; | ||
133 | |||
134 | return try_to_load_type(name); | ||
135 | } | ||
136 | |||
137 | #define family_name(f) ((f) == AF_INET ? "inet" : \ | ||
138 | (f) == AF_INET6 ? "inet6" : "any") | ||
139 | |||
140 | /* Register a set type structure. The type is identified by | ||
141 | * the unique triple of name, family and revision. | ||
142 | */ | ||
143 | int | ||
144 | ip_set_type_register(struct ip_set_type *type) | ||
145 | { | ||
146 | int ret = 0; | ||
147 | |||
148 | if (type->protocol != IPSET_PROTOCOL) { | ||
149 | pr_warning("ip_set type %s, family %s, revision %u uses " | ||
150 | "wrong protocol version %u (want %u)\n", | ||
151 | type->name, family_name(type->family), | ||
152 | type->revision, type->protocol, IPSET_PROTOCOL); | ||
153 | return -EINVAL; | ||
154 | } | ||
155 | |||
156 | ip_set_type_lock(); | ||
157 | if (find_set_type(type->name, type->family, type->revision)) { | ||
158 | /* Duplicate! */ | ||
159 | pr_warning("ip_set type %s, family %s, revision %u " | ||
160 | "already registered!\n", type->name, | ||
161 | family_name(type->family), type->revision); | ||
162 | ret = -EINVAL; | ||
163 | goto unlock; | ||
164 | } | ||
165 | list_add_rcu(&type->list, &ip_set_type_list); | ||
166 | pr_debug("type %s, family %s, revision %u registered.\n", | ||
167 | type->name, family_name(type->family), type->revision); | ||
168 | unlock: | ||
169 | ip_set_type_unlock(); | ||
170 | return ret; | ||
171 | } | ||
172 | EXPORT_SYMBOL_GPL(ip_set_type_register); | ||
173 | |||
174 | /* Unregister a set type. There's a small race with ip_set_create */ | ||
175 | void | ||
176 | ip_set_type_unregister(struct ip_set_type *type) | ||
177 | { | ||
178 | ip_set_type_lock(); | ||
179 | if (!find_set_type(type->name, type->family, type->revision)) { | ||
180 | pr_warning("ip_set type %s, family %s, revision %u " | ||
181 | "not registered\n", type->name, | ||
182 | family_name(type->family), type->revision); | ||
183 | goto unlock; | ||
184 | } | ||
185 | list_del_rcu(&type->list); | ||
186 | pr_debug("type %s, family %s, revision %u unregistered.\n", | ||
187 | type->name, family_name(type->family), type->revision); | ||
188 | unlock: | ||
189 | ip_set_type_unlock(); | ||
190 | |||
191 | synchronize_rcu(); | ||
192 | } | ||
193 | EXPORT_SYMBOL_GPL(ip_set_type_unregister); | ||
194 | |||
195 | /* Utility functions */ | ||
196 | void * | ||
197 | ip_set_alloc(size_t size) | ||
198 | { | ||
199 | void *members = NULL; | ||
200 | |||
201 | if (size < KMALLOC_MAX_SIZE) | ||
202 | members = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); | ||
203 | |||
204 | if (members) { | ||
205 | pr_debug("%p: allocated with kmalloc\n", members); | ||
206 | return members; | ||
207 | } | ||
208 | |||
209 | members = vzalloc(size); | ||
210 | if (!members) | ||
211 | return NULL; | ||
212 | pr_debug("%p: allocated with vmalloc\n", members); | ||
213 | |||
214 | return members; | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(ip_set_alloc); | ||
217 | |||
218 | void | ||
219 | ip_set_free(void *members) | ||
220 | { | ||
221 | pr_debug("%p: free with %s\n", members, | ||
222 | is_vmalloc_addr(members) ? "vfree" : "kfree"); | ||
223 | if (is_vmalloc_addr(members)) | ||
224 | vfree(members); | ||
225 | else | ||
226 | kfree(members); | ||
227 | } | ||
228 | EXPORT_SYMBOL_GPL(ip_set_free); | ||
229 | |||
230 | static inline bool | ||
231 | flag_nested(const struct nlattr *nla) | ||
232 | { | ||
233 | return nla->nla_type & NLA_F_NESTED; | ||
234 | } | ||
235 | |||
236 | static const struct nla_policy ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] = { | ||
237 | [IPSET_ATTR_IPADDR_IPV4] = { .type = NLA_U32 }, | ||
238 | [IPSET_ATTR_IPADDR_IPV6] = { .type = NLA_BINARY, | ||
239 | .len = sizeof(struct in6_addr) }, | ||
240 | }; | ||
241 | |||
242 | int | ||
243 | ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr) | ||
244 | { | ||
245 | struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1]; | ||
246 | |||
247 | if (unlikely(!flag_nested(nla))) | ||
248 | return -IPSET_ERR_PROTOCOL; | ||
249 | if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, ipaddr_policy)) | ||
250 | return -IPSET_ERR_PROTOCOL; | ||
251 | if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV4))) | ||
252 | return -IPSET_ERR_PROTOCOL; | ||
253 | |||
254 | *ipaddr = nla_get_be32(tb[IPSET_ATTR_IPADDR_IPV4]); | ||
255 | return 0; | ||
256 | } | ||
257 | EXPORT_SYMBOL_GPL(ip_set_get_ipaddr4); | ||
258 | |||
259 | int | ||
260 | ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) | ||
261 | { | ||
262 | struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1]; | ||
263 | |||
264 | if (unlikely(!flag_nested(nla))) | ||
265 | return -IPSET_ERR_PROTOCOL; | ||
266 | |||
267 | if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, ipaddr_policy)) | ||
268 | return -IPSET_ERR_PROTOCOL; | ||
269 | if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV6))) | ||
270 | return -IPSET_ERR_PROTOCOL; | ||
271 | |||
272 | memcpy(ipaddr, nla_data(tb[IPSET_ATTR_IPADDR_IPV6]), | ||
273 | sizeof(struct in6_addr)); | ||
274 | return 0; | ||
275 | } | ||
276 | EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); | ||
277 | |||
278 | /* | ||
279 | * Creating/destroying/renaming/swapping affect the existence and | ||
280 | * the properties of a set. All of these can be executed from userspace | ||
281 | * only and serialized by the nfnl mutex indirectly from nfnetlink. | ||
282 | * | ||
283 | * Sets are identified by their index in ip_set_list and the index | ||
284 | * is used by the external references (set/SET netfilter modules). | ||
285 | * | ||
286 | * The set behind an index may change by swapping only, from userspace. | ||
287 | */ | ||
288 | |||
289 | static inline void | ||
290 | __ip_set_get(ip_set_id_t index) | ||
291 | { | ||
292 | atomic_inc(&ip_set_list[index]->ref); | ||
293 | } | ||
294 | |||
295 | static inline void | ||
296 | __ip_set_put(ip_set_id_t index) | ||
297 | { | ||
298 | atomic_dec(&ip_set_list[index]->ref); | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * Add, del and test set entries from kernel. | ||
303 | * | ||
304 | * The set behind the index must exist and must be referenced | ||
305 | * so it can't be destroyed (or changed) under our foot. | ||
306 | */ | ||
307 | |||
308 | int | ||
309 | ip_set_test(ip_set_id_t index, const struct sk_buff *skb, | ||
310 | u8 family, u8 dim, u8 flags) | ||
311 | { | ||
312 | struct ip_set *set = ip_set_list[index]; | ||
313 | int ret = 0; | ||
314 | |||
315 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | ||
316 | pr_debug("set %s, index %u\n", set->name, index); | ||
317 | |||
318 | if (dim < set->type->dimension || | ||
319 | !(family == set->family || set->family == AF_UNSPEC)) | ||
320 | return 0; | ||
321 | |||
322 | read_lock_bh(&set->lock); | ||
323 | ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags); | ||
324 | read_unlock_bh(&set->lock); | ||
325 | |||
326 | if (ret == -EAGAIN) { | ||
327 | /* Type requests element to be completed */ | ||
328 | pr_debug("element must be competed, ADD is triggered\n"); | ||
329 | write_lock_bh(&set->lock); | ||
330 | set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); | ||
331 | write_unlock_bh(&set->lock); | ||
332 | ret = 1; | ||
333 | } | ||
334 | |||
335 | /* Convert error codes to nomatch */ | ||
336 | return (ret < 0 ? 0 : ret); | ||
337 | } | ||
338 | EXPORT_SYMBOL_GPL(ip_set_test); | ||
339 | |||
340 | int | ||
341 | ip_set_add(ip_set_id_t index, const struct sk_buff *skb, | ||
342 | u8 family, u8 dim, u8 flags) | ||
343 | { | ||
344 | struct ip_set *set = ip_set_list[index]; | ||
345 | int ret; | ||
346 | |||
347 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | ||
348 | pr_debug("set %s, index %u\n", set->name, index); | ||
349 | |||
350 | if (dim < set->type->dimension || | ||
351 | !(family == set->family || set->family == AF_UNSPEC)) | ||
352 | return 0; | ||
353 | |||
354 | write_lock_bh(&set->lock); | ||
355 | ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); | ||
356 | write_unlock_bh(&set->lock); | ||
357 | |||
358 | return ret; | ||
359 | } | ||
360 | EXPORT_SYMBOL_GPL(ip_set_add); | ||
361 | |||
362 | int | ||
363 | ip_set_del(ip_set_id_t index, const struct sk_buff *skb, | ||
364 | u8 family, u8 dim, u8 flags) | ||
365 | { | ||
366 | struct ip_set *set = ip_set_list[index]; | ||
367 | int ret = 0; | ||
368 | |||
369 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | ||
370 | pr_debug("set %s, index %u\n", set->name, index); | ||
371 | |||
372 | if (dim < set->type->dimension || | ||
373 | !(family == set->family || set->family == AF_UNSPEC)) | ||
374 | return 0; | ||
375 | |||
376 | write_lock_bh(&set->lock); | ||
377 | ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags); | ||
378 | write_unlock_bh(&set->lock); | ||
379 | |||
380 | return ret; | ||
381 | } | ||
382 | EXPORT_SYMBOL_GPL(ip_set_del); | ||
383 | |||
384 | /* | ||
385 | * Find set by name, reference it once. The reference makes sure the | ||
386 | * thing pointed to, does not go away under our feet. | ||
387 | * | ||
388 | * The nfnl mutex must already be activated. | ||
389 | */ | ||
390 | ip_set_id_t | ||
391 | ip_set_get_byname(const char *name, struct ip_set **set) | ||
392 | { | ||
393 | ip_set_id_t i, index = IPSET_INVALID_ID; | ||
394 | struct ip_set *s; | ||
395 | |||
396 | for (i = 0; i < ip_set_max; i++) { | ||
397 | s = ip_set_list[i]; | ||
398 | if (s != NULL && STREQ(s->name, name)) { | ||
399 | __ip_set_get(i); | ||
400 | index = i; | ||
401 | *set = s; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | return index; | ||
406 | } | ||
407 | EXPORT_SYMBOL_GPL(ip_set_get_byname); | ||
408 | |||
409 | /* | ||
410 | * If the given set pointer points to a valid set, decrement | ||
411 | * reference count by 1. The caller shall not assume the index | ||
412 | * to be valid, after calling this function. | ||
413 | * | ||
414 | * The nfnl mutex must already be activated. | ||
415 | */ | ||
416 | void | ||
417 | ip_set_put_byindex(ip_set_id_t index) | ||
418 | { | ||
419 | if (ip_set_list[index] != NULL) { | ||
420 | BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); | ||
421 | __ip_set_put(index); | ||
422 | } | ||
423 | } | ||
424 | EXPORT_SYMBOL_GPL(ip_set_put_byindex); | ||
425 | |||
426 | /* | ||
427 | * Get the name of a set behind a set index. | ||
428 | * We assume the set is referenced, so it does exist and | ||
429 | * can't be destroyed. The set cannot be renamed due to | ||
430 | * the referencing either. | ||
431 | * | ||
432 | * The nfnl mutex must already be activated. | ||
433 | */ | ||
434 | const char * | ||
435 | ip_set_name_byindex(ip_set_id_t index) | ||
436 | { | ||
437 | const struct ip_set *set = ip_set_list[index]; | ||
438 | |||
439 | BUG_ON(set == NULL); | ||
440 | BUG_ON(atomic_read(&set->ref) == 0); | ||
441 | |||
442 | /* Referenced, so it's safe */ | ||
443 | return set->name; | ||
444 | } | ||
445 | EXPORT_SYMBOL_GPL(ip_set_name_byindex); | ||
446 | |||
447 | /* | ||
448 | * Routines to call by external subsystems, which do not | ||
449 | * call nfnl_lock for us. | ||
450 | */ | ||
451 | |||
452 | /* | ||
453 | * Find set by name, reference it once. The reference makes sure the | ||
454 | * thing pointed to, does not go away under our feet. | ||
455 | * | ||
456 | * The nfnl mutex is used in the function. | ||
457 | */ | ||
458 | ip_set_id_t | ||
459 | ip_set_nfnl_get(const char *name) | ||
460 | { | ||
461 | struct ip_set *s; | ||
462 | ip_set_id_t index; | ||
463 | |||
464 | nfnl_lock(); | ||
465 | index = ip_set_get_byname(name, &s); | ||
466 | nfnl_unlock(); | ||
467 | |||
468 | return index; | ||
469 | } | ||
470 | EXPORT_SYMBOL_GPL(ip_set_nfnl_get); | ||
471 | |||
472 | /* | ||
473 | * Find set by index, reference it once. The reference makes sure the | ||
474 | * thing pointed to, does not go away under our feet. | ||
475 | * | ||
476 | * The nfnl mutex is used in the function. | ||
477 | */ | ||
478 | ip_set_id_t | ||
479 | ip_set_nfnl_get_byindex(ip_set_id_t index) | ||
480 | { | ||
481 | if (index > ip_set_max) | ||
482 | return IPSET_INVALID_ID; | ||
483 | |||
484 | nfnl_lock(); | ||
485 | if (ip_set_list[index]) | ||
486 | __ip_set_get(index); | ||
487 | else | ||
488 | index = IPSET_INVALID_ID; | ||
489 | nfnl_unlock(); | ||
490 | |||
491 | return index; | ||
492 | } | ||
493 | EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex); | ||
494 | |||
495 | /* | ||
496 | * If the given set pointer points to a valid set, decrement | ||
497 | * reference count by 1. The caller shall not assume the index | ||
498 | * to be valid, after calling this function. | ||
499 | * | ||
500 | * The nfnl mutex is used in the function. | ||
501 | */ | ||
502 | void | ||
503 | ip_set_nfnl_put(ip_set_id_t index) | ||
504 | { | ||
505 | nfnl_lock(); | ||
506 | if (ip_set_list[index] != NULL) { | ||
507 | BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); | ||
508 | __ip_set_put(index); | ||
509 | } | ||
510 | nfnl_unlock(); | ||
511 | } | ||
512 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); | ||
513 | |||
514 | /* | ||
515 | * Communication protocol with userspace over netlink. | ||
516 | * | ||
517 | * We already locked by nfnl_lock. | ||
518 | */ | ||
519 | |||
520 | static inline bool | ||
521 | protocol_failed(const struct nlattr * const tb[]) | ||
522 | { | ||
523 | return !tb[IPSET_ATTR_PROTOCOL] || | ||
524 | nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL; | ||
525 | } | ||
526 | |||
527 | static inline u32 | ||
528 | flag_exist(const struct nlmsghdr *nlh) | ||
529 | { | ||
530 | return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST; | ||
531 | } | ||
532 | |||
533 | static struct nlmsghdr * | ||
534 | start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, | ||
535 | enum ipset_cmd cmd) | ||
536 | { | ||
537 | struct nlmsghdr *nlh; | ||
538 | struct nfgenmsg *nfmsg; | ||
539 | |||
540 | nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), | ||
541 | sizeof(*nfmsg), flags); | ||
542 | if (nlh == NULL) | ||
543 | return NULL; | ||
544 | |||
545 | nfmsg = nlmsg_data(nlh); | ||
546 | nfmsg->nfgen_family = AF_INET; | ||
547 | nfmsg->version = NFNETLINK_V0; | ||
548 | nfmsg->res_id = 0; | ||
549 | |||
550 | return nlh; | ||
551 | } | ||
552 | |||
553 | /* Create a set */ | ||
554 | |||
555 | static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
556 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
557 | [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, | ||
558 | .len = IPSET_MAXNAMELEN - 1 }, | ||
559 | [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, | ||
560 | .len = IPSET_MAXNAMELEN - 1}, | ||
561 | [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, | ||
562 | [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, | ||
563 | [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, | ||
564 | }; | ||
565 | |||
566 | static ip_set_id_t | ||
567 | find_set_id(const char *name) | ||
568 | { | ||
569 | ip_set_id_t i, index = IPSET_INVALID_ID; | ||
570 | const struct ip_set *set; | ||
571 | |||
572 | for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { | ||
573 | set = ip_set_list[i]; | ||
574 | if (set != NULL && STREQ(set->name, name)) | ||
575 | index = i; | ||
576 | } | ||
577 | return index; | ||
578 | } | ||
579 | |||
580 | static inline struct ip_set * | ||
581 | find_set(const char *name) | ||
582 | { | ||
583 | ip_set_id_t index = find_set_id(name); | ||
584 | |||
585 | return index == IPSET_INVALID_ID ? NULL : ip_set_list[index]; | ||
586 | } | ||
587 | |||
588 | static int | ||
589 | find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) | ||
590 | { | ||
591 | ip_set_id_t i; | ||
592 | |||
593 | *index = IPSET_INVALID_ID; | ||
594 | for (i = 0; i < ip_set_max; i++) { | ||
595 | if (ip_set_list[i] == NULL) { | ||
596 | if (*index == IPSET_INVALID_ID) | ||
597 | *index = i; | ||
598 | } else if (STREQ(name, ip_set_list[i]->name)) { | ||
599 | /* Name clash */ | ||
600 | *set = ip_set_list[i]; | ||
601 | return -EEXIST; | ||
602 | } | ||
603 | } | ||
604 | if (*index == IPSET_INVALID_ID) | ||
605 | /* No free slot remained */ | ||
606 | return -IPSET_ERR_MAX_SETS; | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static int | ||
611 | ip_set_create(struct sock *ctnl, struct sk_buff *skb, | ||
612 | const struct nlmsghdr *nlh, | ||
613 | const struct nlattr * const attr[]) | ||
614 | { | ||
615 | struct ip_set *set, *clash; | ||
616 | ip_set_id_t index = IPSET_INVALID_ID; | ||
617 | struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {}; | ||
618 | const char *name, *typename; | ||
619 | u8 family, revision; | ||
620 | u32 flags = flag_exist(nlh); | ||
621 | int ret = 0; | ||
622 | |||
623 | if (unlikely(protocol_failed(attr) || | ||
624 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
625 | attr[IPSET_ATTR_TYPENAME] == NULL || | ||
626 | attr[IPSET_ATTR_REVISION] == NULL || | ||
627 | attr[IPSET_ATTR_FAMILY] == NULL || | ||
628 | (attr[IPSET_ATTR_DATA] != NULL && | ||
629 | !flag_nested(attr[IPSET_ATTR_DATA])))) | ||
630 | return -IPSET_ERR_PROTOCOL; | ||
631 | |||
632 | name = nla_data(attr[IPSET_ATTR_SETNAME]); | ||
633 | typename = nla_data(attr[IPSET_ATTR_TYPENAME]); | ||
634 | family = nla_get_u8(attr[IPSET_ATTR_FAMILY]); | ||
635 | revision = nla_get_u8(attr[IPSET_ATTR_REVISION]); | ||
636 | pr_debug("setname: %s, typename: %s, family: %s, revision: %u\n", | ||
637 | name, typename, family_name(family), revision); | ||
638 | |||
639 | /* | ||
640 | * First, and without any locks, allocate and initialize | ||
641 | * a normal base set structure. | ||
642 | */ | ||
643 | set = kzalloc(sizeof(struct ip_set), GFP_KERNEL); | ||
644 | if (!set) | ||
645 | return -ENOMEM; | ||
646 | rwlock_init(&set->lock); | ||
647 | strlcpy(set->name, name, IPSET_MAXNAMELEN); | ||
648 | atomic_set(&set->ref, 0); | ||
649 | set->family = family; | ||
650 | |||
651 | /* | ||
652 | * Next, check that we know the type, and take | ||
653 | * a reference on the type, to make sure it stays available | ||
654 | * while constructing our new set. | ||
655 | * | ||
656 | * After referencing the type, we try to create the type | ||
657 | * specific part of the set without holding any locks. | ||
658 | */ | ||
659 | ret = find_set_type_get(typename, family, revision, &(set->type)); | ||
660 | if (ret) | ||
661 | goto out; | ||
662 | |||
663 | /* | ||
664 | * Without holding any locks, create private part. | ||
665 | */ | ||
666 | if (attr[IPSET_ATTR_DATA] && | ||
667 | nla_parse_nested(tb, IPSET_ATTR_CREATE_MAX, attr[IPSET_ATTR_DATA], | ||
668 | set->type->create_policy)) { | ||
669 | ret = -IPSET_ERR_PROTOCOL; | ||
670 | goto put_out; | ||
671 | } | ||
672 | |||
673 | ret = set->type->create(set, tb, flags); | ||
674 | if (ret != 0) | ||
675 | goto put_out; | ||
676 | |||
677 | /* BTW, ret==0 here. */ | ||
678 | |||
679 | /* | ||
680 | * Here, we have a valid, constructed set and we are protected | ||
681 | * by nfnl_lock. Find the first free index in ip_set_list and | ||
682 | * check clashing. | ||
683 | */ | ||
684 | if ((ret = find_free_id(set->name, &index, &clash)) != 0) { | ||
685 | /* If this is the same set and requested, ignore error */ | ||
686 | if (ret == -EEXIST && | ||
687 | (flags & IPSET_FLAG_EXIST) && | ||
688 | STREQ(set->type->name, clash->type->name) && | ||
689 | set->type->family == clash->type->family && | ||
690 | set->type->revision == clash->type->revision && | ||
691 | set->variant->same_set(set, clash)) | ||
692 | ret = 0; | ||
693 | goto cleanup; | ||
694 | } | ||
695 | |||
696 | /* | ||
697 | * Finally! Add our shiny new set to the list, and be done. | ||
698 | */ | ||
699 | pr_debug("create: '%s' created with index %u!\n", set->name, index); | ||
700 | ip_set_list[index] = set; | ||
701 | |||
702 | return ret; | ||
703 | |||
704 | cleanup: | ||
705 | set->variant->destroy(set); | ||
706 | put_out: | ||
707 | module_put(set->type->me); | ||
708 | out: | ||
709 | kfree(set); | ||
710 | return ret; | ||
711 | } | ||
712 | |||
713 | /* Destroy sets */ | ||
714 | |||
715 | static const struct nla_policy | ||
716 | ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
717 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
718 | [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, | ||
719 | .len = IPSET_MAXNAMELEN - 1 }, | ||
720 | }; | ||
721 | |||
722 | static void | ||
723 | ip_set_destroy_set(ip_set_id_t index) | ||
724 | { | ||
725 | struct ip_set *set = ip_set_list[index]; | ||
726 | |||
727 | pr_debug("set: %s\n", set->name); | ||
728 | ip_set_list[index] = NULL; | ||
729 | |||
730 | /* Must call it without holding any lock */ | ||
731 | set->variant->destroy(set); | ||
732 | module_put(set->type->me); | ||
733 | kfree(set); | ||
734 | } | ||
735 | |||
736 | static int | ||
737 | ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, | ||
738 | const struct nlmsghdr *nlh, | ||
739 | const struct nlattr * const attr[]) | ||
740 | { | ||
741 | ip_set_id_t i; | ||
742 | |||
743 | if (unlikely(protocol_failed(attr))) | ||
744 | return -IPSET_ERR_PROTOCOL; | ||
745 | |||
746 | /* References are protected by the nfnl mutex */ | ||
747 | if (!attr[IPSET_ATTR_SETNAME]) { | ||
748 | for (i = 0; i < ip_set_max; i++) { | ||
749 | if (ip_set_list[i] != NULL && | ||
750 | (atomic_read(&ip_set_list[i]->ref))) | ||
751 | return -IPSET_ERR_BUSY; | ||
752 | } | ||
753 | for (i = 0; i < ip_set_max; i++) { | ||
754 | if (ip_set_list[i] != NULL) | ||
755 | ip_set_destroy_set(i); | ||
756 | } | ||
757 | } else { | ||
758 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
759 | if (i == IPSET_INVALID_ID) | ||
760 | return -ENOENT; | ||
761 | else if (atomic_read(&ip_set_list[i]->ref)) | ||
762 | return -IPSET_ERR_BUSY; | ||
763 | |||
764 | ip_set_destroy_set(i); | ||
765 | } | ||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | /* Flush sets */ | ||
770 | |||
771 | static void | ||
772 | ip_set_flush_set(struct ip_set *set) | ||
773 | { | ||
774 | pr_debug("set: %s\n", set->name); | ||
775 | |||
776 | write_lock_bh(&set->lock); | ||
777 | set->variant->flush(set); | ||
778 | write_unlock_bh(&set->lock); | ||
779 | } | ||
780 | |||
781 | static int | ||
782 | ip_set_flush(struct sock *ctnl, struct sk_buff *skb, | ||
783 | const struct nlmsghdr *nlh, | ||
784 | const struct nlattr * const attr[]) | ||
785 | { | ||
786 | ip_set_id_t i; | ||
787 | |||
788 | if (unlikely(protocol_failed(attr))) | ||
789 | return -EPROTO; | ||
790 | |||
791 | if (!attr[IPSET_ATTR_SETNAME]) { | ||
792 | for (i = 0; i < ip_set_max; i++) | ||
793 | if (ip_set_list[i] != NULL) | ||
794 | ip_set_flush_set(ip_set_list[i]); | ||
795 | } else { | ||
796 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
797 | if (i == IPSET_INVALID_ID) | ||
798 | return -ENOENT; | ||
799 | |||
800 | ip_set_flush_set(ip_set_list[i]); | ||
801 | } | ||
802 | |||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | /* Rename a set */ | ||
807 | |||
808 | static const struct nla_policy | ||
809 | ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
810 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
811 | [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, | ||
812 | .len = IPSET_MAXNAMELEN - 1 }, | ||
813 | [IPSET_ATTR_SETNAME2] = { .type = NLA_NUL_STRING, | ||
814 | .len = IPSET_MAXNAMELEN - 1 }, | ||
815 | }; | ||
816 | |||
817 | static int | ||
818 | ip_set_rename(struct sock *ctnl, struct sk_buff *skb, | ||
819 | const struct nlmsghdr *nlh, | ||
820 | const struct nlattr * const attr[]) | ||
821 | { | ||
822 | struct ip_set *set; | ||
823 | const char *name2; | ||
824 | ip_set_id_t i; | ||
825 | |||
826 | if (unlikely(protocol_failed(attr) || | ||
827 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
828 | attr[IPSET_ATTR_SETNAME2] == NULL)) | ||
829 | return -IPSET_ERR_PROTOCOL; | ||
830 | |||
831 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
832 | if (set == NULL) | ||
833 | return -ENOENT; | ||
834 | if (atomic_read(&set->ref) != 0) | ||
835 | return -IPSET_ERR_REFERENCED; | ||
836 | |||
837 | name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); | ||
838 | for (i = 0; i < ip_set_max; i++) { | ||
839 | if (ip_set_list[i] != NULL && | ||
840 | STREQ(ip_set_list[i]->name, name2)) | ||
841 | return -IPSET_ERR_EXIST_SETNAME2; | ||
842 | } | ||
843 | strncpy(set->name, name2, IPSET_MAXNAMELEN); | ||
844 | |||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | /* Swap two sets so that name/index points to the other. | ||
849 | * References and set names are also swapped. | ||
850 | * | ||
851 | * We are protected by the nfnl mutex and references are | ||
852 | * manipulated only by holding the mutex. The kernel interfaces | ||
853 | * do not hold the mutex but the pointer settings are atomic | ||
854 | * so the ip_set_list always contains valid pointers to the sets. | ||
855 | */ | ||
856 | |||
857 | static int | ||
858 | ip_set_swap(struct sock *ctnl, struct sk_buff *skb, | ||
859 | const struct nlmsghdr *nlh, | ||
860 | const struct nlattr * const attr[]) | ||
861 | { | ||
862 | struct ip_set *from, *to; | ||
863 | ip_set_id_t from_id, to_id; | ||
864 | char from_name[IPSET_MAXNAMELEN]; | ||
865 | u32 from_ref; | ||
866 | |||
867 | if (unlikely(protocol_failed(attr) || | ||
868 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
869 | attr[IPSET_ATTR_SETNAME2] == NULL)) | ||
870 | return -IPSET_ERR_PROTOCOL; | ||
871 | |||
872 | from_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
873 | if (from_id == IPSET_INVALID_ID) | ||
874 | return -ENOENT; | ||
875 | |||
876 | to_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME2])); | ||
877 | if (to_id == IPSET_INVALID_ID) | ||
878 | return -IPSET_ERR_EXIST_SETNAME2; | ||
879 | |||
880 | from = ip_set_list[from_id]; | ||
881 | to = ip_set_list[to_id]; | ||
882 | |||
883 | /* Features must not change. | ||
884 | * Not an artifical restriction anymore, as we must prevent | ||
885 | * possible loops created by swapping in setlist type of sets. */ | ||
886 | if (!(from->type->features == to->type->features && | ||
887 | from->type->family == to->type->family)) | ||
888 | return -IPSET_ERR_TYPE_MISMATCH; | ||
889 | |||
890 | /* No magic here: ref munging protected by the nfnl_lock */ | ||
891 | strncpy(from_name, from->name, IPSET_MAXNAMELEN); | ||
892 | from_ref = atomic_read(&from->ref); | ||
893 | |||
894 | strncpy(from->name, to->name, IPSET_MAXNAMELEN); | ||
895 | atomic_set(&from->ref, atomic_read(&to->ref)); | ||
896 | strncpy(to->name, from_name, IPSET_MAXNAMELEN); | ||
897 | atomic_set(&to->ref, from_ref); | ||
898 | |||
899 | ip_set_list[from_id] = to; | ||
900 | ip_set_list[to_id] = from; | ||
901 | |||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | /* List/save set data */ | ||
906 | |||
907 | #define DUMP_INIT 0L | ||
908 | #define DUMP_ALL 1L | ||
909 | #define DUMP_ONE 2L | ||
910 | #define DUMP_LAST 3L | ||
911 | |||
912 | static int | ||
913 | ip_set_dump_done(struct netlink_callback *cb) | ||
914 | { | ||
915 | if (cb->args[2]) { | ||
916 | pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name); | ||
917 | __ip_set_put((ip_set_id_t) cb->args[1]); | ||
918 | } | ||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | static inline void | ||
923 | dump_attrs(struct nlmsghdr *nlh) | ||
924 | { | ||
925 | const struct nlattr *attr; | ||
926 | int rem; | ||
927 | |||
928 | pr_debug("dump nlmsg\n"); | ||
929 | nlmsg_for_each_attr(attr, nlh, sizeof(struct nfgenmsg), rem) { | ||
930 | pr_debug("type: %u, len %u\n", nla_type(attr), attr->nla_len); | ||
931 | } | ||
932 | } | ||
933 | |||
934 | static int | ||
935 | dump_init(struct netlink_callback *cb) | ||
936 | { | ||
937 | struct nlmsghdr *nlh = nlmsg_hdr(cb->skb); | ||
938 | int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); | ||
939 | struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; | ||
940 | struct nlattr *attr = (void *)nlh + min_len; | ||
941 | ip_set_id_t index; | ||
942 | |||
943 | /* Second pass, so parser can't fail */ | ||
944 | nla_parse(cda, IPSET_ATTR_CMD_MAX, | ||
945 | attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); | ||
946 | |||
947 | /* cb->args[0] : dump single set/all sets | ||
948 | * [1] : set index | ||
949 | * [..]: type specific | ||
950 | */ | ||
951 | |||
952 | if (!cda[IPSET_ATTR_SETNAME]) { | ||
953 | cb->args[0] = DUMP_ALL; | ||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); | ||
958 | if (index == IPSET_INVALID_ID) | ||
959 | return -ENOENT; | ||
960 | |||
961 | cb->args[0] = DUMP_ONE; | ||
962 | cb->args[1] = index; | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | static int | ||
967 | ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) | ||
968 | { | ||
969 | ip_set_id_t index = IPSET_INVALID_ID, max; | ||
970 | struct ip_set *set = NULL; | ||
971 | struct nlmsghdr *nlh = NULL; | ||
972 | unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; | ||
973 | int ret = 0; | ||
974 | |||
975 | if (cb->args[0] == DUMP_INIT) { | ||
976 | ret = dump_init(cb); | ||
977 | if (ret < 0) { | ||
978 | nlh = nlmsg_hdr(cb->skb); | ||
979 | /* We have to create and send the error message | ||
980 | * manually :-( */ | ||
981 | if (nlh->nlmsg_flags & NLM_F_ACK) | ||
982 | netlink_ack(cb->skb, nlh, ret); | ||
983 | return ret; | ||
984 | } | ||
985 | } | ||
986 | |||
987 | if (cb->args[1] >= ip_set_max) | ||
988 | goto out; | ||
989 | |||
990 | pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); | ||
991 | max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; | ||
992 | for (; cb->args[1] < max; cb->args[1]++) { | ||
993 | index = (ip_set_id_t) cb->args[1]; | ||
994 | set = ip_set_list[index]; | ||
995 | if (set == NULL) { | ||
996 | if (cb->args[0] == DUMP_ONE) { | ||
997 | ret = -ENOENT; | ||
998 | goto out; | ||
999 | } | ||
1000 | continue; | ||
1001 | } | ||
1002 | /* When dumping all sets, we must dump "sorted" | ||
1003 | * so that lists (unions of sets) are dumped last. | ||
1004 | */ | ||
1005 | if (cb->args[0] != DUMP_ONE && | ||
1006 | !((cb->args[0] == DUMP_ALL) ^ | ||
1007 | (set->type->features & IPSET_DUMP_LAST))) | ||
1008 | continue; | ||
1009 | pr_debug("List set: %s\n", set->name); | ||
1010 | if (!cb->args[2]) { | ||
1011 | /* Start listing: make sure set won't be destroyed */ | ||
1012 | pr_debug("reference set\n"); | ||
1013 | __ip_set_get(index); | ||
1014 | } | ||
1015 | nlh = start_msg(skb, NETLINK_CB(cb->skb).pid, | ||
1016 | cb->nlh->nlmsg_seq, flags, | ||
1017 | IPSET_CMD_LIST); | ||
1018 | if (!nlh) { | ||
1019 | ret = -EMSGSIZE; | ||
1020 | goto release_refcount; | ||
1021 | } | ||
1022 | NLA_PUT_U8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); | ||
1023 | NLA_PUT_STRING(skb, IPSET_ATTR_SETNAME, set->name); | ||
1024 | switch (cb->args[2]) { | ||
1025 | case 0: | ||
1026 | /* Core header data */ | ||
1027 | NLA_PUT_STRING(skb, IPSET_ATTR_TYPENAME, | ||
1028 | set->type->name); | ||
1029 | NLA_PUT_U8(skb, IPSET_ATTR_FAMILY, | ||
1030 | set->family); | ||
1031 | NLA_PUT_U8(skb, IPSET_ATTR_REVISION, | ||
1032 | set->type->revision); | ||
1033 | ret = set->variant->head(set, skb); | ||
1034 | if (ret < 0) | ||
1035 | goto release_refcount; | ||
1036 | /* Fall through and add elements */ | ||
1037 | default: | ||
1038 | read_lock_bh(&set->lock); | ||
1039 | ret = set->variant->list(set, skb, cb); | ||
1040 | read_unlock_bh(&set->lock); | ||
1041 | if (!cb->args[2]) { | ||
1042 | /* Set is done, proceed with next one */ | ||
1043 | if (cb->args[0] == DUMP_ONE) | ||
1044 | cb->args[1] = IPSET_INVALID_ID; | ||
1045 | else | ||
1046 | cb->args[1]++; | ||
1047 | } | ||
1048 | goto release_refcount; | ||
1049 | } | ||
1050 | } | ||
1051 | goto out; | ||
1052 | |||
1053 | nla_put_failure: | ||
1054 | ret = -EFAULT; | ||
1055 | release_refcount: | ||
1056 | /* If there was an error or set is done, release set */ | ||
1057 | if (ret || !cb->args[2]) { | ||
1058 | pr_debug("release set %s\n", ip_set_list[index]->name); | ||
1059 | __ip_set_put(index); | ||
1060 | } | ||
1061 | |||
1062 | /* If we dump all sets, continue with dumping last ones */ | ||
1063 | if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2]) | ||
1064 | cb->args[0] = DUMP_LAST; | ||
1065 | |||
1066 | out: | ||
1067 | if (nlh) { | ||
1068 | nlmsg_end(skb, nlh); | ||
1069 | pr_debug("nlmsg_len: %u\n", nlh->nlmsg_len); | ||
1070 | dump_attrs(nlh); | ||
1071 | } | ||
1072 | |||
1073 | return ret < 0 ? ret : skb->len; | ||
1074 | } | ||
1075 | |||
1076 | static int | ||
1077 | ip_set_dump(struct sock *ctnl, struct sk_buff *skb, | ||
1078 | const struct nlmsghdr *nlh, | ||
1079 | const struct nlattr * const attr[]) | ||
1080 | { | ||
1081 | if (unlikely(protocol_failed(attr))) | ||
1082 | return -IPSET_ERR_PROTOCOL; | ||
1083 | |||
1084 | return netlink_dump_start(ctnl, skb, nlh, | ||
1085 | ip_set_dump_start, | ||
1086 | ip_set_dump_done); | ||
1087 | } | ||
1088 | |||
1089 | /* Add, del and test */ | ||
1090 | |||
1091 | static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
1092 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
1093 | [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, | ||
1094 | .len = IPSET_MAXNAMELEN - 1 }, | ||
1095 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
1096 | [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, | ||
1097 | [IPSET_ATTR_ADT] = { .type = NLA_NESTED }, | ||
1098 | }; | ||
1099 | |||
1100 | static int | ||
1101 | call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, | ||
1102 | struct nlattr *tb[], enum ipset_adt adt, | ||
1103 | u32 flags, bool use_lineno) | ||
1104 | { | ||
1105 | int ret, retried = 0; | ||
1106 | u32 lineno = 0; | ||
1107 | bool eexist = flags & IPSET_FLAG_EXIST; | ||
1108 | |||
1109 | do { | ||
1110 | write_lock_bh(&set->lock); | ||
1111 | ret = set->variant->uadt(set, tb, adt, &lineno, flags); | ||
1112 | write_unlock_bh(&set->lock); | ||
1113 | } while (ret == -EAGAIN && | ||
1114 | set->variant->resize && | ||
1115 | (ret = set->variant->resize(set, retried++)) == 0); | ||
1116 | |||
1117 | if (!ret || (ret == -IPSET_ERR_EXIST && eexist)) | ||
1118 | return 0; | ||
1119 | if (lineno && use_lineno) { | ||
1120 | /* Error in restore/batch mode: send back lineno */ | ||
1121 | struct nlmsghdr *rep, *nlh = nlmsg_hdr(skb); | ||
1122 | struct sk_buff *skb2; | ||
1123 | struct nlmsgerr *errmsg; | ||
1124 | size_t payload = sizeof(*errmsg) + nlmsg_len(nlh); | ||
1125 | int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); | ||
1126 | struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; | ||
1127 | struct nlattr *cmdattr; | ||
1128 | u32 *errline; | ||
1129 | |||
1130 | skb2 = nlmsg_new(payload, GFP_KERNEL); | ||
1131 | if (skb2 == NULL) | ||
1132 | return -ENOMEM; | ||
1133 | rep = __nlmsg_put(skb2, NETLINK_CB(skb).pid, | ||
1134 | nlh->nlmsg_seq, NLMSG_ERROR, payload, 0); | ||
1135 | errmsg = nlmsg_data(rep); | ||
1136 | errmsg->error = ret; | ||
1137 | memcpy(&errmsg->msg, nlh, nlh->nlmsg_len); | ||
1138 | cmdattr = (void *)&errmsg->msg + min_len; | ||
1139 | |||
1140 | nla_parse(cda, IPSET_ATTR_CMD_MAX, | ||
1141 | cmdattr, nlh->nlmsg_len - min_len, | ||
1142 | ip_set_adt_policy); | ||
1143 | |||
1144 | errline = nla_data(cda[IPSET_ATTR_LINENO]); | ||
1145 | |||
1146 | *errline = lineno; | ||
1147 | |||
1148 | netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | ||
1149 | /* Signal netlink not to send its ACK/errmsg. */ | ||
1150 | return -EINTR; | ||
1151 | } | ||
1152 | |||
1153 | return ret; | ||
1154 | } | ||
1155 | |||
1156 | static int | ||
1157 | ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, | ||
1158 | const struct nlmsghdr *nlh, | ||
1159 | const struct nlattr * const attr[]) | ||
1160 | { | ||
1161 | struct ip_set *set; | ||
1162 | struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; | ||
1163 | const struct nlattr *nla; | ||
1164 | u32 flags = flag_exist(nlh); | ||
1165 | bool use_lineno; | ||
1166 | int ret = 0; | ||
1167 | |||
1168 | if (unlikely(protocol_failed(attr) || | ||
1169 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
1170 | !((attr[IPSET_ATTR_DATA] != NULL) ^ | ||
1171 | (attr[IPSET_ATTR_ADT] != NULL)) || | ||
1172 | (attr[IPSET_ATTR_DATA] != NULL && | ||
1173 | !flag_nested(attr[IPSET_ATTR_DATA])) || | ||
1174 | (attr[IPSET_ATTR_ADT] != NULL && | ||
1175 | (!flag_nested(attr[IPSET_ATTR_ADT]) || | ||
1176 | attr[IPSET_ATTR_LINENO] == NULL)))) | ||
1177 | return -IPSET_ERR_PROTOCOL; | ||
1178 | |||
1179 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
1180 | if (set == NULL) | ||
1181 | return -ENOENT; | ||
1182 | |||
1183 | use_lineno = !!attr[IPSET_ATTR_LINENO]; | ||
1184 | if (attr[IPSET_ATTR_DATA]) { | ||
1185 | if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, | ||
1186 | attr[IPSET_ATTR_DATA], | ||
1187 | set->type->adt_policy)) | ||
1188 | return -IPSET_ERR_PROTOCOL; | ||
1189 | ret = call_ad(ctnl, skb, set, tb, IPSET_ADD, flags, | ||
1190 | use_lineno); | ||
1191 | } else { | ||
1192 | int nla_rem; | ||
1193 | |||
1194 | nla_for_each_nested(nla, attr[IPSET_ATTR_ADT], nla_rem) { | ||
1195 | memset(tb, 0, sizeof(tb)); | ||
1196 | if (nla_type(nla) != IPSET_ATTR_DATA || | ||
1197 | !flag_nested(nla) || | ||
1198 | nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, | ||
1199 | set->type->adt_policy)) | ||
1200 | return -IPSET_ERR_PROTOCOL; | ||
1201 | ret = call_ad(ctnl, skb, set, tb, IPSET_ADD, | ||
1202 | flags, use_lineno); | ||
1203 | if (ret < 0) | ||
1204 | return ret; | ||
1205 | } | ||
1206 | } | ||
1207 | return ret; | ||
1208 | } | ||
1209 | |||
1210 | static int | ||
1211 | ip_set_udel(struct sock *ctnl, struct sk_buff *skb, | ||
1212 | const struct nlmsghdr *nlh, | ||
1213 | const struct nlattr * const attr[]) | ||
1214 | { | ||
1215 | struct ip_set *set; | ||
1216 | struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; | ||
1217 | const struct nlattr *nla; | ||
1218 | u32 flags = flag_exist(nlh); | ||
1219 | bool use_lineno; | ||
1220 | int ret = 0; | ||
1221 | |||
1222 | if (unlikely(protocol_failed(attr) || | ||
1223 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
1224 | !((attr[IPSET_ATTR_DATA] != NULL) ^ | ||
1225 | (attr[IPSET_ATTR_ADT] != NULL)) || | ||
1226 | (attr[IPSET_ATTR_DATA] != NULL && | ||
1227 | !flag_nested(attr[IPSET_ATTR_DATA])) || | ||
1228 | (attr[IPSET_ATTR_ADT] != NULL && | ||
1229 | (!flag_nested(attr[IPSET_ATTR_ADT]) || | ||
1230 | attr[IPSET_ATTR_LINENO] == NULL)))) | ||
1231 | return -IPSET_ERR_PROTOCOL; | ||
1232 | |||
1233 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
1234 | if (set == NULL) | ||
1235 | return -ENOENT; | ||
1236 | |||
1237 | use_lineno = !!attr[IPSET_ATTR_LINENO]; | ||
1238 | if (attr[IPSET_ATTR_DATA]) { | ||
1239 | if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, | ||
1240 | attr[IPSET_ATTR_DATA], | ||
1241 | set->type->adt_policy)) | ||
1242 | return -IPSET_ERR_PROTOCOL; | ||
1243 | ret = call_ad(ctnl, skb, set, tb, IPSET_DEL, flags, | ||
1244 | use_lineno); | ||
1245 | } else { | ||
1246 | int nla_rem; | ||
1247 | |||
1248 | nla_for_each_nested(nla, attr[IPSET_ATTR_ADT], nla_rem) { | ||
1249 | memset(tb, 0, sizeof(*tb)); | ||
1250 | if (nla_type(nla) != IPSET_ATTR_DATA || | ||
1251 | !flag_nested(nla) || | ||
1252 | nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, | ||
1253 | set->type->adt_policy)) | ||
1254 | return -IPSET_ERR_PROTOCOL; | ||
1255 | ret = call_ad(ctnl, skb, set, tb, IPSET_DEL, | ||
1256 | flags, use_lineno); | ||
1257 | if (ret < 0) | ||
1258 | return ret; | ||
1259 | } | ||
1260 | } | ||
1261 | return ret; | ||
1262 | } | ||
1263 | |||
1264 | static int | ||
1265 | ip_set_utest(struct sock *ctnl, struct sk_buff *skb, | ||
1266 | const struct nlmsghdr *nlh, | ||
1267 | const struct nlattr * const attr[]) | ||
1268 | { | ||
1269 | struct ip_set *set; | ||
1270 | struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; | ||
1271 | int ret = 0; | ||
1272 | |||
1273 | if (unlikely(protocol_failed(attr) || | ||
1274 | attr[IPSET_ATTR_SETNAME] == NULL || | ||
1275 | attr[IPSET_ATTR_DATA] == NULL || | ||
1276 | !flag_nested(attr[IPSET_ATTR_DATA]))) | ||
1277 | return -IPSET_ERR_PROTOCOL; | ||
1278 | |||
1279 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
1280 | if (set == NULL) | ||
1281 | return -ENOENT; | ||
1282 | |||
1283 | if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, attr[IPSET_ATTR_DATA], | ||
1284 | set->type->adt_policy)) | ||
1285 | return -IPSET_ERR_PROTOCOL; | ||
1286 | |||
1287 | read_lock_bh(&set->lock); | ||
1288 | ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0); | ||
1289 | read_unlock_bh(&set->lock); | ||
1290 | /* Userspace can't trigger element to be re-added */ | ||
1291 | if (ret == -EAGAIN) | ||
1292 | ret = 1; | ||
1293 | |||
1294 | return ret < 0 ? ret : ret > 0 ? 0 : -IPSET_ERR_EXIST; | ||
1295 | } | ||
1296 | |||
1297 | /* Get headed data of a set */ | ||
1298 | |||
1299 | static int | ||
1300 | ip_set_header(struct sock *ctnl, struct sk_buff *skb, | ||
1301 | const struct nlmsghdr *nlh, | ||
1302 | const struct nlattr * const attr[]) | ||
1303 | { | ||
1304 | const struct ip_set *set; | ||
1305 | struct sk_buff *skb2; | ||
1306 | struct nlmsghdr *nlh2; | ||
1307 | ip_set_id_t index; | ||
1308 | int ret = 0; | ||
1309 | |||
1310 | if (unlikely(protocol_failed(attr) || | ||
1311 | attr[IPSET_ATTR_SETNAME] == NULL)) | ||
1312 | return -IPSET_ERR_PROTOCOL; | ||
1313 | |||
1314 | index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | ||
1315 | if (index == IPSET_INVALID_ID) | ||
1316 | return -ENOENT; | ||
1317 | set = ip_set_list[index]; | ||
1318 | |||
1319 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
1320 | if (skb2 == NULL) | ||
1321 | return -ENOMEM; | ||
1322 | |||
1323 | nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, | ||
1324 | IPSET_CMD_HEADER); | ||
1325 | if (!nlh2) | ||
1326 | goto nlmsg_failure; | ||
1327 | NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); | ||
1328 | NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name); | ||
1329 | NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name); | ||
1330 | NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family); | ||
1331 | NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision); | ||
1332 | nlmsg_end(skb2, nlh2); | ||
1333 | |||
1334 | ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | ||
1335 | if (ret < 0) | ||
1336 | return ret; | ||
1337 | |||
1338 | return 0; | ||
1339 | |||
1340 | nla_put_failure: | ||
1341 | nlmsg_cancel(skb2, nlh2); | ||
1342 | nlmsg_failure: | ||
1343 | kfree_skb(skb2); | ||
1344 | return -EMSGSIZE; | ||
1345 | } | ||
1346 | |||
1347 | /* Get type data */ | ||
1348 | |||
1349 | static const struct nla_policy ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
1350 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
1351 | [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, | ||
1352 | .len = IPSET_MAXNAMELEN - 1 }, | ||
1353 | [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, | ||
1354 | }; | ||
1355 | |||
1356 | static int | ||
1357 | ip_set_type(struct sock *ctnl, struct sk_buff *skb, | ||
1358 | const struct nlmsghdr *nlh, | ||
1359 | const struct nlattr * const attr[]) | ||
1360 | { | ||
1361 | struct sk_buff *skb2; | ||
1362 | struct nlmsghdr *nlh2; | ||
1363 | u8 family, min, max; | ||
1364 | const char *typename; | ||
1365 | int ret = 0; | ||
1366 | |||
1367 | if (unlikely(protocol_failed(attr) || | ||
1368 | attr[IPSET_ATTR_TYPENAME] == NULL || | ||
1369 | attr[IPSET_ATTR_FAMILY] == NULL)) | ||
1370 | return -IPSET_ERR_PROTOCOL; | ||
1371 | |||
1372 | family = nla_get_u8(attr[IPSET_ATTR_FAMILY]); | ||
1373 | typename = nla_data(attr[IPSET_ATTR_TYPENAME]); | ||
1374 | ret = find_set_type_minmax(typename, family, &min, &max); | ||
1375 | if (ret) | ||
1376 | return ret; | ||
1377 | |||
1378 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
1379 | if (skb2 == NULL) | ||
1380 | return -ENOMEM; | ||
1381 | |||
1382 | nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, | ||
1383 | IPSET_CMD_TYPE); | ||
1384 | if (!nlh2) | ||
1385 | goto nlmsg_failure; | ||
1386 | NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); | ||
1387 | NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, typename); | ||
1388 | NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, family); | ||
1389 | NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, max); | ||
1390 | NLA_PUT_U8(skb2, IPSET_ATTR_REVISION_MIN, min); | ||
1391 | nlmsg_end(skb2, nlh2); | ||
1392 | |||
1393 | pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len); | ||
1394 | ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | ||
1395 | if (ret < 0) | ||
1396 | return ret; | ||
1397 | |||
1398 | return 0; | ||
1399 | |||
1400 | nla_put_failure: | ||
1401 | nlmsg_cancel(skb2, nlh2); | ||
1402 | nlmsg_failure: | ||
1403 | kfree_skb(skb2); | ||
1404 | return -EMSGSIZE; | ||
1405 | } | ||
1406 | |||
1407 | /* Get protocol version */ | ||
1408 | |||
1409 | static const struct nla_policy | ||
1410 | ip_set_protocol_policy[IPSET_ATTR_CMD_MAX + 1] = { | ||
1411 | [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, | ||
1412 | }; | ||
1413 | |||
1414 | static int | ||
1415 | ip_set_protocol(struct sock *ctnl, struct sk_buff *skb, | ||
1416 | const struct nlmsghdr *nlh, | ||
1417 | const struct nlattr * const attr[]) | ||
1418 | { | ||
1419 | struct sk_buff *skb2; | ||
1420 | struct nlmsghdr *nlh2; | ||
1421 | int ret = 0; | ||
1422 | |||
1423 | if (unlikely(attr[IPSET_ATTR_PROTOCOL] == NULL)) | ||
1424 | return -IPSET_ERR_PROTOCOL; | ||
1425 | |||
1426 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
1427 | if (skb2 == NULL) | ||
1428 | return -ENOMEM; | ||
1429 | |||
1430 | nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, | ||
1431 | IPSET_CMD_PROTOCOL); | ||
1432 | if (!nlh2) | ||
1433 | goto nlmsg_failure; | ||
1434 | NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); | ||
1435 | nlmsg_end(skb2, nlh2); | ||
1436 | |||
1437 | ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | ||
1438 | if (ret < 0) | ||
1439 | return ret; | ||
1440 | |||
1441 | return 0; | ||
1442 | |||
1443 | nla_put_failure: | ||
1444 | nlmsg_cancel(skb2, nlh2); | ||
1445 | nlmsg_failure: | ||
1446 | kfree_skb(skb2); | ||
1447 | return -EMSGSIZE; | ||
1448 | } | ||
1449 | |||
1450 | static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = { | ||
1451 | [IPSET_CMD_CREATE] = { | ||
1452 | .call = ip_set_create, | ||
1453 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1454 | .policy = ip_set_create_policy, | ||
1455 | }, | ||
1456 | [IPSET_CMD_DESTROY] = { | ||
1457 | .call = ip_set_destroy, | ||
1458 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1459 | .policy = ip_set_setname_policy, | ||
1460 | }, | ||
1461 | [IPSET_CMD_FLUSH] = { | ||
1462 | .call = ip_set_flush, | ||
1463 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1464 | .policy = ip_set_setname_policy, | ||
1465 | }, | ||
1466 | [IPSET_CMD_RENAME] = { | ||
1467 | .call = ip_set_rename, | ||
1468 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1469 | .policy = ip_set_setname2_policy, | ||
1470 | }, | ||
1471 | [IPSET_CMD_SWAP] = { | ||
1472 | .call = ip_set_swap, | ||
1473 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1474 | .policy = ip_set_setname2_policy, | ||
1475 | }, | ||
1476 | [IPSET_CMD_LIST] = { | ||
1477 | .call = ip_set_dump, | ||
1478 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1479 | .policy = ip_set_setname_policy, | ||
1480 | }, | ||
1481 | [IPSET_CMD_SAVE] = { | ||
1482 | .call = ip_set_dump, | ||
1483 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1484 | .policy = ip_set_setname_policy, | ||
1485 | }, | ||
1486 | [IPSET_CMD_ADD] = { | ||
1487 | .call = ip_set_uadd, | ||
1488 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1489 | .policy = ip_set_adt_policy, | ||
1490 | }, | ||
1491 | [IPSET_CMD_DEL] = { | ||
1492 | .call = ip_set_udel, | ||
1493 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1494 | .policy = ip_set_adt_policy, | ||
1495 | }, | ||
1496 | [IPSET_CMD_TEST] = { | ||
1497 | .call = ip_set_utest, | ||
1498 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1499 | .policy = ip_set_adt_policy, | ||
1500 | }, | ||
1501 | [IPSET_CMD_HEADER] = { | ||
1502 | .call = ip_set_header, | ||
1503 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1504 | .policy = ip_set_setname_policy, | ||
1505 | }, | ||
1506 | [IPSET_CMD_TYPE] = { | ||
1507 | .call = ip_set_type, | ||
1508 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1509 | .policy = ip_set_type_policy, | ||
1510 | }, | ||
1511 | [IPSET_CMD_PROTOCOL] = { | ||
1512 | .call = ip_set_protocol, | ||
1513 | .attr_count = IPSET_ATTR_CMD_MAX, | ||
1514 | .policy = ip_set_protocol_policy, | ||
1515 | }, | ||
1516 | }; | ||
1517 | |||
1518 | static struct nfnetlink_subsystem ip_set_netlink_subsys __read_mostly = { | ||
1519 | .name = "ip_set", | ||
1520 | .subsys_id = NFNL_SUBSYS_IPSET, | ||
1521 | .cb_count = IPSET_MSG_MAX, | ||
1522 | .cb = ip_set_netlink_subsys_cb, | ||
1523 | }; | ||
1524 | |||
1525 | /* Interface to iptables/ip6tables */ | ||
1526 | |||
1527 | static int | ||
1528 | ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | ||
1529 | { | ||
1530 | unsigned *op; | ||
1531 | void *data; | ||
1532 | int copylen = *len, ret = 0; | ||
1533 | |||
1534 | if (!capable(CAP_NET_ADMIN)) | ||
1535 | return -EPERM; | ||
1536 | if (optval != SO_IP_SET) | ||
1537 | return -EBADF; | ||
1538 | if (*len < sizeof(unsigned)) | ||
1539 | return -EINVAL; | ||
1540 | |||
1541 | data = vmalloc(*len); | ||
1542 | if (!data) | ||
1543 | return -ENOMEM; | ||
1544 | if (copy_from_user(data, user, *len) != 0) { | ||
1545 | ret = -EFAULT; | ||
1546 | goto done; | ||
1547 | } | ||
1548 | op = (unsigned *) data; | ||
1549 | |||
1550 | if (*op < IP_SET_OP_VERSION) { | ||
1551 | /* Check the version at the beginning of operations */ | ||
1552 | struct ip_set_req_version *req_version = data; | ||
1553 | if (req_version->version != IPSET_PROTOCOL) { | ||
1554 | ret = -EPROTO; | ||
1555 | goto done; | ||
1556 | } | ||
1557 | } | ||
1558 | |||
1559 | switch (*op) { | ||
1560 | case IP_SET_OP_VERSION: { | ||
1561 | struct ip_set_req_version *req_version = data; | ||
1562 | |||
1563 | if (*len != sizeof(struct ip_set_req_version)) { | ||
1564 | ret = -EINVAL; | ||
1565 | goto done; | ||
1566 | } | ||
1567 | |||
1568 | req_version->version = IPSET_PROTOCOL; | ||
1569 | ret = copy_to_user(user, req_version, | ||
1570 | sizeof(struct ip_set_req_version)); | ||
1571 | goto done; | ||
1572 | } | ||
1573 | case IP_SET_OP_GET_BYNAME: { | ||
1574 | struct ip_set_req_get_set *req_get = data; | ||
1575 | |||
1576 | if (*len != sizeof(struct ip_set_req_get_set)) { | ||
1577 | ret = -EINVAL; | ||
1578 | goto done; | ||
1579 | } | ||
1580 | req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; | ||
1581 | nfnl_lock(); | ||
1582 | req_get->set.index = find_set_id(req_get->set.name); | ||
1583 | nfnl_unlock(); | ||
1584 | goto copy; | ||
1585 | } | ||
1586 | case IP_SET_OP_GET_BYINDEX: { | ||
1587 | struct ip_set_req_get_set *req_get = data; | ||
1588 | |||
1589 | if (*len != sizeof(struct ip_set_req_get_set) || | ||
1590 | req_get->set.index >= ip_set_max) { | ||
1591 | ret = -EINVAL; | ||
1592 | goto done; | ||
1593 | } | ||
1594 | nfnl_lock(); | ||
1595 | strncpy(req_get->set.name, | ||
1596 | ip_set_list[req_get->set.index] | ||
1597 | ? ip_set_list[req_get->set.index]->name : "", | ||
1598 | IPSET_MAXNAMELEN); | ||
1599 | nfnl_unlock(); | ||
1600 | goto copy; | ||
1601 | } | ||
1602 | default: | ||
1603 | ret = -EBADMSG; | ||
1604 | goto done; | ||
1605 | } /* end of switch(op) */ | ||
1606 | |||
1607 | copy: | ||
1608 | ret = copy_to_user(user, data, copylen); | ||
1609 | |||
1610 | done: | ||
1611 | vfree(data); | ||
1612 | if (ret > 0) | ||
1613 | ret = 0; | ||
1614 | return ret; | ||
1615 | } | ||
1616 | |||
1617 | static struct nf_sockopt_ops so_set __read_mostly = { | ||
1618 | .pf = PF_INET, | ||
1619 | .get_optmin = SO_IP_SET, | ||
1620 | .get_optmax = SO_IP_SET + 1, | ||
1621 | .get = &ip_set_sockfn_get, | ||
1622 | .owner = THIS_MODULE, | ||
1623 | }; | ||
1624 | |||
1625 | static int __init | ||
1626 | ip_set_init(void) | ||
1627 | { | ||
1628 | int ret; | ||
1629 | |||
1630 | if (max_sets) | ||
1631 | ip_set_max = max_sets; | ||
1632 | if (ip_set_max >= IPSET_INVALID_ID) | ||
1633 | ip_set_max = IPSET_INVALID_ID - 1; | ||
1634 | |||
1635 | ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, | ||
1636 | GFP_KERNEL); | ||
1637 | if (!ip_set_list) { | ||
1638 | pr_err("ip_set: Unable to create ip_set_list\n"); | ||
1639 | return -ENOMEM; | ||
1640 | } | ||
1641 | |||
1642 | ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); | ||
1643 | if (ret != 0) { | ||
1644 | pr_err("ip_set: cannot register with nfnetlink.\n"); | ||
1645 | kfree(ip_set_list); | ||
1646 | return ret; | ||
1647 | } | ||
1648 | ret = nf_register_sockopt(&so_set); | ||
1649 | if (ret != 0) { | ||
1650 | pr_err("SO_SET registry failed: %d\n", ret); | ||
1651 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); | ||
1652 | kfree(ip_set_list); | ||
1653 | return ret; | ||
1654 | } | ||
1655 | |||
1656 | pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); | ||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | static void __exit | ||
1661 | ip_set_fini(void) | ||
1662 | { | ||
1663 | /* There can't be any existing set */ | ||
1664 | nf_unregister_sockopt(&so_set); | ||
1665 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); | ||
1666 | kfree(ip_set_list); | ||
1667 | pr_debug("these are the famous last words\n"); | ||
1668 | } | ||
1669 | |||
1670 | module_init(ip_set_init); | ||
1671 | module_exit(ip_set_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c new file mode 100644 index 000000000000..8d5227212686 --- /dev/null +++ b/net/netfilter/ipset/ip_set_getport.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* 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 | /* Get Layer-4 data from the packets */ | ||
9 | |||
10 | #include <linux/ip.h> | ||
11 | #include <linux/skbuff.h> | ||
12 | #include <linux/icmp.h> | ||
13 | #include <linux/icmpv6.h> | ||
14 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
15 | #include <net/ip.h> | ||
16 | #include <net/ipv6.h> | ||
17 | |||
18 | #include <linux/netfilter/ipset/ip_set_getport.h> | ||
19 | |||
20 | /* We must handle non-linear skbs */ | ||
21 | static bool | ||
22 | get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, | ||
23 | bool src, __be16 *port, u8 *proto) | ||
24 | { | ||
25 | switch (protocol) { | ||
26 | case IPPROTO_TCP: { | ||
27 | struct tcphdr _tcph; | ||
28 | const struct tcphdr *th; | ||
29 | |||
30 | th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); | ||
31 | if (th == NULL) | ||
32 | /* No choice either */ | ||
33 | return false; | ||
34 | |||
35 | *port = src ? th->source : th->dest; | ||
36 | break; | ||
37 | } | ||
38 | case IPPROTO_UDP: { | ||
39 | struct udphdr _udph; | ||
40 | const struct udphdr *uh; | ||
41 | |||
42 | uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); | ||
43 | if (uh == NULL) | ||
44 | /* No choice either */ | ||
45 | return false; | ||
46 | |||
47 | *port = src ? uh->source : uh->dest; | ||
48 | break; | ||
49 | } | ||
50 | case IPPROTO_ICMP: { | ||
51 | struct icmphdr _ich; | ||
52 | const struct icmphdr *ic; | ||
53 | |||
54 | ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); | ||
55 | if (ic == NULL) | ||
56 | return false; | ||
57 | |||
58 | *port = (__force __be16)htons((ic->type << 8) | ic->code); | ||
59 | break; | ||
60 | } | ||
61 | case IPPROTO_ICMPV6: { | ||
62 | struct icmp6hdr _ich; | ||
63 | const struct icmp6hdr *ic; | ||
64 | |||
65 | ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); | ||
66 | if (ic == NULL) | ||
67 | return false; | ||
68 | |||
69 | *port = (__force __be16) | ||
70 | htons((ic->icmp6_type << 8) | ic->icmp6_code); | ||
71 | break; | ||
72 | } | ||
73 | default: | ||
74 | break; | ||
75 | } | ||
76 | *proto = protocol; | ||
77 | |||
78 | return true; | ||
79 | } | ||
80 | |||
81 | bool | ||
82 | ip_set_get_ip4_port(const struct sk_buff *skb, bool src, | ||
83 | __be16 *port, u8 *proto) | ||
84 | { | ||
85 | const struct iphdr *iph = ip_hdr(skb); | ||
86 | unsigned int protooff = ip_hdrlen(skb); | ||
87 | int protocol = iph->protocol; | ||
88 | |||
89 | /* See comments at tcp_match in ip_tables.c */ | ||
90 | if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) | ||
91 | return false; | ||
92 | |||
93 | return get_port(skb, protocol, protooff, src, port, proto); | ||
94 | } | ||
95 | EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); | ||
96 | |||
97 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
98 | bool | ||
99 | ip_set_get_ip6_port(const struct sk_buff *skb, bool src, | ||
100 | __be16 *port, u8 *proto) | ||
101 | { | ||
102 | int protoff; | ||
103 | u8 nexthdr; | ||
104 | |||
105 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
106 | protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); | ||
107 | if (protoff < 0) | ||
108 | return false; | ||
109 | |||
110 | return get_port(skb, nexthdr, protoff, src, port, proto); | ||
111 | } | ||
112 | EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); | ||
113 | #endif | ||
114 | |||
115 | bool | ||
116 | ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) | ||
117 | { | ||
118 | bool ret; | ||
119 | u8 proto; | ||
120 | |||
121 | switch (pf) { | ||
122 | case AF_INET: | ||
123 | ret = ip_set_get_ip4_port(skb, src, port, &proto); | ||
124 | break; | ||
125 | case AF_INET6: | ||
126 | ret = ip_set_get_ip6_port(skb, src, port, &proto); | ||
127 | break; | ||
128 | default: | ||
129 | return false; | ||
130 | } | ||
131 | if (!ret) | ||
132 | return ret; | ||
133 | switch (proto) { | ||
134 | case IPPROTO_TCP: | ||
135 | case IPPROTO_UDP: | ||
136 | return true; | ||
137 | default: | ||
138 | return false; | ||
139 | } | ||
140 | } | ||
141 | EXPORT_SYMBOL_GPL(ip_set_get_ip_port); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c new file mode 100644 index 000000000000..43bcce200129 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ip.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /* 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:ip 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_timeout.h> | ||
25 | #include <linux/netfilter/ipset/ip_set_hash.h> | ||
26 | |||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
29 | MODULE_DESCRIPTION("hash:ip type of IP sets"); | ||
30 | MODULE_ALIAS("ip_set_hash:ip"); | ||
31 | |||
32 | /* Type specific function prefix */ | ||
33 | #define TYPE hash_ip | ||
34 | |||
35 | static bool | ||
36 | hash_ip_same_set(const struct ip_set *a, const struct ip_set *b); | ||
37 | |||
38 | #define hash_ip4_same_set hash_ip_same_set | ||
39 | #define hash_ip6_same_set hash_ip_same_set | ||
40 | |||
41 | /* The type variant functions: IPv4 */ | ||
42 | |||
43 | /* Member elements without timeout */ | ||
44 | struct hash_ip4_elem { | ||
45 | __be32 ip; | ||
46 | }; | ||
47 | |||
48 | /* Member elements with timeout support */ | ||
49 | struct hash_ip4_telem { | ||
50 | __be32 ip; | ||
51 | unsigned long timeout; | ||
52 | }; | ||
53 | |||
54 | static inline bool | ||
55 | hash_ip4_data_equal(const struct hash_ip4_elem *ip1, | ||
56 | const struct hash_ip4_elem *ip2) | ||
57 | { | ||
58 | return ip1->ip == ip2->ip; | ||
59 | } | ||
60 | |||
61 | static inline bool | ||
62 | hash_ip4_data_isnull(const struct hash_ip4_elem *elem) | ||
63 | { | ||
64 | return elem->ip == 0; | ||
65 | } | ||
66 | |||
67 | static inline void | ||
68 | hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src) | ||
69 | { | ||
70 | dst->ip = src->ip; | ||
71 | } | ||
72 | |||
73 | /* Zero valued IP addresses cannot be stored */ | ||
74 | static inline void | ||
75 | hash_ip4_data_zero_out(struct hash_ip4_elem *elem) | ||
76 | { | ||
77 | elem->ip = 0; | ||
78 | } | ||
79 | |||
80 | static inline bool | ||
81 | hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data) | ||
82 | { | ||
83 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
84 | return 0; | ||
85 | |||
86 | nla_put_failure: | ||
87 | return 1; | ||
88 | } | ||
89 | |||
90 | static bool | ||
91 | hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data) | ||
92 | { | ||
93 | const struct hash_ip4_telem *tdata = | ||
94 | (const struct hash_ip4_telem *)data; | ||
95 | |||
96 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
97 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
98 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
99 | |||
100 | return 0; | ||
101 | |||
102 | nla_put_failure: | ||
103 | return 1; | ||
104 | } | ||
105 | |||
106 | #define IP_SET_HASH_WITH_NETMASK | ||
107 | #define PF 4 | ||
108 | #define HOST_MASK 32 | ||
109 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
110 | |||
111 | static int | ||
112 | hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
113 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
114 | { | ||
115 | const struct ip_set_hash *h = set->data; | ||
116 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
117 | __be32 ip; | ||
118 | |||
119 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip); | ||
120 | ip &= ip_set_netmask(h->netmask); | ||
121 | if (ip == 0) | ||
122 | return -EINVAL; | ||
123 | |||
124 | return adtfn(set, &ip, h->timeout); | ||
125 | } | ||
126 | |||
127 | static int | ||
128 | hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
129 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
130 | { | ||
131 | const struct ip_set_hash *h = set->data; | ||
132 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
133 | u32 ip, ip_to, hosts, timeout = h->timeout; | ||
134 | __be32 nip; | ||
135 | int ret = 0; | ||
136 | |||
137 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
138 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
139 | return -IPSET_ERR_PROTOCOL; | ||
140 | |||
141 | if (tb[IPSET_ATTR_LINENO]) | ||
142 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
143 | |||
144 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); | ||
145 | if (ret) | ||
146 | return ret; | ||
147 | |||
148 | ip &= ip_set_hostmask(h->netmask); | ||
149 | |||
150 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
151 | if (!with_timeout(h->timeout)) | ||
152 | return -IPSET_ERR_TIMEOUT; | ||
153 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
154 | } | ||
155 | |||
156 | if (adt == IPSET_TEST) { | ||
157 | nip = htonl(ip); | ||
158 | if (nip == 0) | ||
159 | return -IPSET_ERR_HASH_ELEM; | ||
160 | return adtfn(set, &nip, timeout); | ||
161 | } | ||
162 | |||
163 | if (tb[IPSET_ATTR_IP_TO]) { | ||
164 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | ||
165 | if (ret) | ||
166 | return ret; | ||
167 | if (ip > ip_to) | ||
168 | swap(ip, ip_to); | ||
169 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
170 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
171 | |||
172 | if (cidr > 32) | ||
173 | return -IPSET_ERR_INVALID_CIDR; | ||
174 | ip &= ip_set_hostmask(cidr); | ||
175 | ip_to = ip | ~ip_set_hostmask(cidr); | ||
176 | } else | ||
177 | ip_to = ip; | ||
178 | |||
179 | hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); | ||
180 | |||
181 | for (; !before(ip_to, ip); ip += hosts) { | ||
182 | nip = htonl(ip); | ||
183 | if (nip == 0) | ||
184 | return -IPSET_ERR_HASH_ELEM; | ||
185 | ret = adtfn(set, &nip, timeout); | ||
186 | |||
187 | if (ret && !ip_set_eexist(ret, flags)) | ||
188 | return ret; | ||
189 | else | ||
190 | ret = 0; | ||
191 | } | ||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | static bool | ||
196 | hash_ip_same_set(const struct ip_set *a, const struct ip_set *b) | ||
197 | { | ||
198 | const struct ip_set_hash *x = a->data; | ||
199 | const struct ip_set_hash *y = b->data; | ||
200 | |||
201 | /* Resizing changes htable_bits, so we ignore it */ | ||
202 | return x->maxelem == y->maxelem && | ||
203 | x->timeout == y->timeout && | ||
204 | x->netmask == y->netmask; | ||
205 | } | ||
206 | |||
207 | /* The type variant functions: IPv6 */ | ||
208 | |||
209 | struct hash_ip6_elem { | ||
210 | union nf_inet_addr ip; | ||
211 | }; | ||
212 | |||
213 | struct hash_ip6_telem { | ||
214 | union nf_inet_addr ip; | ||
215 | unsigned long timeout; | ||
216 | }; | ||
217 | |||
218 | static inline bool | ||
219 | hash_ip6_data_equal(const struct hash_ip6_elem *ip1, | ||
220 | const struct hash_ip6_elem *ip2) | ||
221 | { | ||
222 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; | ||
223 | } | ||
224 | |||
225 | static inline bool | ||
226 | hash_ip6_data_isnull(const struct hash_ip6_elem *elem) | ||
227 | { | ||
228 | return ipv6_addr_any(&elem->ip.in6); | ||
229 | } | ||
230 | |||
231 | static inline void | ||
232 | hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src) | ||
233 | { | ||
234 | ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); | ||
235 | } | ||
236 | |||
237 | static inline void | ||
238 | hash_ip6_data_zero_out(struct hash_ip6_elem *elem) | ||
239 | { | ||
240 | ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0); | ||
241 | } | ||
242 | |||
243 | static inline void | ||
244 | ip6_netmask(union nf_inet_addr *ip, u8 prefix) | ||
245 | { | ||
246 | ip->ip6[0] &= ip_set_netmask6(prefix)[0]; | ||
247 | ip->ip6[1] &= ip_set_netmask6(prefix)[1]; | ||
248 | ip->ip6[2] &= ip_set_netmask6(prefix)[2]; | ||
249 | ip->ip6[3] &= ip_set_netmask6(prefix)[3]; | ||
250 | } | ||
251 | |||
252 | static bool | ||
253 | hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data) | ||
254 | { | ||
255 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
256 | return 0; | ||
257 | |||
258 | nla_put_failure: | ||
259 | return 1; | ||
260 | } | ||
261 | |||
262 | static bool | ||
263 | hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data) | ||
264 | { | ||
265 | const struct hash_ip6_telem *e = | ||
266 | (const struct hash_ip6_telem *)data; | ||
267 | |||
268 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
269 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
270 | htonl(ip_set_timeout_get(e->timeout))); | ||
271 | return 0; | ||
272 | |||
273 | nla_put_failure: | ||
274 | return 1; | ||
275 | } | ||
276 | |||
277 | #undef PF | ||
278 | #undef HOST_MASK | ||
279 | |||
280 | #define PF 6 | ||
281 | #define HOST_MASK 128 | ||
282 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
283 | |||
284 | static int | ||
285 | hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
286 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
287 | { | ||
288 | const struct ip_set_hash *h = set->data; | ||
289 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
290 | union nf_inet_addr ip; | ||
291 | |||
292 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6); | ||
293 | ip6_netmask(&ip, h->netmask); | ||
294 | if (ipv6_addr_any(&ip.in6)) | ||
295 | return -EINVAL; | ||
296 | |||
297 | return adtfn(set, &ip, h->timeout); | ||
298 | } | ||
299 | |||
300 | static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = { | ||
301 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
302 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
303 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
304 | }; | ||
305 | |||
306 | static int | ||
307 | hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
308 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
309 | { | ||
310 | const struct ip_set_hash *h = set->data; | ||
311 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
312 | union nf_inet_addr ip; | ||
313 | u32 timeout = h->timeout; | ||
314 | int ret; | ||
315 | |||
316 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
317 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
318 | tb[IPSET_ATTR_IP_TO] || | ||
319 | tb[IPSET_ATTR_CIDR])) | ||
320 | return -IPSET_ERR_PROTOCOL; | ||
321 | |||
322 | if (tb[IPSET_ATTR_LINENO]) | ||
323 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
324 | |||
325 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &ip); | ||
326 | if (ret) | ||
327 | return ret; | ||
328 | |||
329 | ip6_netmask(&ip, h->netmask); | ||
330 | if (ipv6_addr_any(&ip.in6)) | ||
331 | return -IPSET_ERR_HASH_ELEM; | ||
332 | |||
333 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
334 | if (!with_timeout(h->timeout)) | ||
335 | return -IPSET_ERR_TIMEOUT; | ||
336 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
337 | } | ||
338 | |||
339 | ret = adtfn(set, &ip, timeout); | ||
340 | |||
341 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
342 | } | ||
343 | |||
344 | /* Create hash:ip type of sets */ | ||
345 | |||
346 | static int | ||
347 | hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
348 | { | ||
349 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
350 | u8 netmask, hbits; | ||
351 | struct ip_set_hash *h; | ||
352 | |||
353 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
354 | return -IPSET_ERR_INVALID_FAMILY; | ||
355 | netmask = set->family == AF_INET ? 32 : 128; | ||
356 | pr_debug("Create set %s with family %s\n", | ||
357 | set->name, set->family == AF_INET ? "inet" : "inet6"); | ||
358 | |||
359 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
360 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
361 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
362 | return -IPSET_ERR_PROTOCOL; | ||
363 | |||
364 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
365 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
366 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
367 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
368 | } | ||
369 | |||
370 | if (tb[IPSET_ATTR_MAXELEM]) | ||
371 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
372 | |||
373 | if (tb[IPSET_ATTR_NETMASK]) { | ||
374 | netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); | ||
375 | |||
376 | if ((set->family == AF_INET && netmask > 32) || | ||
377 | (set->family == AF_INET6 && netmask > 128) || | ||
378 | netmask == 0) | ||
379 | return -IPSET_ERR_INVALID_NETMASK; | ||
380 | } | ||
381 | |||
382 | h = kzalloc(sizeof(*h), GFP_KERNEL); | ||
383 | if (!h) | ||
384 | return -ENOMEM; | ||
385 | |||
386 | h->maxelem = maxelem; | ||
387 | h->netmask = netmask; | ||
388 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
389 | h->timeout = IPSET_NO_TIMEOUT; | ||
390 | |||
391 | hbits = htable_bits(hashsize); | ||
392 | h->table = ip_set_alloc( | ||
393 | sizeof(struct htable) | ||
394 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
395 | if (!h->table) { | ||
396 | kfree(h); | ||
397 | return -ENOMEM; | ||
398 | } | ||
399 | h->table->htable_bits = hbits; | ||
400 | |||
401 | set->data = h; | ||
402 | |||
403 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
404 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
405 | |||
406 | set->variant = set->family == AF_INET | ||
407 | ? &hash_ip4_tvariant : &hash_ip6_tvariant; | ||
408 | |||
409 | if (set->family == AF_INET) | ||
410 | hash_ip4_gc_init(set); | ||
411 | else | ||
412 | hash_ip6_gc_init(set); | ||
413 | } else { | ||
414 | set->variant = set->family == AF_INET | ||
415 | ? &hash_ip4_variant : &hash_ip6_variant; | ||
416 | } | ||
417 | |||
418 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
419 | set->name, jhash_size(h->table->htable_bits), | ||
420 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static struct ip_set_type hash_ip_type __read_mostly = { | ||
426 | .name = "hash:ip", | ||
427 | .protocol = IPSET_PROTOCOL, | ||
428 | .features = IPSET_TYPE_IP, | ||
429 | .dimension = IPSET_DIM_ONE, | ||
430 | .family = AF_UNSPEC, | ||
431 | .revision = 0, | ||
432 | .create = hash_ip_create, | ||
433 | .create_policy = { | ||
434 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
435 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
436 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
437 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
438 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
439 | [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, | ||
440 | }, | ||
441 | .adt_policy = { | ||
442 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
443 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
444 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
445 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
446 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
447 | }, | ||
448 | .me = THIS_MODULE, | ||
449 | }; | ||
450 | |||
451 | static int __init | ||
452 | hash_ip_init(void) | ||
453 | { | ||
454 | return ip_set_type_register(&hash_ip_type); | ||
455 | } | ||
456 | |||
457 | static void __exit | ||
458 | hash_ip_fini(void) | ||
459 | { | ||
460 | ip_set_type_unregister(&hash_ip_type); | ||
461 | } | ||
462 | |||
463 | module_init(hash_ip_init); | ||
464 | module_exit(hash_ip_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c new file mode 100644 index 000000000000..adbe787ea5dc --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ipport.c | |||
@@ -0,0 +1,544 @@ | |||
1 | /* 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:ip,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> | ||
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_timeout.h> | ||
25 | #include <linux/netfilter/ipset/ip_set_getport.h> | ||
26 | #include <linux/netfilter/ipset/ip_set_hash.h> | ||
27 | |||
28 | MODULE_LICENSE("GPL"); | ||
29 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
30 | MODULE_DESCRIPTION("hash:ip,port type of IP sets"); | ||
31 | MODULE_ALIAS("ip_set_hash:ip,port"); | ||
32 | |||
33 | /* Type specific function prefix */ | ||
34 | #define TYPE hash_ipport | ||
35 | |||
36 | static bool | ||
37 | hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b); | ||
38 | |||
39 | #define hash_ipport4_same_set hash_ipport_same_set | ||
40 | #define hash_ipport6_same_set hash_ipport_same_set | ||
41 | |||
42 | /* The type variant functions: IPv4 */ | ||
43 | |||
44 | /* Member elements without timeout */ | ||
45 | struct hash_ipport4_elem { | ||
46 | __be32 ip; | ||
47 | __be16 port; | ||
48 | u8 proto; | ||
49 | u8 padding; | ||
50 | }; | ||
51 | |||
52 | /* Member elements with timeout support */ | ||
53 | struct hash_ipport4_telem { | ||
54 | __be32 ip; | ||
55 | __be16 port; | ||
56 | u8 proto; | ||
57 | u8 padding; | ||
58 | unsigned long timeout; | ||
59 | }; | ||
60 | |||
61 | static inline bool | ||
62 | hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, | ||
63 | const struct hash_ipport4_elem *ip2) | ||
64 | { | ||
65 | return ip1->ip == ip2->ip && | ||
66 | ip1->port == ip2->port && | ||
67 | ip1->proto == ip2->proto; | ||
68 | } | ||
69 | |||
70 | static inline bool | ||
71 | hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem) | ||
72 | { | ||
73 | return elem->proto == 0; | ||
74 | } | ||
75 | |||
76 | static inline void | ||
77 | hash_ipport4_data_copy(struct hash_ipport4_elem *dst, | ||
78 | const struct hash_ipport4_elem *src) | ||
79 | { | ||
80 | dst->ip = src->ip; | ||
81 | dst->port = src->port; | ||
82 | dst->proto = src->proto; | ||
83 | } | ||
84 | |||
85 | static inline void | ||
86 | hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem) | ||
87 | { | ||
88 | elem->proto = 0; | ||
89 | } | ||
90 | |||
91 | static bool | ||
92 | hash_ipport4_data_list(struct sk_buff *skb, | ||
93 | const struct hash_ipport4_elem *data) | ||
94 | { | ||
95 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
96 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
97 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
98 | return 0; | ||
99 | |||
100 | nla_put_failure: | ||
101 | return 1; | ||
102 | } | ||
103 | |||
104 | static bool | ||
105 | hash_ipport4_data_tlist(struct sk_buff *skb, | ||
106 | const struct hash_ipport4_elem *data) | ||
107 | { | ||
108 | const struct hash_ipport4_telem *tdata = | ||
109 | (const struct hash_ipport4_telem *)data; | ||
110 | |||
111 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
112 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); | ||
113 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
114 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
115 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
116 | |||
117 | return 0; | ||
118 | |||
119 | nla_put_failure: | ||
120 | return 1; | ||
121 | } | ||
122 | |||
123 | #define PF 4 | ||
124 | #define HOST_MASK 32 | ||
125 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
126 | |||
127 | static int | ||
128 | hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
129 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
130 | { | ||
131 | const struct ip_set_hash *h = set->data; | ||
132 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
133 | struct hash_ipport4_elem data = { }; | ||
134 | |||
135 | if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
136 | &data.port, &data.proto)) | ||
137 | return -EINVAL; | ||
138 | |||
139 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); | ||
140 | |||
141 | return adtfn(set, &data, h->timeout); | ||
142 | } | ||
143 | |||
144 | static int | ||
145 | hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
146 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
147 | { | ||
148 | const struct ip_set_hash *h = set->data; | ||
149 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
150 | struct hash_ipport4_elem data = { }; | ||
151 | u32 ip, ip_to, p, port, port_to; | ||
152 | u32 timeout = h->timeout; | ||
153 | int ret; | ||
154 | |||
155 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
156 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
157 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
158 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
159 | return -IPSET_ERR_PROTOCOL; | ||
160 | |||
161 | if (tb[IPSET_ATTR_LINENO]) | ||
162 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
163 | |||
164 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); | ||
165 | if (ret) | ||
166 | return ret; | ||
167 | |||
168 | if (tb[IPSET_ATTR_PORT]) | ||
169 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
170 | else | ||
171 | return -IPSET_ERR_PROTOCOL; | ||
172 | |||
173 | if (tb[IPSET_ATTR_PROTO]) { | ||
174 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
175 | |||
176 | if (data.proto == 0) | ||
177 | return -IPSET_ERR_INVALID_PROTO; | ||
178 | } else | ||
179 | return -IPSET_ERR_MISSING_PROTO; | ||
180 | |||
181 | switch (data.proto) { | ||
182 | case IPPROTO_UDP: | ||
183 | case IPPROTO_TCP: | ||
184 | case IPPROTO_ICMP: | ||
185 | break; | ||
186 | default: | ||
187 | data.port = 0; | ||
188 | break; | ||
189 | } | ||
190 | |||
191 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
192 | if (!with_timeout(h->timeout)) | ||
193 | return -IPSET_ERR_TIMEOUT; | ||
194 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
195 | } | ||
196 | |||
197 | if (adt == IPSET_TEST || | ||
198 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
199 | !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || | ||
200 | tb[IPSET_ATTR_PORT_TO])) { | ||
201 | ret = adtfn(set, &data, timeout); | ||
202 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
203 | } | ||
204 | |||
205 | ip = ntohl(data.ip); | ||
206 | if (tb[IPSET_ATTR_IP_TO]) { | ||
207 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | ||
208 | if (ret) | ||
209 | return ret; | ||
210 | if (ip > ip_to) | ||
211 | swap(ip, ip_to); | ||
212 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
213 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
214 | |||
215 | if (cidr > 32) | ||
216 | return -IPSET_ERR_INVALID_CIDR; | ||
217 | ip &= ip_set_hostmask(cidr); | ||
218 | ip_to = ip | ~ip_set_hostmask(cidr); | ||
219 | } else | ||
220 | ip_to = ip; | ||
221 | |||
222 | port = ntohs(data.port); | ||
223 | if (tb[IPSET_ATTR_PORT_TO]) { | ||
224 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
225 | if (port > port_to) | ||
226 | swap(port, port_to); | ||
227 | } else | ||
228 | port_to = port; | ||
229 | |||
230 | for (; !before(ip_to, ip); ip++) | ||
231 | for (p = port; p <= port_to; p++) { | ||
232 | data.ip = htonl(ip); | ||
233 | data.port = htons(p); | ||
234 | ret = adtfn(set, &data, timeout); | ||
235 | |||
236 | if (ret && !ip_set_eexist(ret, flags)) | ||
237 | return ret; | ||
238 | else | ||
239 | ret = 0; | ||
240 | } | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static bool | ||
245 | hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) | ||
246 | { | ||
247 | const struct ip_set_hash *x = a->data; | ||
248 | const struct ip_set_hash *y = b->data; | ||
249 | |||
250 | /* Resizing changes htable_bits, so we ignore it */ | ||
251 | return x->maxelem == y->maxelem && | ||
252 | x->timeout == y->timeout; | ||
253 | } | ||
254 | |||
255 | /* The type variant functions: IPv6 */ | ||
256 | |||
257 | struct hash_ipport6_elem { | ||
258 | union nf_inet_addr ip; | ||
259 | __be16 port; | ||
260 | u8 proto; | ||
261 | u8 padding; | ||
262 | }; | ||
263 | |||
264 | struct hash_ipport6_telem { | ||
265 | union nf_inet_addr ip; | ||
266 | __be16 port; | ||
267 | u8 proto; | ||
268 | u8 padding; | ||
269 | unsigned long timeout; | ||
270 | }; | ||
271 | |||
272 | static inline bool | ||
273 | hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, | ||
274 | const struct hash_ipport6_elem *ip2) | ||
275 | { | ||
276 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && | ||
277 | ip1->port == ip2->port && | ||
278 | ip1->proto == ip2->proto; | ||
279 | } | ||
280 | |||
281 | static inline bool | ||
282 | hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem) | ||
283 | { | ||
284 | return elem->proto == 0; | ||
285 | } | ||
286 | |||
287 | static inline void | ||
288 | hash_ipport6_data_copy(struct hash_ipport6_elem *dst, | ||
289 | const struct hash_ipport6_elem *src) | ||
290 | { | ||
291 | memcpy(dst, src, sizeof(*dst)); | ||
292 | } | ||
293 | |||
294 | static inline void | ||
295 | hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem) | ||
296 | { | ||
297 | elem->proto = 0; | ||
298 | } | ||
299 | |||
300 | static bool | ||
301 | hash_ipport6_data_list(struct sk_buff *skb, | ||
302 | const struct hash_ipport6_elem *data) | ||
303 | { | ||
304 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
305 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
306 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
307 | return 0; | ||
308 | |||
309 | nla_put_failure: | ||
310 | return 1; | ||
311 | } | ||
312 | |||
313 | static bool | ||
314 | hash_ipport6_data_tlist(struct sk_buff *skb, | ||
315 | const struct hash_ipport6_elem *data) | ||
316 | { | ||
317 | const struct hash_ipport6_telem *e = | ||
318 | (const struct hash_ipport6_telem *)data; | ||
319 | |||
320 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
321 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
322 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
323 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
324 | htonl(ip_set_timeout_get(e->timeout))); | ||
325 | return 0; | ||
326 | |||
327 | nla_put_failure: | ||
328 | return 1; | ||
329 | } | ||
330 | |||
331 | #undef PF | ||
332 | #undef HOST_MASK | ||
333 | |||
334 | #define PF 6 | ||
335 | #define HOST_MASK 128 | ||
336 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
337 | |||
338 | static int | ||
339 | hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
340 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
341 | { | ||
342 | const struct ip_set_hash *h = set->data; | ||
343 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
344 | struct hash_ipport6_elem data = { }; | ||
345 | |||
346 | if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
347 | &data.port, &data.proto)) | ||
348 | return -EINVAL; | ||
349 | |||
350 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); | ||
351 | |||
352 | return adtfn(set, &data, h->timeout); | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
357 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
358 | { | ||
359 | const struct ip_set_hash *h = set->data; | ||
360 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
361 | struct hash_ipport6_elem data = { }; | ||
362 | u32 port, port_to; | ||
363 | u32 timeout = h->timeout; | ||
364 | int ret; | ||
365 | |||
366 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
367 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
368 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
369 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
370 | tb[IPSET_ATTR_IP_TO] || | ||
371 | tb[IPSET_ATTR_CIDR])) | ||
372 | return -IPSET_ERR_PROTOCOL; | ||
373 | |||
374 | if (tb[IPSET_ATTR_LINENO]) | ||
375 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
376 | |||
377 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); | ||
378 | if (ret) | ||
379 | return ret; | ||
380 | |||
381 | if (tb[IPSET_ATTR_PORT]) | ||
382 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
383 | else | ||
384 | return -IPSET_ERR_PROTOCOL; | ||
385 | |||
386 | if (tb[IPSET_ATTR_PROTO]) { | ||
387 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
388 | |||
389 | if (data.proto == 0) | ||
390 | return -IPSET_ERR_INVALID_PROTO; | ||
391 | } else | ||
392 | return -IPSET_ERR_MISSING_PROTO; | ||
393 | |||
394 | switch (data.proto) { | ||
395 | case IPPROTO_UDP: | ||
396 | case IPPROTO_TCP: | ||
397 | case IPPROTO_ICMPV6: | ||
398 | break; | ||
399 | default: | ||
400 | data.port = 0; | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
405 | if (!with_timeout(h->timeout)) | ||
406 | return -IPSET_ERR_TIMEOUT; | ||
407 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
408 | } | ||
409 | |||
410 | if (adt == IPSET_TEST || | ||
411 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
412 | !tb[IPSET_ATTR_PORT_TO]) { | ||
413 | ret = adtfn(set, &data, timeout); | ||
414 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
415 | } | ||
416 | |||
417 | port = ntohs(data.port); | ||
418 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
419 | if (port > port_to) | ||
420 | swap(port, port_to); | ||
421 | |||
422 | for (; port <= port_to; port++) { | ||
423 | data.port = htons(port); | ||
424 | ret = adtfn(set, &data, timeout); | ||
425 | |||
426 | if (ret && !ip_set_eexist(ret, flags)) | ||
427 | return ret; | ||
428 | else | ||
429 | ret = 0; | ||
430 | } | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | /* Create hash:ip type of sets */ | ||
435 | |||
436 | static int | ||
437 | hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
438 | { | ||
439 | struct ip_set_hash *h; | ||
440 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
441 | u8 hbits; | ||
442 | |||
443 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
444 | return -IPSET_ERR_INVALID_FAMILY; | ||
445 | |||
446 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
447 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
448 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
449 | return -IPSET_ERR_PROTOCOL; | ||
450 | |||
451 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
452 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
453 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
454 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
455 | } | ||
456 | |||
457 | if (tb[IPSET_ATTR_MAXELEM]) | ||
458 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
459 | |||
460 | h = kzalloc(sizeof(*h), GFP_KERNEL); | ||
461 | if (!h) | ||
462 | return -ENOMEM; | ||
463 | |||
464 | h->maxelem = maxelem; | ||
465 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
466 | h->timeout = IPSET_NO_TIMEOUT; | ||
467 | |||
468 | hbits = htable_bits(hashsize); | ||
469 | h->table = ip_set_alloc( | ||
470 | sizeof(struct htable) | ||
471 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
472 | if (!h->table) { | ||
473 | kfree(h); | ||
474 | return -ENOMEM; | ||
475 | } | ||
476 | h->table->htable_bits = hbits; | ||
477 | |||
478 | set->data = h; | ||
479 | |||
480 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
481 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
482 | |||
483 | set->variant = set->family == AF_INET | ||
484 | ? &hash_ipport4_tvariant : &hash_ipport6_tvariant; | ||
485 | |||
486 | if (set->family == AF_INET) | ||
487 | hash_ipport4_gc_init(set); | ||
488 | else | ||
489 | hash_ipport6_gc_init(set); | ||
490 | } else { | ||
491 | set->variant = set->family == AF_INET | ||
492 | ? &hash_ipport4_variant : &hash_ipport6_variant; | ||
493 | } | ||
494 | |||
495 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
496 | set->name, jhash_size(h->table->htable_bits), | ||
497 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static struct ip_set_type hash_ipport_type __read_mostly = { | ||
503 | .name = "hash:ip,port", | ||
504 | .protocol = IPSET_PROTOCOL, | ||
505 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, | ||
506 | .dimension = IPSET_DIM_TWO, | ||
507 | .family = AF_UNSPEC, | ||
508 | .revision = 0, | ||
509 | .create = hash_ipport_create, | ||
510 | .create_policy = { | ||
511 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
512 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
513 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
514 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
515 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
516 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
517 | }, | ||
518 | .adt_policy = { | ||
519 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
520 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
521 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
522 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
523 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
524 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
525 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
526 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
527 | }, | ||
528 | .me = THIS_MODULE, | ||
529 | }; | ||
530 | |||
531 | static int __init | ||
532 | hash_ipport_init(void) | ||
533 | { | ||
534 | return ip_set_type_register(&hash_ipport_type); | ||
535 | } | ||
536 | |||
537 | static void __exit | ||
538 | hash_ipport_fini(void) | ||
539 | { | ||
540 | ip_set_type_unregister(&hash_ipport_type); | ||
541 | } | ||
542 | |||
543 | module_init(hash_ipport_init); | ||
544 | module_exit(hash_ipport_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c new file mode 100644 index 000000000000..22e23abb86c6 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c | |||
@@ -0,0 +1,562 @@ | |||
1 | /* 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:ip,port,ip 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_timeout.h> | ||
25 | #include <linux/netfilter/ipset/ip_set_getport.h> | ||
26 | #include <linux/netfilter/ipset/ip_set_hash.h> | ||
27 | |||
28 | MODULE_LICENSE("GPL"); | ||
29 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
30 | MODULE_DESCRIPTION("hash:ip,port,ip type of IP sets"); | ||
31 | MODULE_ALIAS("ip_set_hash:ip,port,ip"); | ||
32 | |||
33 | /* Type specific function prefix */ | ||
34 | #define TYPE hash_ipportip | ||
35 | |||
36 | static bool | ||
37 | hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b); | ||
38 | |||
39 | #define hash_ipportip4_same_set hash_ipportip_same_set | ||
40 | #define hash_ipportip6_same_set hash_ipportip_same_set | ||
41 | |||
42 | /* The type variant functions: IPv4 */ | ||
43 | |||
44 | /* Member elements without timeout */ | ||
45 | struct hash_ipportip4_elem { | ||
46 | __be32 ip; | ||
47 | __be32 ip2; | ||
48 | __be16 port; | ||
49 | u8 proto; | ||
50 | u8 padding; | ||
51 | }; | ||
52 | |||
53 | /* Member elements with timeout support */ | ||
54 | struct hash_ipportip4_telem { | ||
55 | __be32 ip; | ||
56 | __be32 ip2; | ||
57 | __be16 port; | ||
58 | u8 proto; | ||
59 | u8 padding; | ||
60 | unsigned long timeout; | ||
61 | }; | ||
62 | |||
63 | static inline bool | ||
64 | hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, | ||
65 | const struct hash_ipportip4_elem *ip2) | ||
66 | { | ||
67 | return ip1->ip == ip2->ip && | ||
68 | ip1->ip2 == ip2->ip2 && | ||
69 | ip1->port == ip2->port && | ||
70 | ip1->proto == ip2->proto; | ||
71 | } | ||
72 | |||
73 | static inline bool | ||
74 | hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem) | ||
75 | { | ||
76 | return elem->proto == 0; | ||
77 | } | ||
78 | |||
79 | static inline void | ||
80 | hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst, | ||
81 | const struct hash_ipportip4_elem *src) | ||
82 | { | ||
83 | memcpy(dst, src, sizeof(*dst)); | ||
84 | } | ||
85 | |||
86 | static inline void | ||
87 | hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem) | ||
88 | { | ||
89 | elem->proto = 0; | ||
90 | } | ||
91 | |||
92 | static bool | ||
93 | hash_ipportip4_data_list(struct sk_buff *skb, | ||
94 | const struct hash_ipportip4_elem *data) | ||
95 | { | ||
96 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
97 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); | ||
98 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
99 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
100 | return 0; | ||
101 | |||
102 | nla_put_failure: | ||
103 | return 1; | ||
104 | } | ||
105 | |||
106 | static bool | ||
107 | hash_ipportip4_data_tlist(struct sk_buff *skb, | ||
108 | const struct hash_ipportip4_elem *data) | ||
109 | { | ||
110 | const struct hash_ipportip4_telem *tdata = | ||
111 | (const struct hash_ipportip4_telem *)data; | ||
112 | |||
113 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
114 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); | ||
115 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); | ||
116 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
117 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
118 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
119 | |||
120 | return 0; | ||
121 | |||
122 | nla_put_failure: | ||
123 | return 1; | ||
124 | } | ||
125 | |||
126 | #define PF 4 | ||
127 | #define HOST_MASK 32 | ||
128 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
129 | |||
130 | static int | ||
131 | hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
132 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
133 | { | ||
134 | const struct ip_set_hash *h = set->data; | ||
135 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
136 | struct hash_ipportip4_elem data = { }; | ||
137 | |||
138 | if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
139 | &data.port, &data.proto)) | ||
140 | return -EINVAL; | ||
141 | |||
142 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); | ||
143 | ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); | ||
144 | |||
145 | return adtfn(set, &data, h->timeout); | ||
146 | } | ||
147 | |||
148 | static int | ||
149 | hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
150 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
151 | { | ||
152 | const struct ip_set_hash *h = set->data; | ||
153 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
154 | struct hash_ipportip4_elem data = { }; | ||
155 | u32 ip, ip_to, p, port, port_to; | ||
156 | u32 timeout = h->timeout; | ||
157 | int ret; | ||
158 | |||
159 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | ||
160 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
161 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
162 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
163 | return -IPSET_ERR_PROTOCOL; | ||
164 | |||
165 | if (tb[IPSET_ATTR_LINENO]) | ||
166 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
167 | |||
168 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); | ||
169 | if (ret) | ||
170 | return ret; | ||
171 | |||
172 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2); | ||
173 | if (ret) | ||
174 | return ret; | ||
175 | |||
176 | if (tb[IPSET_ATTR_PORT]) | ||
177 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
178 | else | ||
179 | return -IPSET_ERR_PROTOCOL; | ||
180 | |||
181 | if (tb[IPSET_ATTR_PROTO]) { | ||
182 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
183 | |||
184 | if (data.proto == 0) | ||
185 | return -IPSET_ERR_INVALID_PROTO; | ||
186 | } else | ||
187 | return -IPSET_ERR_MISSING_PROTO; | ||
188 | |||
189 | switch (data.proto) { | ||
190 | case IPPROTO_UDP: | ||
191 | case IPPROTO_TCP: | ||
192 | case IPPROTO_ICMP: | ||
193 | break; | ||
194 | default: | ||
195 | data.port = 0; | ||
196 | break; | ||
197 | } | ||
198 | |||
199 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
200 | if (!with_timeout(h->timeout)) | ||
201 | return -IPSET_ERR_TIMEOUT; | ||
202 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
203 | } | ||
204 | |||
205 | if (adt == IPSET_TEST || | ||
206 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
207 | !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || | ||
208 | tb[IPSET_ATTR_PORT_TO])) { | ||
209 | ret = adtfn(set, &data, timeout); | ||
210 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
211 | } | ||
212 | |||
213 | ip = ntohl(data.ip); | ||
214 | if (tb[IPSET_ATTR_IP_TO]) { | ||
215 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | if (ip > ip_to) | ||
219 | swap(ip, ip_to); | ||
220 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
221 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
222 | |||
223 | if (cidr > 32) | ||
224 | return -IPSET_ERR_INVALID_CIDR; | ||
225 | ip &= ip_set_hostmask(cidr); | ||
226 | ip_to = ip | ~ip_set_hostmask(cidr); | ||
227 | } else | ||
228 | ip_to = ip; | ||
229 | |||
230 | port = ntohs(data.port); | ||
231 | if (tb[IPSET_ATTR_PORT_TO]) { | ||
232 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
233 | if (port > port_to) | ||
234 | swap(port, port_to); | ||
235 | } else | ||
236 | port_to = port; | ||
237 | |||
238 | for (; !before(ip_to, ip); ip++) | ||
239 | for (p = port; p <= port_to; p++) { | ||
240 | data.ip = htonl(ip); | ||
241 | data.port = htons(p); | ||
242 | ret = adtfn(set, &data, timeout); | ||
243 | |||
244 | if (ret && !ip_set_eexist(ret, flags)) | ||
245 | return ret; | ||
246 | else | ||
247 | ret = 0; | ||
248 | } | ||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | static bool | ||
253 | hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b) | ||
254 | { | ||
255 | const struct ip_set_hash *x = a->data; | ||
256 | const struct ip_set_hash *y = b->data; | ||
257 | |||
258 | /* Resizing changes htable_bits, so we ignore it */ | ||
259 | return x->maxelem == y->maxelem && | ||
260 | x->timeout == y->timeout; | ||
261 | } | ||
262 | |||
263 | /* The type variant functions: IPv6 */ | ||
264 | |||
265 | struct hash_ipportip6_elem { | ||
266 | union nf_inet_addr ip; | ||
267 | union nf_inet_addr ip2; | ||
268 | __be16 port; | ||
269 | u8 proto; | ||
270 | u8 padding; | ||
271 | }; | ||
272 | |||
273 | struct hash_ipportip6_telem { | ||
274 | union nf_inet_addr ip; | ||
275 | union nf_inet_addr ip2; | ||
276 | __be16 port; | ||
277 | u8 proto; | ||
278 | u8 padding; | ||
279 | unsigned long timeout; | ||
280 | }; | ||
281 | |||
282 | static inline bool | ||
283 | hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, | ||
284 | const struct hash_ipportip6_elem *ip2) | ||
285 | { | ||
286 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && | ||
287 | ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && | ||
288 | ip1->port == ip2->port && | ||
289 | ip1->proto == ip2->proto; | ||
290 | } | ||
291 | |||
292 | static inline bool | ||
293 | hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem) | ||
294 | { | ||
295 | return elem->proto == 0; | ||
296 | } | ||
297 | |||
298 | static inline void | ||
299 | hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst, | ||
300 | const struct hash_ipportip6_elem *src) | ||
301 | { | ||
302 | memcpy(dst, src, sizeof(*dst)); | ||
303 | } | ||
304 | |||
305 | static inline void | ||
306 | hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem) | ||
307 | { | ||
308 | elem->proto = 0; | ||
309 | } | ||
310 | |||
311 | static bool | ||
312 | hash_ipportip6_data_list(struct sk_buff *skb, | ||
313 | const struct hash_ipportip6_elem *data) | ||
314 | { | ||
315 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
316 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); | ||
317 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
318 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
319 | return 0; | ||
320 | |||
321 | nla_put_failure: | ||
322 | return 1; | ||
323 | } | ||
324 | |||
325 | static bool | ||
326 | hash_ipportip6_data_tlist(struct sk_buff *skb, | ||
327 | const struct hash_ipportip6_elem *data) | ||
328 | { | ||
329 | const struct hash_ipportip6_telem *e = | ||
330 | (const struct hash_ipportip6_telem *)data; | ||
331 | |||
332 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
333 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); | ||
334 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
335 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
336 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
337 | htonl(ip_set_timeout_get(e->timeout))); | ||
338 | return 0; | ||
339 | |||
340 | nla_put_failure: | ||
341 | return 1; | ||
342 | } | ||
343 | |||
344 | #undef PF | ||
345 | #undef HOST_MASK | ||
346 | |||
347 | #define PF 6 | ||
348 | #define HOST_MASK 128 | ||
349 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
350 | |||
351 | static int | ||
352 | hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
353 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
354 | { | ||
355 | const struct ip_set_hash *h = set->data; | ||
356 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
357 | struct hash_ipportip6_elem data = { }; | ||
358 | |||
359 | if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
360 | &data.port, &data.proto)) | ||
361 | return -EINVAL; | ||
362 | |||
363 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); | ||
364 | ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); | ||
365 | |||
366 | return adtfn(set, &data, h->timeout); | ||
367 | } | ||
368 | |||
369 | static int | ||
370 | hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
371 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
372 | { | ||
373 | const struct ip_set_hash *h = set->data; | ||
374 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
375 | struct hash_ipportip6_elem data = { }; | ||
376 | u32 port, port_to; | ||
377 | u32 timeout = h->timeout; | ||
378 | int ret; | ||
379 | |||
380 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | ||
381 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
382 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
383 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
384 | tb[IPSET_ATTR_IP_TO] || | ||
385 | tb[IPSET_ATTR_CIDR])) | ||
386 | return -IPSET_ERR_PROTOCOL; | ||
387 | |||
388 | if (tb[IPSET_ATTR_LINENO]) | ||
389 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
390 | |||
391 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); | ||
392 | if (ret) | ||
393 | return ret; | ||
394 | |||
395 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2); | ||
396 | if (ret) | ||
397 | return ret; | ||
398 | |||
399 | if (tb[IPSET_ATTR_PORT]) | ||
400 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
401 | else | ||
402 | return -IPSET_ERR_PROTOCOL; | ||
403 | |||
404 | if (tb[IPSET_ATTR_PROTO]) { | ||
405 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
406 | |||
407 | if (data.proto == 0) | ||
408 | return -IPSET_ERR_INVALID_PROTO; | ||
409 | } else | ||
410 | return -IPSET_ERR_MISSING_PROTO; | ||
411 | |||
412 | switch (data.proto) { | ||
413 | case IPPROTO_UDP: | ||
414 | case IPPROTO_TCP: | ||
415 | case IPPROTO_ICMPV6: | ||
416 | break; | ||
417 | default: | ||
418 | data.port = 0; | ||
419 | break; | ||
420 | } | ||
421 | |||
422 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
423 | if (!with_timeout(h->timeout)) | ||
424 | return -IPSET_ERR_TIMEOUT; | ||
425 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
426 | } | ||
427 | |||
428 | if (adt == IPSET_TEST || | ||
429 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
430 | !tb[IPSET_ATTR_PORT_TO]) { | ||
431 | ret = adtfn(set, &data, timeout); | ||
432 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
433 | } | ||
434 | |||
435 | port = ntohs(data.port); | ||
436 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
437 | if (port > port_to) | ||
438 | swap(port, port_to); | ||
439 | |||
440 | for (; port <= port_to; port++) { | ||
441 | data.port = htons(port); | ||
442 | ret = adtfn(set, &data, timeout); | ||
443 | |||
444 | if (ret && !ip_set_eexist(ret, flags)) | ||
445 | return ret; | ||
446 | else | ||
447 | ret = 0; | ||
448 | } | ||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | /* Create hash:ip type of sets */ | ||
453 | |||
454 | static int | ||
455 | hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
456 | { | ||
457 | struct ip_set_hash *h; | ||
458 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
459 | u8 hbits; | ||
460 | |||
461 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
462 | return -IPSET_ERR_INVALID_FAMILY; | ||
463 | |||
464 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
465 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
466 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
467 | return -IPSET_ERR_PROTOCOL; | ||
468 | |||
469 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
470 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
471 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
472 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
473 | } | ||
474 | |||
475 | if (tb[IPSET_ATTR_MAXELEM]) | ||
476 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
477 | |||
478 | h = kzalloc(sizeof(*h), GFP_KERNEL); | ||
479 | if (!h) | ||
480 | return -ENOMEM; | ||
481 | |||
482 | h->maxelem = maxelem; | ||
483 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
484 | h->timeout = IPSET_NO_TIMEOUT; | ||
485 | |||
486 | hbits = htable_bits(hashsize); | ||
487 | h->table = ip_set_alloc( | ||
488 | sizeof(struct htable) | ||
489 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
490 | if (!h->table) { | ||
491 | kfree(h); | ||
492 | return -ENOMEM; | ||
493 | } | ||
494 | h->table->htable_bits = hbits; | ||
495 | |||
496 | set->data = h; | ||
497 | |||
498 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
499 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
500 | |||
501 | set->variant = set->family == AF_INET | ||
502 | ? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant; | ||
503 | |||
504 | if (set->family == AF_INET) | ||
505 | hash_ipportip4_gc_init(set); | ||
506 | else | ||
507 | hash_ipportip6_gc_init(set); | ||
508 | } else { | ||
509 | set->variant = set->family == AF_INET | ||
510 | ? &hash_ipportip4_variant : &hash_ipportip6_variant; | ||
511 | } | ||
512 | |||
513 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
514 | set->name, jhash_size(h->table->htable_bits), | ||
515 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static struct ip_set_type hash_ipportip_type __read_mostly = { | ||
521 | .name = "hash:ip,port,ip", | ||
522 | .protocol = IPSET_PROTOCOL, | ||
523 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, | ||
524 | .dimension = IPSET_DIM_THREE, | ||
525 | .family = AF_UNSPEC, | ||
526 | .revision = 0, | ||
527 | .create = hash_ipportip_create, | ||
528 | .create_policy = { | ||
529 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
530 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
531 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
532 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
533 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
534 | }, | ||
535 | .adt_policy = { | ||
536 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
537 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
538 | [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, | ||
539 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
540 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
541 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
542 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
543 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
544 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
545 | }, | ||
546 | .me = THIS_MODULE, | ||
547 | }; | ||
548 | |||
549 | static int __init | ||
550 | hash_ipportip_init(void) | ||
551 | { | ||
552 | return ip_set_type_register(&hash_ipportip_type); | ||
553 | } | ||
554 | |||
555 | static void __exit | ||
556 | hash_ipportip_fini(void) | ||
557 | { | ||
558 | ip_set_type_unregister(&hash_ipportip_type); | ||
559 | } | ||
560 | |||
561 | module_init(hash_ipportip_init); | ||
562 | module_exit(hash_ipportip_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c new file mode 100644 index 000000000000..6033e8b54bbd --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c | |||
@@ -0,0 +1,628 @@ | |||
1 | /* 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: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_timeout.h> | ||
25 | #include <linux/netfilter/ipset/ip_set_getport.h> | ||
26 | #include <linux/netfilter/ipset/ip_set_hash.h> | ||
27 | |||
28 | MODULE_LICENSE("GPL"); | ||
29 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
30 | MODULE_DESCRIPTION("hash:ip,port,net type of IP sets"); | ||
31 | MODULE_ALIAS("ip_set_hash:ip,port,net"); | ||
32 | |||
33 | /* Type specific function prefix */ | ||
34 | #define TYPE hash_ipportnet | ||
35 | |||
36 | static bool | ||
37 | hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b); | ||
38 | |||
39 | #define hash_ipportnet4_same_set hash_ipportnet_same_set | ||
40 | #define hash_ipportnet6_same_set hash_ipportnet_same_set | ||
41 | |||
42 | /* The type variant functions: IPv4 */ | ||
43 | |||
44 | /* Member elements without timeout */ | ||
45 | struct hash_ipportnet4_elem { | ||
46 | __be32 ip; | ||
47 | __be32 ip2; | ||
48 | __be16 port; | ||
49 | u8 cidr; | ||
50 | u8 proto; | ||
51 | }; | ||
52 | |||
53 | /* Member elements with timeout support */ | ||
54 | struct hash_ipportnet4_telem { | ||
55 | __be32 ip; | ||
56 | __be32 ip2; | ||
57 | __be16 port; | ||
58 | u8 cidr; | ||
59 | u8 proto; | ||
60 | unsigned long timeout; | ||
61 | }; | ||
62 | |||
63 | static inline bool | ||
64 | hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, | ||
65 | const struct hash_ipportnet4_elem *ip2) | ||
66 | { | ||
67 | return ip1->ip == ip2->ip && | ||
68 | ip1->ip2 == ip2->ip2 && | ||
69 | ip1->cidr == ip2->cidr && | ||
70 | ip1->port == ip2->port && | ||
71 | ip1->proto == ip2->proto; | ||
72 | } | ||
73 | |||
74 | static inline bool | ||
75 | hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem) | ||
76 | { | ||
77 | return elem->proto == 0; | ||
78 | } | ||
79 | |||
80 | static inline void | ||
81 | hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, | ||
82 | const struct hash_ipportnet4_elem *src) | ||
83 | { | ||
84 | memcpy(dst, src, sizeof(*dst)); | ||
85 | } | ||
86 | |||
87 | static inline void | ||
88 | hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) | ||
89 | { | ||
90 | elem->ip2 &= ip_set_netmask(cidr); | ||
91 | elem->cidr = cidr; | ||
92 | } | ||
93 | |||
94 | static inline void | ||
95 | hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem) | ||
96 | { | ||
97 | elem->proto = 0; | ||
98 | } | ||
99 | |||
100 | static bool | ||
101 | hash_ipportnet4_data_list(struct sk_buff *skb, | ||
102 | const struct hash_ipportnet4_elem *data) | ||
103 | { | ||
104 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
105 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); | ||
106 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
107 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); | ||
108 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
109 | return 0; | ||
110 | |||
111 | nla_put_failure: | ||
112 | return 1; | ||
113 | } | ||
114 | |||
115 | static bool | ||
116 | hash_ipportnet4_data_tlist(struct sk_buff *skb, | ||
117 | const struct hash_ipportnet4_elem *data) | ||
118 | { | ||
119 | const struct hash_ipportnet4_telem *tdata = | ||
120 | (const struct hash_ipportnet4_telem *)data; | ||
121 | |||
122 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
123 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); | ||
124 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); | ||
125 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); | ||
126 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
127 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
128 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
129 | |||
130 | return 0; | ||
131 | |||
132 | nla_put_failure: | ||
133 | return 1; | ||
134 | } | ||
135 | |||
136 | #define IP_SET_HASH_WITH_PROTO | ||
137 | #define IP_SET_HASH_WITH_NETS | ||
138 | |||
139 | #define PF 4 | ||
140 | #define HOST_MASK 32 | ||
141 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
142 | |||
143 | static int | ||
144 | hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
145 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
146 | { | ||
147 | const struct ip_set_hash *h = set->data; | ||
148 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
149 | struct hash_ipportnet4_elem data = | ||
150 | { .cidr = h->nets[0].cidr || HOST_MASK }; | ||
151 | |||
152 | if (data.cidr == 0) | ||
153 | return -EINVAL; | ||
154 | if (adt == IPSET_TEST) | ||
155 | data.cidr = HOST_MASK; | ||
156 | |||
157 | if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
158 | &data.port, &data.proto)) | ||
159 | return -EINVAL; | ||
160 | |||
161 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); | ||
162 | ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); | ||
163 | data.ip2 &= ip_set_netmask(data.cidr); | ||
164 | |||
165 | return adtfn(set, &data, h->timeout); | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
170 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
171 | { | ||
172 | const struct ip_set_hash *h = set->data; | ||
173 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
174 | struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; | ||
175 | u32 ip, ip_to, p, port, port_to; | ||
176 | u32 timeout = h->timeout; | ||
177 | int ret; | ||
178 | |||
179 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | ||
180 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
181 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
182 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
183 | return -IPSET_ERR_PROTOCOL; | ||
184 | |||
185 | if (tb[IPSET_ATTR_LINENO]) | ||
186 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
187 | |||
188 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); | ||
189 | if (ret) | ||
190 | return ret; | ||
191 | |||
192 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2); | ||
193 | if (ret) | ||
194 | return ret; | ||
195 | |||
196 | if (tb[IPSET_ATTR_CIDR2]) | ||
197 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | ||
198 | |||
199 | if (!data.cidr) | ||
200 | return -IPSET_ERR_INVALID_CIDR; | ||
201 | |||
202 | data.ip2 &= ip_set_netmask(data.cidr); | ||
203 | |||
204 | if (tb[IPSET_ATTR_PORT]) | ||
205 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
206 | else | ||
207 | return -IPSET_ERR_PROTOCOL; | ||
208 | |||
209 | if (tb[IPSET_ATTR_PROTO]) { | ||
210 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
211 | |||
212 | if (data.proto == 0) | ||
213 | return -IPSET_ERR_INVALID_PROTO; | ||
214 | } else | ||
215 | return -IPSET_ERR_MISSING_PROTO; | ||
216 | |||
217 | switch (data.proto) { | ||
218 | case IPPROTO_UDP: | ||
219 | case IPPROTO_TCP: | ||
220 | case IPPROTO_ICMP: | ||
221 | break; | ||
222 | default: | ||
223 | data.port = 0; | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
228 | if (!with_timeout(h->timeout)) | ||
229 | return -IPSET_ERR_TIMEOUT; | ||
230 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
231 | } | ||
232 | |||
233 | if (adt == IPSET_TEST || | ||
234 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
235 | !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || | ||
236 | tb[IPSET_ATTR_PORT_TO])) { | ||
237 | ret = adtfn(set, &data, timeout); | ||
238 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
239 | } | ||
240 | |||
241 | ip = ntohl(data.ip); | ||
242 | if (tb[IPSET_ATTR_IP_TO]) { | ||
243 | ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); | ||
244 | if (ret) | ||
245 | return ret; | ||
246 | if (ip > ip_to) | ||
247 | swap(ip, ip_to); | ||
248 | } else if (tb[IPSET_ATTR_CIDR]) { | ||
249 | u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
250 | |||
251 | if (cidr > 32) | ||
252 | return -IPSET_ERR_INVALID_CIDR; | ||
253 | ip &= ip_set_hostmask(cidr); | ||
254 | ip_to = ip | ~ip_set_hostmask(cidr); | ||
255 | } else | ||
256 | ip_to = ip; | ||
257 | |||
258 | port = ntohs(data.port); | ||
259 | if (tb[IPSET_ATTR_PORT_TO]) { | ||
260 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
261 | if (port > port_to) | ||
262 | swap(port, port_to); | ||
263 | } else | ||
264 | port_to = port; | ||
265 | |||
266 | for (; !before(ip_to, ip); ip++) | ||
267 | for (p = port; p <= port_to; p++) { | ||
268 | data.ip = htonl(ip); | ||
269 | data.port = htons(p); | ||
270 | ret = adtfn(set, &data, timeout); | ||
271 | |||
272 | if (ret && !ip_set_eexist(ret, flags)) | ||
273 | return ret; | ||
274 | else | ||
275 | ret = 0; | ||
276 | } | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static bool | ||
281 | hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) | ||
282 | { | ||
283 | const struct ip_set_hash *x = a->data; | ||
284 | const struct ip_set_hash *y = b->data; | ||
285 | |||
286 | /* Resizing changes htable_bits, so we ignore it */ | ||
287 | return x->maxelem == y->maxelem && | ||
288 | x->timeout == y->timeout; | ||
289 | } | ||
290 | |||
291 | /* The type variant functions: IPv6 */ | ||
292 | |||
293 | struct hash_ipportnet6_elem { | ||
294 | union nf_inet_addr ip; | ||
295 | union nf_inet_addr ip2; | ||
296 | __be16 port; | ||
297 | u8 cidr; | ||
298 | u8 proto; | ||
299 | }; | ||
300 | |||
301 | struct hash_ipportnet6_telem { | ||
302 | union nf_inet_addr ip; | ||
303 | union nf_inet_addr ip2; | ||
304 | __be16 port; | ||
305 | u8 cidr; | ||
306 | u8 proto; | ||
307 | unsigned long timeout; | ||
308 | }; | ||
309 | |||
310 | static inline bool | ||
311 | hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, | ||
312 | const struct hash_ipportnet6_elem *ip2) | ||
313 | { | ||
314 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && | ||
315 | ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && | ||
316 | ip1->cidr == ip2->cidr && | ||
317 | ip1->port == ip2->port && | ||
318 | ip1->proto == ip2->proto; | ||
319 | } | ||
320 | |||
321 | static inline bool | ||
322 | hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem) | ||
323 | { | ||
324 | return elem->proto == 0; | ||
325 | } | ||
326 | |||
327 | static inline void | ||
328 | hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, | ||
329 | const struct hash_ipportnet6_elem *src) | ||
330 | { | ||
331 | memcpy(dst, src, sizeof(*dst)); | ||
332 | } | ||
333 | |||
334 | static inline void | ||
335 | hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) | ||
336 | { | ||
337 | elem->proto = 0; | ||
338 | } | ||
339 | |||
340 | static inline void | ||
341 | ip6_netmask(union nf_inet_addr *ip, u8 prefix) | ||
342 | { | ||
343 | ip->ip6[0] &= ip_set_netmask6(prefix)[0]; | ||
344 | ip->ip6[1] &= ip_set_netmask6(prefix)[1]; | ||
345 | ip->ip6[2] &= ip_set_netmask6(prefix)[2]; | ||
346 | ip->ip6[3] &= ip_set_netmask6(prefix)[3]; | ||
347 | } | ||
348 | |||
349 | static inline void | ||
350 | hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) | ||
351 | { | ||
352 | ip6_netmask(&elem->ip2, cidr); | ||
353 | elem->cidr = cidr; | ||
354 | } | ||
355 | |||
356 | static bool | ||
357 | hash_ipportnet6_data_list(struct sk_buff *skb, | ||
358 | const struct hash_ipportnet6_elem *data) | ||
359 | { | ||
360 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
361 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); | ||
362 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
363 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); | ||
364 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
365 | return 0; | ||
366 | |||
367 | nla_put_failure: | ||
368 | return 1; | ||
369 | } | ||
370 | |||
371 | static bool | ||
372 | hash_ipportnet6_data_tlist(struct sk_buff *skb, | ||
373 | const struct hash_ipportnet6_elem *data) | ||
374 | { | ||
375 | const struct hash_ipportnet6_telem *e = | ||
376 | (const struct hash_ipportnet6_telem *)data; | ||
377 | |||
378 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
379 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); | ||
380 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
381 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); | ||
382 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
383 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
384 | htonl(ip_set_timeout_get(e->timeout))); | ||
385 | return 0; | ||
386 | |||
387 | nla_put_failure: | ||
388 | return 1; | ||
389 | } | ||
390 | |||
391 | #undef PF | ||
392 | #undef HOST_MASK | ||
393 | |||
394 | #define PF 6 | ||
395 | #define HOST_MASK 128 | ||
396 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
397 | |||
398 | static int | ||
399 | hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
400 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
401 | { | ||
402 | const struct ip_set_hash *h = set->data; | ||
403 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
404 | struct hash_ipportnet6_elem data = | ||
405 | { .cidr = h->nets[0].cidr || HOST_MASK }; | ||
406 | |||
407 | if (data.cidr == 0) | ||
408 | return -EINVAL; | ||
409 | if (adt == IPSET_TEST) | ||
410 | data.cidr = HOST_MASK; | ||
411 | |||
412 | if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
413 | &data.port, &data.proto)) | ||
414 | return -EINVAL; | ||
415 | |||
416 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); | ||
417 | ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); | ||
418 | ip6_netmask(&data.ip2, data.cidr); | ||
419 | |||
420 | return adtfn(set, &data, h->timeout); | ||
421 | } | ||
422 | |||
423 | static int | ||
424 | hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
425 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
426 | { | ||
427 | const struct ip_set_hash *h = set->data; | ||
428 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
429 | struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; | ||
430 | u32 port, port_to; | ||
431 | u32 timeout = h->timeout; | ||
432 | int ret; | ||
433 | |||
434 | if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || | ||
435 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
436 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
437 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
438 | tb[IPSET_ATTR_IP_TO] || | ||
439 | tb[IPSET_ATTR_CIDR])) | ||
440 | return -IPSET_ERR_PROTOCOL; | ||
441 | |||
442 | if (tb[IPSET_ATTR_LINENO]) | ||
443 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
444 | |||
445 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); | ||
446 | if (ret) | ||
447 | return ret; | ||
448 | |||
449 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2); | ||
450 | if (ret) | ||
451 | return ret; | ||
452 | |||
453 | if (tb[IPSET_ATTR_CIDR2]) | ||
454 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); | ||
455 | |||
456 | if (!data.cidr) | ||
457 | return -IPSET_ERR_INVALID_CIDR; | ||
458 | |||
459 | ip6_netmask(&data.ip2, data.cidr); | ||
460 | |||
461 | if (tb[IPSET_ATTR_PORT]) | ||
462 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
463 | else | ||
464 | return -IPSET_ERR_PROTOCOL; | ||
465 | |||
466 | if (tb[IPSET_ATTR_PROTO]) { | ||
467 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
468 | |||
469 | if (data.proto == 0) | ||
470 | return -IPSET_ERR_INVALID_PROTO; | ||
471 | } else | ||
472 | return -IPSET_ERR_MISSING_PROTO; | ||
473 | |||
474 | switch (data.proto) { | ||
475 | case IPPROTO_UDP: | ||
476 | case IPPROTO_TCP: | ||
477 | case IPPROTO_ICMPV6: | ||
478 | break; | ||
479 | default: | ||
480 | data.port = 0; | ||
481 | break; | ||
482 | } | ||
483 | |||
484 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
485 | if (!with_timeout(h->timeout)) | ||
486 | return -IPSET_ERR_TIMEOUT; | ||
487 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
488 | } | ||
489 | |||
490 | if (adt == IPSET_TEST || | ||
491 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
492 | !tb[IPSET_ATTR_PORT_TO]) { | ||
493 | ret = adtfn(set, &data, timeout); | ||
494 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
495 | } | ||
496 | |||
497 | port = ntohs(data.port); | ||
498 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
499 | if (port > port_to) | ||
500 | swap(port, port_to); | ||
501 | |||
502 | for (; port <= port_to; port++) { | ||
503 | data.port = htons(port); | ||
504 | ret = adtfn(set, &data, timeout); | ||
505 | |||
506 | if (ret && !ip_set_eexist(ret, flags)) | ||
507 | return ret; | ||
508 | else | ||
509 | ret = 0; | ||
510 | } | ||
511 | return ret; | ||
512 | } | ||
513 | |||
514 | /* Create hash:ip type of sets */ | ||
515 | |||
516 | static int | ||
517 | hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
518 | { | ||
519 | struct ip_set_hash *h; | ||
520 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
521 | u8 hbits; | ||
522 | |||
523 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
524 | return -IPSET_ERR_INVALID_FAMILY; | ||
525 | |||
526 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
527 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
528 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
529 | return -IPSET_ERR_PROTOCOL; | ||
530 | |||
531 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
532 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
533 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
534 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
535 | } | ||
536 | |||
537 | if (tb[IPSET_ATTR_MAXELEM]) | ||
538 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
539 | |||
540 | h = kzalloc(sizeof(*h) | ||
541 | + sizeof(struct ip_set_hash_nets) | ||
542 | * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); | ||
543 | if (!h) | ||
544 | return -ENOMEM; | ||
545 | |||
546 | h->maxelem = maxelem; | ||
547 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
548 | h->timeout = IPSET_NO_TIMEOUT; | ||
549 | |||
550 | hbits = htable_bits(hashsize); | ||
551 | h->table = ip_set_alloc( | ||
552 | sizeof(struct htable) | ||
553 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
554 | if (!h->table) { | ||
555 | kfree(h); | ||
556 | return -ENOMEM; | ||
557 | } | ||
558 | h->table->htable_bits = hbits; | ||
559 | |||
560 | set->data = h; | ||
561 | |||
562 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
563 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
564 | |||
565 | set->variant = set->family == AF_INET | ||
566 | ? &hash_ipportnet4_tvariant | ||
567 | : &hash_ipportnet6_tvariant; | ||
568 | |||
569 | if (set->family == AF_INET) | ||
570 | hash_ipportnet4_gc_init(set); | ||
571 | else | ||
572 | hash_ipportnet6_gc_init(set); | ||
573 | } else { | ||
574 | set->variant = set->family == AF_INET | ||
575 | ? &hash_ipportnet4_variant : &hash_ipportnet6_variant; | ||
576 | } | ||
577 | |||
578 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
579 | set->name, jhash_size(h->table->htable_bits), | ||
580 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | static struct ip_set_type hash_ipportnet_type __read_mostly = { | ||
586 | .name = "hash:ip,port,net", | ||
587 | .protocol = IPSET_PROTOCOL, | ||
588 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, | ||
589 | .dimension = IPSET_DIM_THREE, | ||
590 | .family = AF_UNSPEC, | ||
591 | .revision = 0, | ||
592 | .create = hash_ipportnet_create, | ||
593 | .create_policy = { | ||
594 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
595 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
596 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
597 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
598 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
599 | }, | ||
600 | .adt_policy = { | ||
601 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
602 | [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, | ||
603 | [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, | ||
604 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
605 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
606 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
607 | [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, | ||
608 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
609 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
610 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
611 | }, | ||
612 | .me = THIS_MODULE, | ||
613 | }; | ||
614 | |||
615 | static int __init | ||
616 | hash_ipportnet_init(void) | ||
617 | { | ||
618 | return ip_set_type_register(&hash_ipportnet_type); | ||
619 | } | ||
620 | |||
621 | static void __exit | ||
622 | hash_ipportnet_fini(void) | ||
623 | { | ||
624 | ip_set_type_unregister(&hash_ipportnet_type); | ||
625 | } | ||
626 | |||
627 | module_init(hash_ipportnet_init); | ||
628 | module_exit(hash_ipportnet_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c new file mode 100644 index 000000000000..c4db202b7da4 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_net.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* 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 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 | |||
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_hash.h> | ||
25 | |||
26 | MODULE_LICENSE("GPL"); | ||
27 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
28 | MODULE_DESCRIPTION("hash:net type of IP sets"); | ||
29 | MODULE_ALIAS("ip_set_hash:net"); | ||
30 | |||
31 | /* Type specific function prefix */ | ||
32 | #define TYPE hash_net | ||
33 | |||
34 | static bool | ||
35 | hash_net_same_set(const struct ip_set *a, const struct ip_set *b); | ||
36 | |||
37 | #define hash_net4_same_set hash_net_same_set | ||
38 | #define hash_net6_same_set hash_net_same_set | ||
39 | |||
40 | /* The type variant functions: IPv4 */ | ||
41 | |||
42 | /* Member elements without timeout */ | ||
43 | struct hash_net4_elem { | ||
44 | __be32 ip; | ||
45 | u16 padding0; | ||
46 | u8 padding1; | ||
47 | u8 cidr; | ||
48 | }; | ||
49 | |||
50 | /* Member elements with timeout support */ | ||
51 | struct hash_net4_telem { | ||
52 | __be32 ip; | ||
53 | u16 padding0; | ||
54 | u8 padding1; | ||
55 | u8 cidr; | ||
56 | unsigned long timeout; | ||
57 | }; | ||
58 | |||
59 | static inline bool | ||
60 | hash_net4_data_equal(const struct hash_net4_elem *ip1, | ||
61 | const struct hash_net4_elem *ip2) | ||
62 | { | ||
63 | return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; | ||
64 | } | ||
65 | |||
66 | static inline bool | ||
67 | hash_net4_data_isnull(const struct hash_net4_elem *elem) | ||
68 | { | ||
69 | return elem->cidr == 0; | ||
70 | } | ||
71 | |||
72 | static inline void | ||
73 | hash_net4_data_copy(struct hash_net4_elem *dst, | ||
74 | const struct hash_net4_elem *src) | ||
75 | { | ||
76 | dst->ip = src->ip; | ||
77 | dst->cidr = src->cidr; | ||
78 | } | ||
79 | |||
80 | static inline void | ||
81 | hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr) | ||
82 | { | ||
83 | elem->ip &= ip_set_netmask(cidr); | ||
84 | elem->cidr = cidr; | ||
85 | } | ||
86 | |||
87 | /* Zero CIDR values cannot be stored */ | ||
88 | static inline void | ||
89 | hash_net4_data_zero_out(struct hash_net4_elem *elem) | ||
90 | { | ||
91 | elem->cidr = 0; | ||
92 | } | ||
93 | |||
94 | static bool | ||
95 | hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data) | ||
96 | { | ||
97 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
98 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
99 | return 0; | ||
100 | |||
101 | nla_put_failure: | ||
102 | return 1; | ||
103 | } | ||
104 | |||
105 | static bool | ||
106 | hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data) | ||
107 | { | ||
108 | const struct hash_net4_telem *tdata = | ||
109 | (const struct hash_net4_telem *)data; | ||
110 | |||
111 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
112 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr); | ||
113 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
114 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
115 | |||
116 | return 0; | ||
117 | |||
118 | nla_put_failure: | ||
119 | return 1; | ||
120 | } | ||
121 | |||
122 | #define IP_SET_HASH_WITH_NETS | ||
123 | |||
124 | #define PF 4 | ||
125 | #define HOST_MASK 32 | ||
126 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
127 | |||
128 | static int | ||
129 | hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
130 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
131 | { | ||
132 | const struct ip_set_hash *h = set->data; | ||
133 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
134 | struct hash_net4_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; | ||
135 | |||
136 | if (data.cidr == 0) | ||
137 | return -EINVAL; | ||
138 | if (adt == IPSET_TEST) | ||
139 | data.cidr = HOST_MASK; | ||
140 | |||
141 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); | ||
142 | data.ip &= ip_set_netmask(data.cidr); | ||
143 | |||
144 | return adtfn(set, &data, h->timeout); | ||
145 | } | ||
146 | |||
147 | static int | ||
148 | hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
149 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
150 | { | ||
151 | const struct ip_set_hash *h = set->data; | ||
152 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
153 | struct hash_net4_elem data = { .cidr = HOST_MASK }; | ||
154 | u32 timeout = h->timeout; | ||
155 | int ret; | ||
156 | |||
157 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
158 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
159 | return -IPSET_ERR_PROTOCOL; | ||
160 | |||
161 | if (tb[IPSET_ATTR_LINENO]) | ||
162 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
163 | |||
164 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); | ||
165 | if (ret) | ||
166 | return ret; | ||
167 | |||
168 | if (tb[IPSET_ATTR_CIDR]) | ||
169 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
170 | |||
171 | if (!data.cidr) | ||
172 | return -IPSET_ERR_INVALID_CIDR; | ||
173 | |||
174 | data.ip &= ip_set_netmask(data.cidr); | ||
175 | |||
176 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
177 | if (!with_timeout(h->timeout)) | ||
178 | return -IPSET_ERR_TIMEOUT; | ||
179 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
180 | } | ||
181 | |||
182 | ret = adtfn(set, &data, timeout); | ||
183 | |||
184 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
185 | } | ||
186 | |||
187 | static bool | ||
188 | hash_net_same_set(const struct ip_set *a, const struct ip_set *b) | ||
189 | { | ||
190 | const struct ip_set_hash *x = a->data; | ||
191 | const struct ip_set_hash *y = b->data; | ||
192 | |||
193 | /* Resizing changes htable_bits, so we ignore it */ | ||
194 | return x->maxelem == y->maxelem && | ||
195 | x->timeout == y->timeout; | ||
196 | } | ||
197 | |||
198 | /* The type variant functions: IPv6 */ | ||
199 | |||
200 | struct hash_net6_elem { | ||
201 | union nf_inet_addr ip; | ||
202 | u16 padding0; | ||
203 | u8 padding1; | ||
204 | u8 cidr; | ||
205 | }; | ||
206 | |||
207 | struct hash_net6_telem { | ||
208 | union nf_inet_addr ip; | ||
209 | u16 padding0; | ||
210 | u8 padding1; | ||
211 | u8 cidr; | ||
212 | unsigned long timeout; | ||
213 | }; | ||
214 | |||
215 | static inline bool | ||
216 | hash_net6_data_equal(const struct hash_net6_elem *ip1, | ||
217 | const struct hash_net6_elem *ip2) | ||
218 | { | ||
219 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && | ||
220 | ip1->cidr == ip2->cidr; | ||
221 | } | ||
222 | |||
223 | static inline bool | ||
224 | hash_net6_data_isnull(const struct hash_net6_elem *elem) | ||
225 | { | ||
226 | return elem->cidr == 0; | ||
227 | } | ||
228 | |||
229 | static inline void | ||
230 | hash_net6_data_copy(struct hash_net6_elem *dst, | ||
231 | const struct hash_net6_elem *src) | ||
232 | { | ||
233 | ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); | ||
234 | dst->cidr = src->cidr; | ||
235 | } | ||
236 | |||
237 | static inline void | ||
238 | hash_net6_data_zero_out(struct hash_net6_elem *elem) | ||
239 | { | ||
240 | elem->cidr = 0; | ||
241 | } | ||
242 | |||
243 | static inline void | ||
244 | ip6_netmask(union nf_inet_addr *ip, u8 prefix) | ||
245 | { | ||
246 | ip->ip6[0] &= ip_set_netmask6(prefix)[0]; | ||
247 | ip->ip6[1] &= ip_set_netmask6(prefix)[1]; | ||
248 | ip->ip6[2] &= ip_set_netmask6(prefix)[2]; | ||
249 | ip->ip6[3] &= ip_set_netmask6(prefix)[3]; | ||
250 | } | ||
251 | |||
252 | static inline void | ||
253 | hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr) | ||
254 | { | ||
255 | ip6_netmask(&elem->ip, cidr); | ||
256 | elem->cidr = cidr; | ||
257 | } | ||
258 | |||
259 | static bool | ||
260 | hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data) | ||
261 | { | ||
262 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
263 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
264 | return 0; | ||
265 | |||
266 | nla_put_failure: | ||
267 | return 1; | ||
268 | } | ||
269 | |||
270 | static bool | ||
271 | hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data) | ||
272 | { | ||
273 | const struct hash_net6_telem *e = | ||
274 | (const struct hash_net6_telem *)data; | ||
275 | |||
276 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
277 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr); | ||
278 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
279 | htonl(ip_set_timeout_get(e->timeout))); | ||
280 | return 0; | ||
281 | |||
282 | nla_put_failure: | ||
283 | return 1; | ||
284 | } | ||
285 | |||
286 | #undef PF | ||
287 | #undef HOST_MASK | ||
288 | |||
289 | #define PF 6 | ||
290 | #define HOST_MASK 128 | ||
291 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
292 | |||
293 | static int | ||
294 | hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
295 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
296 | { | ||
297 | const struct ip_set_hash *h = set->data; | ||
298 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
299 | struct hash_net6_elem data = { .cidr = h->nets[0].cidr || HOST_MASK }; | ||
300 | |||
301 | if (data.cidr == 0) | ||
302 | return -EINVAL; | ||
303 | if (adt == IPSET_TEST) | ||
304 | data.cidr = HOST_MASK; | ||
305 | |||
306 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); | ||
307 | ip6_netmask(&data.ip, data.cidr); | ||
308 | |||
309 | return adtfn(set, &data, h->timeout); | ||
310 | } | ||
311 | |||
312 | static int | ||
313 | hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
314 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
315 | { | ||
316 | const struct ip_set_hash *h = set->data; | ||
317 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
318 | struct hash_net6_elem data = { .cidr = HOST_MASK }; | ||
319 | u32 timeout = h->timeout; | ||
320 | int ret; | ||
321 | |||
322 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
323 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
324 | return -IPSET_ERR_PROTOCOL; | ||
325 | |||
326 | if (tb[IPSET_ATTR_LINENO]) | ||
327 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
328 | |||
329 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); | ||
330 | if (ret) | ||
331 | return ret; | ||
332 | |||
333 | if (tb[IPSET_ATTR_CIDR]) | ||
334 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
335 | |||
336 | if (!data.cidr) | ||
337 | return -IPSET_ERR_INVALID_CIDR; | ||
338 | |||
339 | ip6_netmask(&data.ip, data.cidr); | ||
340 | |||
341 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
342 | if (!with_timeout(h->timeout)) | ||
343 | return -IPSET_ERR_TIMEOUT; | ||
344 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
345 | } | ||
346 | |||
347 | ret = adtfn(set, &data, timeout); | ||
348 | |||
349 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
350 | } | ||
351 | |||
352 | /* Create hash:ip type of sets */ | ||
353 | |||
354 | static int | ||
355 | hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
356 | { | ||
357 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
358 | struct ip_set_hash *h; | ||
359 | u8 hbits; | ||
360 | |||
361 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
362 | return -IPSET_ERR_INVALID_FAMILY; | ||
363 | |||
364 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
365 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
366 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
367 | return -IPSET_ERR_PROTOCOL; | ||
368 | |||
369 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
370 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
371 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
372 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
373 | } | ||
374 | |||
375 | if (tb[IPSET_ATTR_MAXELEM]) | ||
376 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
377 | |||
378 | h = kzalloc(sizeof(*h) | ||
379 | + sizeof(struct ip_set_hash_nets) | ||
380 | * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); | ||
381 | if (!h) | ||
382 | return -ENOMEM; | ||
383 | |||
384 | h->maxelem = maxelem; | ||
385 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
386 | h->timeout = IPSET_NO_TIMEOUT; | ||
387 | |||
388 | hbits = htable_bits(hashsize); | ||
389 | h->table = ip_set_alloc( | ||
390 | sizeof(struct htable) | ||
391 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
392 | if (!h->table) { | ||
393 | kfree(h); | ||
394 | return -ENOMEM; | ||
395 | } | ||
396 | h->table->htable_bits = hbits; | ||
397 | |||
398 | set->data = h; | ||
399 | |||
400 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
401 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
402 | |||
403 | set->variant = set->family == AF_INET | ||
404 | ? &hash_net4_tvariant : &hash_net6_tvariant; | ||
405 | |||
406 | if (set->family == AF_INET) | ||
407 | hash_net4_gc_init(set); | ||
408 | else | ||
409 | hash_net6_gc_init(set); | ||
410 | } else { | ||
411 | set->variant = set->family == AF_INET | ||
412 | ? &hash_net4_variant : &hash_net6_variant; | ||
413 | } | ||
414 | |||
415 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
416 | set->name, jhash_size(h->table->htable_bits), | ||
417 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static struct ip_set_type hash_net_type __read_mostly = { | ||
423 | .name = "hash:net", | ||
424 | .protocol = IPSET_PROTOCOL, | ||
425 | .features = IPSET_TYPE_IP, | ||
426 | .dimension = IPSET_DIM_ONE, | ||
427 | .family = AF_UNSPEC, | ||
428 | .revision = 0, | ||
429 | .create = hash_net_create, | ||
430 | .create_policy = { | ||
431 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
432 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
433 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
434 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
435 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
436 | }, | ||
437 | .adt_policy = { | ||
438 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
439 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
440 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
441 | }, | ||
442 | .me = THIS_MODULE, | ||
443 | }; | ||
444 | |||
445 | static int __init | ||
446 | hash_net_init(void) | ||
447 | { | ||
448 | return ip_set_type_register(&hash_net_type); | ||
449 | } | ||
450 | |||
451 | static void __exit | ||
452 | hash_net_fini(void) | ||
453 | { | ||
454 | ip_set_type_unregister(&hash_net_type); | ||
455 | } | ||
456 | |||
457 | module_init(hash_net_init); | ||
458 | module_exit(hash_net_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c new file mode 100644 index 000000000000..34a165626ee9 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_netport.c | |||
@@ -0,0 +1,578 @@ | |||
1 | /* 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> | ||
15 | #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 | |||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
29 | MODULE_DESCRIPTION("hash:net,port type of IP sets"); | ||
30 | MODULE_ALIAS("ip_set_hash:net,port"); | ||
31 | |||
32 | /* Type specific function prefix */ | ||
33 | #define TYPE hash_netport | ||
34 | |||
35 | static bool | ||
36 | hash_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 | |||
43 | /* Member elements without timeout */ | ||
44 | struct hash_netport4_elem { | ||
45 | __be32 ip; | ||
46 | __be16 port; | ||
47 | u8 proto; | ||
48 | u8 cidr; | ||
49 | }; | ||
50 | |||
51 | /* Member elements with timeout support */ | ||
52 | struct hash_netport4_telem { | ||
53 | __be32 ip; | ||
54 | __be16 port; | ||
55 | u8 proto; | ||
56 | u8 cidr; | ||
57 | unsigned long timeout; | ||
58 | }; | ||
59 | |||
60 | static inline bool | ||
61 | hash_netport4_data_equal(const struct hash_netport4_elem *ip1, | ||
62 | const struct hash_netport4_elem *ip2) | ||
63 | { | ||
64 | return ip1->ip == ip2->ip && | ||
65 | ip1->port == ip2->port && | ||
66 | ip1->proto == ip2->proto && | ||
67 | ip1->cidr == ip2->cidr; | ||
68 | } | ||
69 | |||
70 | static inline bool | ||
71 | hash_netport4_data_isnull(const struct hash_netport4_elem *elem) | ||
72 | { | ||
73 | return elem->proto == 0; | ||
74 | } | ||
75 | |||
76 | static inline void | ||
77 | hash_netport4_data_copy(struct hash_netport4_elem *dst, | ||
78 | const struct hash_netport4_elem *src) | ||
79 | { | ||
80 | dst->ip = src->ip; | ||
81 | dst->port = src->port; | ||
82 | dst->proto = src->proto; | ||
83 | dst->cidr = src->cidr; | ||
84 | } | ||
85 | |||
86 | static inline void | ||
87 | hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr) | ||
88 | { | ||
89 | elem->ip &= ip_set_netmask(cidr); | ||
90 | elem->cidr = cidr; | ||
91 | } | ||
92 | |||
93 | static inline void | ||
94 | hash_netport4_data_zero_out(struct hash_netport4_elem *elem) | ||
95 | { | ||
96 | elem->proto = 0; | ||
97 | } | ||
98 | |||
99 | static bool | ||
100 | hash_netport4_data_list(struct sk_buff *skb, | ||
101 | const struct hash_netport4_elem *data) | ||
102 | { | ||
103 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); | ||
104 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
105 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
106 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
107 | return 0; | ||
108 | |||
109 | nla_put_failure: | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | static bool | ||
114 | hash_netport4_data_tlist(struct sk_buff *skb, | ||
115 | const struct hash_netport4_elem *data) | ||
116 | { | ||
117 | const struct hash_netport4_telem *tdata = | ||
118 | (const struct hash_netport4_telem *)data; | ||
119 | |||
120 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); | ||
121 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); | ||
122 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
123 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
124 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
125 | htonl(ip_set_timeout_get(tdata->timeout))); | ||
126 | |||
127 | return 0; | ||
128 | |||
129 | nla_put_failure: | ||
130 | return 1; | ||
131 | } | ||
132 | |||
133 | #define IP_SET_HASH_WITH_PROTO | ||
134 | #define IP_SET_HASH_WITH_NETS | ||
135 | |||
136 | #define PF 4 | ||
137 | #define HOST_MASK 32 | ||
138 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
139 | |||
140 | static int | ||
141 | hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
142 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
143 | { | ||
144 | const struct ip_set_hash *h = set->data; | ||
145 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
146 | struct hash_netport4_elem data = { | ||
147 | .cidr = h->nets[0].cidr || HOST_MASK }; | ||
148 | |||
149 | if (data.cidr == 0) | ||
150 | return -EINVAL; | ||
151 | if (adt == IPSET_TEST) | ||
152 | data.cidr = HOST_MASK; | ||
153 | |||
154 | if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
155 | &data.port, &data.proto)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); | ||
159 | data.ip &= ip_set_netmask(data.cidr); | ||
160 | |||
161 | return adtfn(set, &data, h->timeout); | ||
162 | } | ||
163 | |||
164 | static int | ||
165 | hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], | ||
166 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
167 | { | ||
168 | const struct ip_set_hash *h = set->data; | ||
169 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
170 | struct hash_netport4_elem data = { .cidr = HOST_MASK }; | ||
171 | u32 port, port_to; | ||
172 | u32 timeout = h->timeout; | ||
173 | int ret; | ||
174 | |||
175 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
176 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
177 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
178 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
179 | return -IPSET_ERR_PROTOCOL; | ||
180 | |||
181 | if (tb[IPSET_ATTR_LINENO]) | ||
182 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
183 | |||
184 | ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); | ||
185 | if (ret) | ||
186 | return ret; | ||
187 | |||
188 | if (tb[IPSET_ATTR_CIDR]) | ||
189 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
190 | if (!data.cidr) | ||
191 | return -IPSET_ERR_INVALID_CIDR; | ||
192 | data.ip &= ip_set_netmask(data.cidr); | ||
193 | |||
194 | if (tb[IPSET_ATTR_PORT]) | ||
195 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
196 | else | ||
197 | return -IPSET_ERR_PROTOCOL; | ||
198 | |||
199 | if (tb[IPSET_ATTR_PROTO]) { | ||
200 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
201 | |||
202 | if (data.proto == 0) | ||
203 | return -IPSET_ERR_INVALID_PROTO; | ||
204 | } else | ||
205 | return -IPSET_ERR_MISSING_PROTO; | ||
206 | |||
207 | switch (data.proto) { | ||
208 | case IPPROTO_UDP: | ||
209 | case IPPROTO_TCP: | ||
210 | case IPPROTO_ICMP: | ||
211 | break; | ||
212 | default: | ||
213 | data.port = 0; | ||
214 | break; | ||
215 | } | ||
216 | |||
217 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
218 | if (!with_timeout(h->timeout)) | ||
219 | return -IPSET_ERR_TIMEOUT; | ||
220 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
221 | } | ||
222 | |||
223 | if (adt == IPSET_TEST || | ||
224 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
225 | !tb[IPSET_ATTR_PORT_TO]) { | ||
226 | ret = adtfn(set, &data, timeout); | ||
227 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
228 | } | ||
229 | |||
230 | port = ntohs(data.port); | ||
231 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
232 | if (port > port_to) | ||
233 | swap(port, port_to); | ||
234 | |||
235 | for (; port <= port_to; port++) { | ||
236 | data.port = htons(port); | ||
237 | ret = adtfn(set, &data, timeout); | ||
238 | |||
239 | if (ret && !ip_set_eexist(ret, flags)) | ||
240 | return ret; | ||
241 | else | ||
242 | ret = 0; | ||
243 | } | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static bool | ||
248 | hash_netport_same_set(const struct ip_set *a, const struct ip_set *b) | ||
249 | { | ||
250 | const struct ip_set_hash *x = a->data; | ||
251 | const struct ip_set_hash *y = b->data; | ||
252 | |||
253 | /* Resizing changes htable_bits, so we ignore it */ | ||
254 | return x->maxelem == y->maxelem && | ||
255 | x->timeout == y->timeout; | ||
256 | } | ||
257 | |||
258 | /* The type variant functions: IPv6 */ | ||
259 | |||
260 | struct hash_netport6_elem { | ||
261 | union nf_inet_addr ip; | ||
262 | __be16 port; | ||
263 | u8 proto; | ||
264 | u8 cidr; | ||
265 | }; | ||
266 | |||
267 | struct hash_netport6_telem { | ||
268 | union nf_inet_addr ip; | ||
269 | __be16 port; | ||
270 | u8 proto; | ||
271 | u8 cidr; | ||
272 | unsigned long timeout; | ||
273 | }; | ||
274 | |||
275 | static inline bool | ||
276 | hash_netport6_data_equal(const struct hash_netport6_elem *ip1, | ||
277 | const struct hash_netport6_elem *ip2) | ||
278 | { | ||
279 | return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && | ||
280 | ip1->port == ip2->port && | ||
281 | ip1->proto == ip2->proto && | ||
282 | ip1->cidr == ip2->cidr; | ||
283 | } | ||
284 | |||
285 | static inline bool | ||
286 | hash_netport6_data_isnull(const struct hash_netport6_elem *elem) | ||
287 | { | ||
288 | return elem->proto == 0; | ||
289 | } | ||
290 | |||
291 | static inline void | ||
292 | hash_netport6_data_copy(struct hash_netport6_elem *dst, | ||
293 | const struct hash_netport6_elem *src) | ||
294 | { | ||
295 | memcpy(dst, src, sizeof(*dst)); | ||
296 | } | ||
297 | |||
298 | static inline void | ||
299 | hash_netport6_data_zero_out(struct hash_netport6_elem *elem) | ||
300 | { | ||
301 | elem->proto = 0; | ||
302 | } | ||
303 | |||
304 | static inline void | ||
305 | ip6_netmask(union nf_inet_addr *ip, u8 prefix) | ||
306 | { | ||
307 | ip->ip6[0] &= ip_set_netmask6(prefix)[0]; | ||
308 | ip->ip6[1] &= ip_set_netmask6(prefix)[1]; | ||
309 | ip->ip6[2] &= ip_set_netmask6(prefix)[2]; | ||
310 | ip->ip6[3] &= ip_set_netmask6(prefix)[3]; | ||
311 | } | ||
312 | |||
313 | static inline void | ||
314 | hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr) | ||
315 | { | ||
316 | ip6_netmask(&elem->ip, cidr); | ||
317 | elem->cidr = cidr; | ||
318 | } | ||
319 | |||
320 | static bool | ||
321 | hash_netport6_data_list(struct sk_buff *skb, | ||
322 | const struct hash_netport6_elem *data) | ||
323 | { | ||
324 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); | ||
325 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
326 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
327 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
328 | return 0; | ||
329 | |||
330 | nla_put_failure: | ||
331 | return 1; | ||
332 | } | ||
333 | |||
334 | static bool | ||
335 | hash_netport6_data_tlist(struct sk_buff *skb, | ||
336 | const struct hash_netport6_elem *data) | ||
337 | { | ||
338 | const struct hash_netport6_telem *e = | ||
339 | (const struct hash_netport6_telem *)data; | ||
340 | |||
341 | NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); | ||
342 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); | ||
343 | NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); | ||
344 | NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); | ||
345 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
346 | htonl(ip_set_timeout_get(e->timeout))); | ||
347 | return 0; | ||
348 | |||
349 | nla_put_failure: | ||
350 | return 1; | ||
351 | } | ||
352 | |||
353 | #undef PF | ||
354 | #undef HOST_MASK | ||
355 | |||
356 | #define PF 6 | ||
357 | #define HOST_MASK 128 | ||
358 | #include <linux/netfilter/ipset/ip_set_ahash.h> | ||
359 | |||
360 | static int | ||
361 | hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
362 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
363 | { | ||
364 | const struct ip_set_hash *h = set->data; | ||
365 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
366 | struct hash_netport6_elem data = { | ||
367 | .cidr = h->nets[0].cidr || HOST_MASK }; | ||
368 | |||
369 | if (data.cidr == 0) | ||
370 | return -EINVAL; | ||
371 | if (adt == IPSET_TEST) | ||
372 | data.cidr = HOST_MASK; | ||
373 | |||
374 | if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, | ||
375 | &data.port, &data.proto)) | ||
376 | return -EINVAL; | ||
377 | |||
378 | ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); | ||
379 | ip6_netmask(&data.ip, data.cidr); | ||
380 | |||
381 | return adtfn(set, &data, h->timeout); | ||
382 | } | ||
383 | |||
384 | static int | ||
385 | hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], | ||
386 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
387 | { | ||
388 | const struct ip_set_hash *h = set->data; | ||
389 | ipset_adtfn adtfn = set->variant->adt[adt]; | ||
390 | struct hash_netport6_elem data = { .cidr = HOST_MASK }; | ||
391 | u32 port, port_to; | ||
392 | u32 timeout = h->timeout; | ||
393 | int ret; | ||
394 | |||
395 | if (unlikely(!tb[IPSET_ATTR_IP] || | ||
396 | !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || | ||
397 | !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || | ||
398 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
399 | return -IPSET_ERR_PROTOCOL; | ||
400 | |||
401 | if (tb[IPSET_ATTR_LINENO]) | ||
402 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
403 | |||
404 | ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); | ||
405 | if (ret) | ||
406 | return ret; | ||
407 | |||
408 | if (tb[IPSET_ATTR_CIDR]) | ||
409 | data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); | ||
410 | if (!data.cidr) | ||
411 | return -IPSET_ERR_INVALID_CIDR; | ||
412 | ip6_netmask(&data.ip, data.cidr); | ||
413 | |||
414 | if (tb[IPSET_ATTR_PORT]) | ||
415 | data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); | ||
416 | else | ||
417 | return -IPSET_ERR_PROTOCOL; | ||
418 | |||
419 | if (tb[IPSET_ATTR_PROTO]) { | ||
420 | data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); | ||
421 | |||
422 | if (data.proto == 0) | ||
423 | return -IPSET_ERR_INVALID_PROTO; | ||
424 | } else | ||
425 | return -IPSET_ERR_MISSING_PROTO; | ||
426 | |||
427 | switch (data.proto) { | ||
428 | case IPPROTO_UDP: | ||
429 | case IPPROTO_TCP: | ||
430 | case IPPROTO_ICMPV6: | ||
431 | break; | ||
432 | default: | ||
433 | data.port = 0; | ||
434 | break; | ||
435 | } | ||
436 | |||
437 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
438 | if (!with_timeout(h->timeout)) | ||
439 | return -IPSET_ERR_TIMEOUT; | ||
440 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
441 | } | ||
442 | |||
443 | if (adt == IPSET_TEST || | ||
444 | !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) || | ||
445 | !tb[IPSET_ATTR_PORT_TO]) { | ||
446 | ret = adtfn(set, &data, timeout); | ||
447 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
448 | } | ||
449 | |||
450 | port = ntohs(data.port); | ||
451 | port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); | ||
452 | if (port > port_to) | ||
453 | swap(port, port_to); | ||
454 | |||
455 | for (; port <= port_to; port++) { | ||
456 | data.port = htons(port); | ||
457 | ret = adtfn(set, &data, timeout); | ||
458 | |||
459 | if (ret && !ip_set_eexist(ret, flags)) | ||
460 | return ret; | ||
461 | else | ||
462 | ret = 0; | ||
463 | } | ||
464 | return ret; | ||
465 | } | ||
466 | |||
467 | /* Create hash:ip type of sets */ | ||
468 | |||
469 | static int | ||
470 | hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
471 | { | ||
472 | struct ip_set_hash *h; | ||
473 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
474 | u8 hbits; | ||
475 | |||
476 | if (!(set->family == AF_INET || set->family == AF_INET6)) | ||
477 | return -IPSET_ERR_INVALID_FAMILY; | ||
478 | |||
479 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
480 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
481 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
482 | return -IPSET_ERR_PROTOCOL; | ||
483 | |||
484 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
485 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
486 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
487 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
488 | } | ||
489 | |||
490 | if (tb[IPSET_ATTR_MAXELEM]) | ||
491 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
492 | |||
493 | h = kzalloc(sizeof(*h) | ||
494 | + sizeof(struct ip_set_hash_nets) | ||
495 | * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); | ||
496 | if (!h) | ||
497 | return -ENOMEM; | ||
498 | |||
499 | h->maxelem = maxelem; | ||
500 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
501 | h->timeout = IPSET_NO_TIMEOUT; | ||
502 | |||
503 | hbits = htable_bits(hashsize); | ||
504 | h->table = ip_set_alloc( | ||
505 | sizeof(struct htable) | ||
506 | + jhash_size(hbits) * sizeof(struct hbucket)); | ||
507 | if (!h->table) { | ||
508 | kfree(h); | ||
509 | return -ENOMEM; | ||
510 | } | ||
511 | h->table->htable_bits = hbits; | ||
512 | |||
513 | set->data = h; | ||
514 | |||
515 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
516 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
517 | |||
518 | set->variant = set->family == AF_INET | ||
519 | ? &hash_netport4_tvariant : &hash_netport6_tvariant; | ||
520 | |||
521 | if (set->family == AF_INET) | ||
522 | hash_netport4_gc_init(set); | ||
523 | else | ||
524 | hash_netport6_gc_init(set); | ||
525 | } else { | ||
526 | set->variant = set->family == AF_INET | ||
527 | ? &hash_netport4_variant : &hash_netport6_variant; | ||
528 | } | ||
529 | |||
530 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
531 | set->name, jhash_size(h->table->htable_bits), | ||
532 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static struct ip_set_type hash_netport_type __read_mostly = { | ||
538 | .name = "hash:net,port", | ||
539 | .protocol = IPSET_PROTOCOL, | ||
540 | .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, | ||
541 | .dimension = IPSET_DIM_TWO, | ||
542 | .family = AF_UNSPEC, | ||
543 | .revision = 0, | ||
544 | .create = hash_netport_create, | ||
545 | .create_policy = { | ||
546 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | ||
547 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | ||
548 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | ||
549 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | ||
550 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
551 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
552 | }, | ||
553 | .adt_policy = { | ||
554 | [IPSET_ATTR_IP] = { .type = NLA_NESTED }, | ||
555 | [IPSET_ATTR_PORT] = { .type = NLA_U16 }, | ||
556 | [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, | ||
557 | [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, | ||
558 | [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, | ||
559 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
560 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
561 | }, | ||
562 | .me = THIS_MODULE, | ||
563 | }; | ||
564 | |||
565 | static int __init | ||
566 | hash_netport_init(void) | ||
567 | { | ||
568 | return ip_set_type_register(&hash_netport_type); | ||
569 | } | ||
570 | |||
571 | static void __exit | ||
572 | hash_netport_fini(void) | ||
573 | { | ||
574 | ip_set_type_unregister(&hash_netport_type); | ||
575 | } | ||
576 | |||
577 | module_init(hash_netport_init); | ||
578 | module_exit(hash_netport_fini); | ||
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c new file mode 100644 index 000000000000..a47c32982f06 --- /dev/null +++ b/net/netfilter/ipset/ip_set_list_set.c | |||
@@ -0,0 +1,584 @@ | |||
1 | /* Copyright (C) 2008-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 list:set type */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/errno.h> | ||
14 | |||
15 | #include <linux/netfilter/ipset/ip_set.h> | ||
16 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
17 | #include <linux/netfilter/ipset/ip_set_list.h> | ||
18 | |||
19 | MODULE_LICENSE("GPL"); | ||
20 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
21 | MODULE_DESCRIPTION("list:set type of IP sets"); | ||
22 | MODULE_ALIAS("ip_set_list:set"); | ||
23 | |||
24 | /* Member elements without and with timeout */ | ||
25 | struct set_elem { | ||
26 | ip_set_id_t id; | ||
27 | }; | ||
28 | |||
29 | struct set_telem { | ||
30 | ip_set_id_t id; | ||
31 | unsigned long timeout; | ||
32 | }; | ||
33 | |||
34 | /* Type structure */ | ||
35 | struct list_set { | ||
36 | size_t dsize; /* element size */ | ||
37 | u32 size; /* size of set list array */ | ||
38 | u32 timeout; /* timeout value */ | ||
39 | struct timer_list gc; /* garbage collection */ | ||
40 | struct set_elem members[0]; /* the set members */ | ||
41 | }; | ||
42 | |||
43 | static inline struct set_elem * | ||
44 | list_set_elem(const struct list_set *map, u32 id) | ||
45 | { | ||
46 | return (struct set_elem *)((char *)map->members + id * map->dsize); | ||
47 | } | ||
48 | |||
49 | static inline bool | ||
50 | list_set_timeout(const struct list_set *map, u32 id) | ||
51 | { | ||
52 | const struct set_telem *elem = | ||
53 | (const struct set_telem *) list_set_elem(map, id); | ||
54 | |||
55 | return ip_set_timeout_test(elem->timeout); | ||
56 | } | ||
57 | |||
58 | static inline bool | ||
59 | list_set_expired(const struct list_set *map, u32 id) | ||
60 | { | ||
61 | const struct set_telem *elem = | ||
62 | (const struct set_telem *) list_set_elem(map, id); | ||
63 | |||
64 | return ip_set_timeout_expired(elem->timeout); | ||
65 | } | ||
66 | |||
67 | static inline int | ||
68 | list_set_exist(const struct set_telem *elem) | ||
69 | { | ||
70 | return elem->id != IPSET_INVALID_ID && | ||
71 | !ip_set_timeout_expired(elem->timeout); | ||
72 | } | ||
73 | |||
74 | /* Set list without and with timeout */ | ||
75 | |||
76 | static int | ||
77 | list_set_kadt(struct ip_set *set, const struct sk_buff *skb, | ||
78 | enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | ||
79 | { | ||
80 | struct list_set *map = set->data; | ||
81 | struct set_elem *elem; | ||
82 | u32 i; | ||
83 | int ret; | ||
84 | |||
85 | for (i = 0; i < map->size; i++) { | ||
86 | elem = list_set_elem(map, i); | ||
87 | if (elem->id == IPSET_INVALID_ID) | ||
88 | return 0; | ||
89 | if (with_timeout(map->timeout) && list_set_expired(map, i)) | ||
90 | continue; | ||
91 | switch (adt) { | ||
92 | case IPSET_TEST: | ||
93 | ret = ip_set_test(elem->id, skb, pf, dim, flags); | ||
94 | if (ret > 0) | ||
95 | return ret; | ||
96 | break; | ||
97 | case IPSET_ADD: | ||
98 | ret = ip_set_add(elem->id, skb, pf, dim, flags); | ||
99 | if (ret == 0) | ||
100 | return ret; | ||
101 | break; | ||
102 | case IPSET_DEL: | ||
103 | ret = ip_set_del(elem->id, skb, pf, dim, flags); | ||
104 | if (ret == 0) | ||
105 | return ret; | ||
106 | break; | ||
107 | default: | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | |||
114 | static bool | ||
115 | next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) | ||
116 | { | ||
117 | const struct set_elem *elem; | ||
118 | |||
119 | if (i + 1 < map->size) { | ||
120 | elem = list_set_elem(map, i + 1); | ||
121 | return !!(elem->id == id && | ||
122 | !(with_timeout(map->timeout) && | ||
123 | list_set_expired(map, i + 1))); | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static void | ||
130 | list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) | ||
131 | { | ||
132 | struct set_elem *e; | ||
133 | |||
134 | for (; i < map->size; i++) { | ||
135 | e = list_set_elem(map, i); | ||
136 | swap(e->id, id); | ||
137 | if (e->id == IPSET_INVALID_ID) | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static void | ||
143 | list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, | ||
144 | unsigned long timeout) | ||
145 | { | ||
146 | struct set_telem *e; | ||
147 | |||
148 | for (; i < map->size; i++) { | ||
149 | e = (struct set_telem *)list_set_elem(map, i); | ||
150 | swap(e->id, id); | ||
151 | if (e->id == IPSET_INVALID_ID) | ||
152 | break; | ||
153 | swap(e->timeout, timeout); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | static int | ||
158 | list_set_add(struct list_set *map, u32 i, ip_set_id_t id, | ||
159 | unsigned long timeout) | ||
160 | { | ||
161 | const struct set_elem *e = list_set_elem(map, i); | ||
162 | |||
163 | if (i == map->size - 1 && e->id != IPSET_INVALID_ID) | ||
164 | /* Last element replaced: e.g. add new,before,last */ | ||
165 | ip_set_put_byindex(e->id); | ||
166 | if (with_timeout(map->timeout)) | ||
167 | list_elem_tadd(map, i, id, timeout); | ||
168 | else | ||
169 | list_elem_add(map, i, id); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int | ||
175 | list_set_del(struct list_set *map, ip_set_id_t id, u32 i) | ||
176 | { | ||
177 | struct set_elem *a = list_set_elem(map, i), *b; | ||
178 | |||
179 | ip_set_put_byindex(id); | ||
180 | |||
181 | for (; i < map->size - 1; i++) { | ||
182 | b = list_set_elem(map, i + 1); | ||
183 | a->id = b->id; | ||
184 | if (with_timeout(map->timeout)) | ||
185 | ((struct set_telem *)a)->timeout = | ||
186 | ((struct set_telem *)b)->timeout; | ||
187 | a = b; | ||
188 | if (a->id == IPSET_INVALID_ID) | ||
189 | break; | ||
190 | } | ||
191 | /* Last element */ | ||
192 | a->id = IPSET_INVALID_ID; | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int | ||
197 | list_set_uadt(struct ip_set *set, struct nlattr *tb[], | ||
198 | enum ipset_adt adt, u32 *lineno, u32 flags) | ||
199 | { | ||
200 | struct list_set *map = set->data; | ||
201 | bool with_timeout = with_timeout(map->timeout); | ||
202 | int before = 0; | ||
203 | u32 timeout = map->timeout; | ||
204 | ip_set_id_t id, refid = IPSET_INVALID_ID; | ||
205 | const struct set_elem *elem; | ||
206 | struct ip_set *s; | ||
207 | u32 i; | ||
208 | int ret = 0; | ||
209 | |||
210 | if (unlikely(!tb[IPSET_ATTR_NAME] || | ||
211 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
212 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) | ||
213 | return -IPSET_ERR_PROTOCOL; | ||
214 | |||
215 | if (tb[IPSET_ATTR_LINENO]) | ||
216 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | ||
217 | |||
218 | id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); | ||
219 | if (id == IPSET_INVALID_ID) | ||
220 | return -IPSET_ERR_NAME; | ||
221 | /* "Loop detection" */ | ||
222 | if (s->type->features & IPSET_TYPE_NAME) { | ||
223 | ret = -IPSET_ERR_LOOP; | ||
224 | goto finish; | ||
225 | } | ||
226 | |||
227 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | ||
228 | u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | ||
229 | before = f & IPSET_FLAG_BEFORE; | ||
230 | } | ||
231 | |||
232 | if (before && !tb[IPSET_ATTR_NAMEREF]) { | ||
233 | ret = -IPSET_ERR_BEFORE; | ||
234 | goto finish; | ||
235 | } | ||
236 | |||
237 | if (tb[IPSET_ATTR_NAMEREF]) { | ||
238 | refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), | ||
239 | &s); | ||
240 | if (refid == IPSET_INVALID_ID) { | ||
241 | ret = -IPSET_ERR_NAMEREF; | ||
242 | goto finish; | ||
243 | } | ||
244 | if (!before) | ||
245 | before = -1; | ||
246 | } | ||
247 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
248 | if (!with_timeout) { | ||
249 | ret = -IPSET_ERR_TIMEOUT; | ||
250 | goto finish; | ||
251 | } | ||
252 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
253 | } | ||
254 | |||
255 | switch (adt) { | ||
256 | case IPSET_TEST: | ||
257 | for (i = 0; i < map->size && !ret; i++) { | ||
258 | elem = list_set_elem(map, i); | ||
259 | if (elem->id == IPSET_INVALID_ID || | ||
260 | (before != 0 && i + 1 >= map->size)) | ||
261 | break; | ||
262 | else if (with_timeout && list_set_expired(map, i)) | ||
263 | continue; | ||
264 | else if (before > 0 && elem->id == id) | ||
265 | ret = next_id_eq(map, i, refid); | ||
266 | else if (before < 0 && elem->id == refid) | ||
267 | ret = next_id_eq(map, i, id); | ||
268 | else if (before == 0 && elem->id == id) | ||
269 | ret = 1; | ||
270 | } | ||
271 | break; | ||
272 | case IPSET_ADD: | ||
273 | for (i = 0; i < map->size && !ret; i++) { | ||
274 | elem = list_set_elem(map, i); | ||
275 | if (elem->id == id && | ||
276 | !(with_timeout && list_set_expired(map, i))) | ||
277 | ret = -IPSET_ERR_EXIST; | ||
278 | } | ||
279 | if (ret == -IPSET_ERR_EXIST) | ||
280 | break; | ||
281 | ret = -IPSET_ERR_LIST_FULL; | ||
282 | for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { | ||
283 | elem = list_set_elem(map, i); | ||
284 | if (elem->id == IPSET_INVALID_ID) | ||
285 | ret = before != 0 ? -IPSET_ERR_REF_EXIST | ||
286 | : list_set_add(map, i, id, timeout); | ||
287 | else if (elem->id != refid) | ||
288 | continue; | ||
289 | else if (with_timeout && list_set_expired(map, i)) | ||
290 | ret = -IPSET_ERR_REF_EXIST; | ||
291 | else if (before) | ||
292 | ret = list_set_add(map, i, id, timeout); | ||
293 | else if (i + 1 < map->size) | ||
294 | ret = list_set_add(map, i + 1, id, timeout); | ||
295 | } | ||
296 | break; | ||
297 | case IPSET_DEL: | ||
298 | ret = -IPSET_ERR_EXIST; | ||
299 | for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { | ||
300 | elem = list_set_elem(map, i); | ||
301 | if (elem->id == IPSET_INVALID_ID) { | ||
302 | ret = before != 0 ? -IPSET_ERR_REF_EXIST | ||
303 | : -IPSET_ERR_EXIST; | ||
304 | break; | ||
305 | } else if (with_timeout && list_set_expired(map, i)) | ||
306 | continue; | ||
307 | else if (elem->id == id && | ||
308 | (before == 0 || | ||
309 | (before > 0 && | ||
310 | next_id_eq(map, i, refid)))) | ||
311 | ret = list_set_del(map, id, i); | ||
312 | else if (before < 0 && | ||
313 | elem->id == refid && | ||
314 | next_id_eq(map, i, id)) | ||
315 | ret = list_set_del(map, id, i + 1); | ||
316 | } | ||
317 | break; | ||
318 | default: | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | finish: | ||
323 | if (refid != IPSET_INVALID_ID) | ||
324 | ip_set_put_byindex(refid); | ||
325 | if (adt != IPSET_ADD || ret) | ||
326 | ip_set_put_byindex(id); | ||
327 | |||
328 | return ip_set_eexist(ret, flags) ? 0 : ret; | ||
329 | } | ||
330 | |||
331 | static void | ||
332 | list_set_flush(struct ip_set *set) | ||
333 | { | ||
334 | struct list_set *map = set->data; | ||
335 | struct set_elem *elem; | ||
336 | u32 i; | ||
337 | |||
338 | for (i = 0; i < map->size; i++) { | ||
339 | elem = list_set_elem(map, i); | ||
340 | if (elem->id != IPSET_INVALID_ID) { | ||
341 | ip_set_put_byindex(elem->id); | ||
342 | elem->id = IPSET_INVALID_ID; | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static void | ||
348 | list_set_destroy(struct ip_set *set) | ||
349 | { | ||
350 | struct list_set *map = set->data; | ||
351 | |||
352 | if (with_timeout(map->timeout)) | ||
353 | del_timer_sync(&map->gc); | ||
354 | list_set_flush(set); | ||
355 | kfree(map); | ||
356 | |||
357 | set->data = NULL; | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | list_set_head(struct ip_set *set, struct sk_buff *skb) | ||
362 | { | ||
363 | const struct list_set *map = set->data; | ||
364 | struct nlattr *nested; | ||
365 | |||
366 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
367 | if (!nested) | ||
368 | goto nla_put_failure; | ||
369 | NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); | ||
370 | if (with_timeout(map->timeout)) | ||
371 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | ||
372 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | ||
373 | htonl(atomic_read(&set->ref) - 1)); | ||
374 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | ||
375 | htonl(sizeof(*map) + map->size * map->dsize)); | ||
376 | ipset_nest_end(skb, nested); | ||
377 | |||
378 | return 0; | ||
379 | nla_put_failure: | ||
380 | return -EMSGSIZE; | ||
381 | } | ||
382 | |||
383 | static int | ||
384 | list_set_list(const struct ip_set *set, | ||
385 | struct sk_buff *skb, struct netlink_callback *cb) | ||
386 | { | ||
387 | const struct list_set *map = set->data; | ||
388 | struct nlattr *atd, *nested; | ||
389 | u32 i, first = cb->args[2]; | ||
390 | const struct set_elem *e; | ||
391 | |||
392 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
393 | if (!atd) | ||
394 | return -EMSGSIZE; | ||
395 | for (; cb->args[2] < map->size; cb->args[2]++) { | ||
396 | i = cb->args[2]; | ||
397 | e = list_set_elem(map, i); | ||
398 | if (e->id == IPSET_INVALID_ID) | ||
399 | goto finish; | ||
400 | if (with_timeout(map->timeout) && list_set_expired(map, i)) | ||
401 | continue; | ||
402 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
403 | if (!nested) { | ||
404 | if (i == first) { | ||
405 | nla_nest_cancel(skb, atd); | ||
406 | return -EMSGSIZE; | ||
407 | } else | ||
408 | goto nla_put_failure; | ||
409 | } | ||
410 | NLA_PUT_STRING(skb, IPSET_ATTR_NAME, | ||
411 | ip_set_name_byindex(e->id)); | ||
412 | if (with_timeout(map->timeout)) { | ||
413 | const struct set_telem *te = | ||
414 | (const struct set_telem *) e; | ||
415 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | ||
416 | htonl(ip_set_timeout_get(te->timeout))); | ||
417 | } | ||
418 | ipset_nest_end(skb, nested); | ||
419 | } | ||
420 | finish: | ||
421 | ipset_nest_end(skb, atd); | ||
422 | /* Set listing finished */ | ||
423 | cb->args[2] = 0; | ||
424 | return 0; | ||
425 | |||
426 | nla_put_failure: | ||
427 | nla_nest_cancel(skb, nested); | ||
428 | ipset_nest_end(skb, atd); | ||
429 | if (unlikely(i == first)) { | ||
430 | cb->args[2] = 0; | ||
431 | return -EMSGSIZE; | ||
432 | } | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static bool | ||
437 | list_set_same_set(const struct ip_set *a, const struct ip_set *b) | ||
438 | { | ||
439 | const struct list_set *x = a->data; | ||
440 | const struct list_set *y = b->data; | ||
441 | |||
442 | return x->size == y->size && | ||
443 | x->timeout == y->timeout; | ||
444 | } | ||
445 | |||
446 | static const struct ip_set_type_variant list_set = { | ||
447 | .kadt = list_set_kadt, | ||
448 | .uadt = list_set_uadt, | ||
449 | .destroy = list_set_destroy, | ||
450 | .flush = list_set_flush, | ||
451 | .head = list_set_head, | ||
452 | .list = list_set_list, | ||
453 | .same_set = list_set_same_set, | ||
454 | }; | ||
455 | |||
456 | static void | ||
457 | list_set_gc(unsigned long ul_set) | ||
458 | { | ||
459 | struct ip_set *set = (struct ip_set *) ul_set; | ||
460 | struct list_set *map = set->data; | ||
461 | struct set_telem *e; | ||
462 | u32 i; | ||
463 | |||
464 | /* We run parallel with other readers (test element) | ||
465 | * but adding/deleting new entries is locked out */ | ||
466 | read_lock_bh(&set->lock); | ||
467 | for (i = map->size - 1; i >= 0; i--) { | ||
468 | e = (struct set_telem *) list_set_elem(map, i); | ||
469 | if (e->id != IPSET_INVALID_ID && | ||
470 | list_set_expired(map, i)) | ||
471 | list_set_del(map, e->id, i); | ||
472 | } | ||
473 | read_unlock_bh(&set->lock); | ||
474 | |||
475 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
476 | add_timer(&map->gc); | ||
477 | } | ||
478 | |||
479 | static void | ||
480 | list_set_gc_init(struct ip_set *set) | ||
481 | { | ||
482 | struct list_set *map = set->data; | ||
483 | |||
484 | init_timer(&map->gc); | ||
485 | map->gc.data = (unsigned long) set; | ||
486 | map->gc.function = list_set_gc; | ||
487 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | ||
488 | add_timer(&map->gc); | ||
489 | } | ||
490 | |||
491 | /* Create list:set type of sets */ | ||
492 | |||
493 | static bool | ||
494 | init_list_set(struct ip_set *set, u32 size, size_t dsize, | ||
495 | unsigned long timeout) | ||
496 | { | ||
497 | struct list_set *map; | ||
498 | struct set_elem *e; | ||
499 | u32 i; | ||
500 | |||
501 | map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); | ||
502 | if (!map) | ||
503 | return false; | ||
504 | |||
505 | map->size = size; | ||
506 | map->dsize = dsize; | ||
507 | map->timeout = timeout; | ||
508 | set->data = map; | ||
509 | |||
510 | for (i = 0; i < size; i++) { | ||
511 | e = list_set_elem(map, i); | ||
512 | e->id = IPSET_INVALID_ID; | ||
513 | } | ||
514 | |||
515 | return true; | ||
516 | } | ||
517 | |||
518 | static int | ||
519 | list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
520 | { | ||
521 | u32 size = IP_SET_LIST_DEFAULT_SIZE; | ||
522 | |||
523 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || | ||
524 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | ||
525 | return -IPSET_ERR_PROTOCOL; | ||
526 | |||
527 | if (tb[IPSET_ATTR_SIZE]) | ||
528 | size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]); | ||
529 | if (size < IP_SET_LIST_MIN_SIZE) | ||
530 | size = IP_SET_LIST_MIN_SIZE; | ||
531 | |||
532 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
533 | if (!init_list_set(set, size, sizeof(struct set_telem), | ||
534 | ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) | ||
535 | return -ENOMEM; | ||
536 | |||
537 | list_set_gc_init(set); | ||
538 | } else { | ||
539 | if (!init_list_set(set, size, sizeof(struct set_elem), | ||
540 | IPSET_NO_TIMEOUT)) | ||
541 | return -ENOMEM; | ||
542 | } | ||
543 | set->variant = &list_set; | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static struct ip_set_type list_set_type __read_mostly = { | ||
548 | .name = "list:set", | ||
549 | .protocol = IPSET_PROTOCOL, | ||
550 | .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, | ||
551 | .dimension = IPSET_DIM_ONE, | ||
552 | .family = AF_UNSPEC, | ||
553 | .revision = 0, | ||
554 | .create = list_set_create, | ||
555 | .create_policy = { | ||
556 | [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, | ||
557 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
558 | }, | ||
559 | .adt_policy = { | ||
560 | [IPSET_ATTR_NAME] = { .type = NLA_STRING, | ||
561 | .len = IPSET_MAXNAMELEN }, | ||
562 | [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, | ||
563 | .len = IPSET_MAXNAMELEN }, | ||
564 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
565 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | ||
566 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | ||
567 | }, | ||
568 | .me = THIS_MODULE, | ||
569 | }; | ||
570 | |||
571 | static int __init | ||
572 | list_set_init(void) | ||
573 | { | ||
574 | return ip_set_type_register(&list_set_type); | ||
575 | } | ||
576 | |||
577 | static void __exit | ||
578 | list_set_fini(void) | ||
579 | { | ||
580 | ip_set_type_unregister(&list_set_type); | ||
581 | } | ||
582 | |||
583 | module_init(list_set_init); | ||
584 | module_exit(list_set_fini); | ||
diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c new file mode 100644 index 000000000000..23f8c8162214 --- /dev/null +++ b/net/netfilter/ipset/pfxlen.c | |||
@@ -0,0 +1,291 @@ | |||
1 | #include <linux/netfilter/ipset/pfxlen.h> | ||
2 | |||
3 | /* | ||
4 | * Prefixlen maps for fast conversions, by Jan Engelhardt. | ||
5 | */ | ||
6 | |||
7 | #define E(a, b, c, d) \ | ||
8 | {.ip6 = { \ | ||
9 | __constant_htonl(a), __constant_htonl(b), \ | ||
10 | __constant_htonl(c), __constant_htonl(d), \ | ||
11 | } } | ||
12 | |||
13 | /* | ||
14 | * This table works for both IPv4 and IPv6; | ||
15 | * just use prefixlen_netmask_map[prefixlength].ip. | ||
16 | */ | ||
17 | const union nf_inet_addr ip_set_netmask_map[] = { | ||
18 | E(0x00000000, 0x00000000, 0x00000000, 0x00000000), | ||
19 | E(0x80000000, 0x00000000, 0x00000000, 0x00000000), | ||
20 | E(0xC0000000, 0x00000000, 0x00000000, 0x00000000), | ||
21 | E(0xE0000000, 0x00000000, 0x00000000, 0x00000000), | ||
22 | E(0xF0000000, 0x00000000, 0x00000000, 0x00000000), | ||
23 | E(0xF8000000, 0x00000000, 0x00000000, 0x00000000), | ||
24 | E(0xFC000000, 0x00000000, 0x00000000, 0x00000000), | ||
25 | E(0xFE000000, 0x00000000, 0x00000000, 0x00000000), | ||
26 | E(0xFF000000, 0x00000000, 0x00000000, 0x00000000), | ||
27 | E(0xFF800000, 0x00000000, 0x00000000, 0x00000000), | ||
28 | E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000), | ||
29 | E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000), | ||
30 | E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000), | ||
31 | E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000), | ||
32 | E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000), | ||
33 | E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000), | ||
34 | E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000), | ||
35 | E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000), | ||
36 | E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000), | ||
37 | E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000), | ||
38 | E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000), | ||
39 | E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000), | ||
40 | E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000), | ||
41 | E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000), | ||
42 | E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000), | ||
43 | E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000), | ||
44 | E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000), | ||
45 | E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000), | ||
46 | E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000), | ||
47 | E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000), | ||
48 | E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000), | ||
49 | E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000), | ||
50 | E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), | ||
51 | E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000), | ||
52 | E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000), | ||
53 | E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000), | ||
54 | E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000), | ||
55 | E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000), | ||
56 | E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000), | ||
57 | E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000), | ||
58 | E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000), | ||
59 | E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000), | ||
60 | E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000), | ||
61 | E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000), | ||
62 | E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000), | ||
63 | E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000), | ||
64 | E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000), | ||
65 | E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000), | ||
66 | E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000), | ||
67 | E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000), | ||
68 | E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000), | ||
69 | E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000), | ||
70 | E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000), | ||
71 | E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000), | ||
72 | E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000), | ||
73 | E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000), | ||
74 | E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000), | ||
75 | E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000), | ||
76 | E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000), | ||
77 | E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000), | ||
78 | E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000), | ||
79 | E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000), | ||
80 | E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000), | ||
81 | E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000), | ||
82 | E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000), | ||
83 | E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000), | ||
84 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000), | ||
85 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000), | ||
86 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000), | ||
87 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000), | ||
88 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000), | ||
89 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000), | ||
90 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000), | ||
91 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000), | ||
92 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000), | ||
93 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000), | ||
94 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000), | ||
95 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000), | ||
96 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000), | ||
97 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000), | ||
98 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000), | ||
99 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000), | ||
100 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000), | ||
101 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000), | ||
102 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000), | ||
103 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000), | ||
104 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000), | ||
105 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000), | ||
106 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000), | ||
107 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000), | ||
108 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000), | ||
109 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000), | ||
110 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000), | ||
111 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000), | ||
112 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000), | ||
113 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000), | ||
114 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000), | ||
115 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000), | ||
116 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000), | ||
117 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000), | ||
118 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000), | ||
119 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000), | ||
120 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000), | ||
121 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000), | ||
122 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000), | ||
123 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000), | ||
124 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000), | ||
125 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000), | ||
126 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000), | ||
127 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000), | ||
128 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000), | ||
129 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000), | ||
130 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000), | ||
131 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000), | ||
132 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000), | ||
133 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000), | ||
134 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000), | ||
135 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800), | ||
136 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00), | ||
137 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00), | ||
138 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00), | ||
139 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80), | ||
140 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0), | ||
141 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0), | ||
142 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0), | ||
143 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8), | ||
144 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC), | ||
145 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE), | ||
146 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), | ||
147 | }; | ||
148 | EXPORT_SYMBOL_GPL(ip_set_netmask_map); | ||
149 | |||
150 | #undef E | ||
151 | #define E(a, b, c, d) \ | ||
152 | {.ip6 = { (__force __be32) a, (__force __be32) b, \ | ||
153 | (__force __be32) c, (__force __be32) d, \ | ||
154 | } } | ||
155 | |||
156 | /* | ||
157 | * This table works for both IPv4 and IPv6; | ||
158 | * just use prefixlen_hostmask_map[prefixlength].ip. | ||
159 | */ | ||
160 | const union nf_inet_addr ip_set_hostmask_map[] = { | ||
161 | E(0x00000000, 0x00000000, 0x00000000, 0x00000000), | ||
162 | E(0x80000000, 0x00000000, 0x00000000, 0x00000000), | ||
163 | E(0xC0000000, 0x00000000, 0x00000000, 0x00000000), | ||
164 | E(0xE0000000, 0x00000000, 0x00000000, 0x00000000), | ||
165 | E(0xF0000000, 0x00000000, 0x00000000, 0x00000000), | ||
166 | E(0xF8000000, 0x00000000, 0x00000000, 0x00000000), | ||
167 | E(0xFC000000, 0x00000000, 0x00000000, 0x00000000), | ||
168 | E(0xFE000000, 0x00000000, 0x00000000, 0x00000000), | ||
169 | E(0xFF000000, 0x00000000, 0x00000000, 0x00000000), | ||
170 | E(0xFF800000, 0x00000000, 0x00000000, 0x00000000), | ||
171 | E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000), | ||
172 | E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000), | ||
173 | E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000), | ||
174 | E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000), | ||
175 | E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000), | ||
176 | E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000), | ||
177 | E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000), | ||
178 | E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000), | ||
179 | E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000), | ||
180 | E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000), | ||
181 | E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000), | ||
182 | E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000), | ||
183 | E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000), | ||
184 | E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000), | ||
185 | E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000), | ||
186 | E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000), | ||
187 | E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000), | ||
188 | E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000), | ||
189 | E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000), | ||
190 | E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000), | ||
191 | E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000), | ||
192 | E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000), | ||
193 | E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), | ||
194 | E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000), | ||
195 | E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000), | ||
196 | E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000), | ||
197 | E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000), | ||
198 | E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000), | ||
199 | E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000), | ||
200 | E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000), | ||
201 | E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000), | ||
202 | E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000), | ||
203 | E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000), | ||
204 | E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000), | ||
205 | E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000), | ||
206 | E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000), | ||
207 | E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000), | ||
208 | E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000), | ||
209 | E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000), | ||
210 | E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000), | ||
211 | E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000), | ||
212 | E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000), | ||
213 | E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000), | ||
214 | E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000), | ||
215 | E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000), | ||
216 | E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000), | ||
217 | E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000), | ||
218 | E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000), | ||
219 | E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000), | ||
220 | E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000), | ||
221 | E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000), | ||
222 | E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000), | ||
223 | E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000), | ||
224 | E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000), | ||
225 | E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000), | ||
226 | E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000), | ||
227 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000), | ||
228 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000), | ||
229 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000), | ||
230 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000), | ||
231 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000), | ||
232 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000), | ||
233 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000), | ||
234 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000), | ||
235 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000), | ||
236 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000), | ||
237 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000), | ||
238 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000), | ||
239 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000), | ||
240 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000), | ||
241 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000), | ||
242 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000), | ||
243 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000), | ||
244 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000), | ||
245 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000), | ||
246 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000), | ||
247 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000), | ||
248 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000), | ||
249 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000), | ||
250 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000), | ||
251 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000), | ||
252 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000), | ||
253 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000), | ||
254 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000), | ||
255 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000), | ||
256 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000), | ||
257 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000), | ||
258 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000), | ||
259 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000), | ||
260 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000), | ||
261 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000), | ||
262 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000), | ||
263 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000), | ||
264 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000), | ||
265 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000), | ||
266 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000), | ||
267 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000), | ||
268 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000), | ||
269 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000), | ||
270 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000), | ||
271 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000), | ||
272 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000), | ||
273 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000), | ||
274 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000), | ||
275 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000), | ||
276 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000), | ||
277 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000), | ||
278 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800), | ||
279 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00), | ||
280 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00), | ||
281 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00), | ||
282 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80), | ||
283 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0), | ||
284 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0), | ||
285 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0), | ||
286 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8), | ||
287 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC), | ||
288 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE), | ||
289 | E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), | ||
290 | }; | ||
291 | EXPORT_SYMBOL_GPL(ip_set_hostmask_map); | ||
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d889f4f6be99..4d06617fab6c 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c | |||
@@ -1887,7 +1887,7 @@ static int __net_init __ip_vs_init(struct net *net) | |||
1887 | ipvs->gen = atomic_read(&ipvs_netns_cnt); | 1887 | ipvs->gen = atomic_read(&ipvs_netns_cnt); |
1888 | atomic_inc(&ipvs_netns_cnt); | 1888 | atomic_inc(&ipvs_netns_cnt); |
1889 | net->ipvs = ipvs; | 1889 | net->ipvs = ipvs; |
1890 | printk(KERN_INFO "IPVS: Creating netns size=%lu id=%d\n", | 1890 | printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n", |
1891 | sizeof(struct netns_ipvs), ipvs->gen); | 1891 | sizeof(struct netns_ipvs), ipvs->gen); |
1892 | return 0; | 1892 | return 0; |
1893 | } | 1893 | } |
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 98df59a12453..c73b0c831a2d 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c | |||
@@ -3515,9 +3515,6 @@ int __net_init __ip_vs_control_init(struct net *net) | |||
3515 | } | 3515 | } |
3516 | spin_lock_init(&ipvs->tot_stats->lock); | 3516 | spin_lock_init(&ipvs->tot_stats->lock); |
3517 | 3517 | ||
3518 | for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) | ||
3519 | INIT_LIST_HEAD(&ipvs->rs_table[idx]); | ||
3520 | |||
3521 | proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); | 3518 | proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); |
3522 | proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); | 3519 | proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); |
3523 | proc_net_fops_create(net, "ip_vs_stats_percpu", 0, | 3520 | proc_net_fops_create(net, "ip_vs_stats_percpu", 0, |
@@ -3555,10 +3552,15 @@ int __net_init __ip_vs_control_init(struct net *net) | |||
3555 | tbl[idx++].data = &ipvs->sysctl_nat_icmp_send; | 3552 | tbl[idx++].data = &ipvs->sysctl_nat_icmp_send; |
3556 | 3553 | ||
3557 | 3554 | ||
3555 | #ifdef CONFIG_SYSCTL | ||
3558 | ipvs->sysctl_hdr = register_net_sysctl_table(net, net_vs_ctl_path, | 3556 | ipvs->sysctl_hdr = register_net_sysctl_table(net, net_vs_ctl_path, |
3559 | tbl); | 3557 | tbl); |
3560 | if (ipvs->sysctl_hdr == NULL) | 3558 | if (ipvs->sysctl_hdr == NULL) { |
3561 | goto err_reg; | 3559 | if (!net_eq(net, &init_net)) |
3560 | kfree(tbl); | ||
3561 | goto err_dup; | ||
3562 | } | ||
3563 | #endif | ||
3562 | ip_vs_new_estimator(net, ipvs->tot_stats); | 3564 | ip_vs_new_estimator(net, ipvs->tot_stats); |
3563 | ipvs->sysctl_tbl = tbl; | 3565 | ipvs->sysctl_tbl = tbl; |
3564 | /* Schedule defense work */ | 3566 | /* Schedule defense work */ |
@@ -3566,9 +3568,6 @@ int __net_init __ip_vs_control_init(struct net *net) | |||
3566 | schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD); | 3568 | schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD); |
3567 | return 0; | 3569 | return 0; |
3568 | 3570 | ||
3569 | err_reg: | ||
3570 | if (!net_eq(net, &init_net)) | ||
3571 | kfree(tbl); | ||
3572 | err_dup: | 3571 | err_dup: |
3573 | free_percpu(ipvs->cpustats); | 3572 | free_percpu(ipvs->cpustats); |
3574 | err_alloc: | 3573 | err_alloc: |
@@ -3584,7 +3583,9 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) | |||
3584 | ip_vs_kill_estimator(net, ipvs->tot_stats); | 3583 | ip_vs_kill_estimator(net, ipvs->tot_stats); |
3585 | cancel_delayed_work_sync(&ipvs->defense_work); | 3584 | cancel_delayed_work_sync(&ipvs->defense_work); |
3586 | cancel_work_sync(&ipvs->defense_work.work); | 3585 | cancel_work_sync(&ipvs->defense_work.work); |
3586 | #ifdef CONFIG_SYSCTL | ||
3587 | unregister_net_sysctl_table(ipvs->sysctl_hdr); | 3587 | unregister_net_sysctl_table(ipvs->sysctl_hdr); |
3588 | #endif | ||
3588 | proc_net_remove(net, "ip_vs_stats_percpu"); | 3589 | proc_net_remove(net, "ip_vs_stats_percpu"); |
3589 | proc_net_remove(net, "ip_vs_stats"); | 3590 | proc_net_remove(net, "ip_vs_stats"); |
3590 | proc_net_remove(net, "ip_vs"); | 3591 | proc_net_remove(net, "ip_vs"); |
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index d5bec3371871..00b5ffab3768 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c | |||
@@ -554,33 +554,33 @@ static int __net_init __ip_vs_lblc_init(struct net *net) | |||
554 | sizeof(vs_vars_table), | 554 | sizeof(vs_vars_table), |
555 | GFP_KERNEL); | 555 | GFP_KERNEL); |
556 | if (ipvs->lblc_ctl_table == NULL) | 556 | if (ipvs->lblc_ctl_table == NULL) |
557 | goto err_dup; | 557 | return -ENOMEM; |
558 | } else | 558 | } else |
559 | ipvs->lblc_ctl_table = vs_vars_table; | 559 | ipvs->lblc_ctl_table = vs_vars_table; |
560 | ipvs->sysctl_lblc_expiration = 24*60*60*HZ; | 560 | ipvs->sysctl_lblc_expiration = 24*60*60*HZ; |
561 | ipvs->lblc_ctl_table[0].data = &ipvs->sysctl_lblc_expiration; | 561 | ipvs->lblc_ctl_table[0].data = &ipvs->sysctl_lblc_expiration; |
562 | 562 | ||
563 | #ifdef CONFIG_SYSCTL | ||
563 | ipvs->lblc_ctl_header = | 564 | ipvs->lblc_ctl_header = |
564 | register_net_sysctl_table(net, net_vs_ctl_path, | 565 | register_net_sysctl_table(net, net_vs_ctl_path, |
565 | ipvs->lblc_ctl_table); | 566 | ipvs->lblc_ctl_table); |
566 | if (!ipvs->lblc_ctl_header) | 567 | if (!ipvs->lblc_ctl_header) { |
567 | goto err_reg; | 568 | if (!net_eq(net, &init_net)) |
569 | kfree(ipvs->lblc_ctl_table); | ||
570 | return -ENOMEM; | ||
571 | } | ||
572 | #endif | ||
568 | 573 | ||
569 | return 0; | 574 | return 0; |
570 | |||
571 | err_reg: | ||
572 | if (!net_eq(net, &init_net)) | ||
573 | kfree(ipvs->lblc_ctl_table); | ||
574 | |||
575 | err_dup: | ||
576 | return -ENOMEM; | ||
577 | } | 575 | } |
578 | 576 | ||
579 | static void __net_exit __ip_vs_lblc_exit(struct net *net) | 577 | static void __net_exit __ip_vs_lblc_exit(struct net *net) |
580 | { | 578 | { |
581 | struct netns_ipvs *ipvs = net_ipvs(net); | 579 | struct netns_ipvs *ipvs = net_ipvs(net); |
582 | 580 | ||
581 | #ifdef CONFIG_SYSCTL | ||
583 | unregister_net_sysctl_table(ipvs->lblc_ctl_header); | 582 | unregister_net_sysctl_table(ipvs->lblc_ctl_header); |
583 | #endif | ||
584 | 584 | ||
585 | if (!net_eq(net, &init_net)) | 585 | if (!net_eq(net, &init_net)) |
586 | kfree(ipvs->lblc_ctl_table); | 586 | kfree(ipvs->lblc_ctl_table); |
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 61ae8cfcf0b4..bfa25f1ea9e4 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c | |||
@@ -754,33 +754,33 @@ static int __net_init __ip_vs_lblcr_init(struct net *net) | |||
754 | sizeof(vs_vars_table), | 754 | sizeof(vs_vars_table), |
755 | GFP_KERNEL); | 755 | GFP_KERNEL); |
756 | if (ipvs->lblcr_ctl_table == NULL) | 756 | if (ipvs->lblcr_ctl_table == NULL) |
757 | goto err_dup; | 757 | return -ENOMEM; |
758 | } else | 758 | } else |
759 | ipvs->lblcr_ctl_table = vs_vars_table; | 759 | ipvs->lblcr_ctl_table = vs_vars_table; |
760 | ipvs->sysctl_lblcr_expiration = 24*60*60*HZ; | 760 | ipvs->sysctl_lblcr_expiration = 24*60*60*HZ; |
761 | ipvs->lblcr_ctl_table[0].data = &ipvs->sysctl_lblcr_expiration; | 761 | ipvs->lblcr_ctl_table[0].data = &ipvs->sysctl_lblcr_expiration; |
762 | 762 | ||
763 | #ifdef CONFIG_SYSCTL | ||
763 | ipvs->lblcr_ctl_header = | 764 | ipvs->lblcr_ctl_header = |
764 | register_net_sysctl_table(net, net_vs_ctl_path, | 765 | register_net_sysctl_table(net, net_vs_ctl_path, |
765 | ipvs->lblcr_ctl_table); | 766 | ipvs->lblcr_ctl_table); |
766 | if (!ipvs->lblcr_ctl_header) | 767 | if (!ipvs->lblcr_ctl_header) { |
767 | goto err_reg; | 768 | if (!net_eq(net, &init_net)) |
769 | kfree(ipvs->lblcr_ctl_table); | ||
770 | return -ENOMEM; | ||
771 | } | ||
772 | #endif | ||
768 | 773 | ||
769 | return 0; | 774 | return 0; |
770 | |||
771 | err_reg: | ||
772 | if (!net_eq(net, &init_net)) | ||
773 | kfree(ipvs->lblcr_ctl_table); | ||
774 | |||
775 | err_dup: | ||
776 | return -ENOMEM; | ||
777 | } | 775 | } |
778 | 776 | ||
779 | static void __net_exit __ip_vs_lblcr_exit(struct net *net) | 777 | static void __net_exit __ip_vs_lblcr_exit(struct net *net) |
780 | { | 778 | { |
781 | struct netns_ipvs *ipvs = net_ipvs(net); | 779 | struct netns_ipvs *ipvs = net_ipvs(net); |
782 | 780 | ||
781 | #ifdef CONFIG_SYSCTL | ||
783 | unregister_net_sysctl_table(ipvs->lblcr_ctl_header); | 782 | unregister_net_sysctl_table(ipvs->lblcr_ctl_header); |
783 | #endif | ||
784 | 784 | ||
785 | if (!net_eq(net, &init_net)) | 785 | if (!net_eq(net, &init_net)) |
786 | kfree(ipvs->lblcr_ctl_table); | 786 | kfree(ipvs->lblcr_ctl_table); |
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index d5a6e640ea45..2a2a8363ca16 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c | |||
@@ -1686,7 +1686,7 @@ int __init ip_vs_sync_init(void) | |||
1686 | return register_pernet_subsys(&ipvs_sync_ops); | 1686 | return register_pernet_subsys(&ipvs_sync_ops); |
1687 | } | 1687 | } |
1688 | 1688 | ||
1689 | void __exit ip_vs_sync_cleanup(void) | 1689 | void ip_vs_sync_cleanup(void) |
1690 | { | 1690 | { |
1691 | unregister_pernet_subsys(&ipvs_sync_ops); | 1691 | unregister_pernet_subsys(&ipvs_sync_ops); |
1692 | } | 1692 | } |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 3fec12c570a8..b4df3eff4240 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -803,7 +803,7 @@ static const struct nla_policy tuple_nla_policy[CTA_TUPLE_MAX+1] = { | |||
803 | static int | 803 | static int |
804 | ctnetlink_parse_tuple(const struct nlattr * const cda[], | 804 | ctnetlink_parse_tuple(const struct nlattr * const cda[], |
805 | struct nf_conntrack_tuple *tuple, | 805 | struct nf_conntrack_tuple *tuple, |
806 | enum ctattr_tuple type, u_int8_t l3num) | 806 | enum ctattr_type type, u_int8_t l3num) |
807 | { | 807 | { |
808 | struct nlattr *tb[CTA_TUPLE_MAX+1]; | 808 | struct nlattr *tb[CTA_TUPLE_MAX+1]; |
809 | int err; | 809 | int err; |
diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c new file mode 100644 index 000000000000..d9202cdd25c9 --- /dev/null +++ b/net/netfilter/xt_devgroup.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | ||
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 | #include <linux/module.h> | ||
10 | #include <linux/skbuff.h> | ||
11 | #include <linux/netdevice.h> | ||
12 | |||
13 | #include <linux/netfilter/xt_devgroup.h> | ||
14 | #include <linux/netfilter/x_tables.h> | ||
15 | |||
16 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
17 | MODULE_LICENSE("GPL"); | ||
18 | MODULE_DESCRIPTION("Xtables: Device group match"); | ||
19 | MODULE_ALIAS("ipt_devgroup"); | ||
20 | MODULE_ALIAS("ip6t_devgroup"); | ||
21 | |||
22 | static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par) | ||
23 | { | ||
24 | const struct xt_devgroup_info *info = par->matchinfo; | ||
25 | |||
26 | if (info->flags & XT_DEVGROUP_MATCH_SRC && | ||
27 | (((info->src_group ^ par->in->group) & info->src_mask ? 1 : 0) ^ | ||
28 | ((info->flags & XT_DEVGROUP_INVERT_SRC) ? 1 : 0))) | ||
29 | return false; | ||
30 | |||
31 | if (info->flags & XT_DEVGROUP_MATCH_DST && | ||
32 | (((info->dst_group ^ par->out->group) & info->dst_mask ? 1 : 0) ^ | ||
33 | ((info->flags & XT_DEVGROUP_INVERT_DST) ? 1 : 0))) | ||
34 | return false; | ||
35 | |||
36 | return true; | ||
37 | } | ||
38 | |||
39 | static int devgroup_mt_checkentry(const struct xt_mtchk_param *par) | ||
40 | { | ||
41 | const struct xt_devgroup_info *info = par->matchinfo; | ||
42 | |||
43 | if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC | | ||
44 | XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST)) | ||
45 | return -EINVAL; | ||
46 | |||
47 | if (info->flags & XT_DEVGROUP_MATCH_SRC && | ||
48 | par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) | | ||
49 | (1 << NF_INET_LOCAL_IN) | | ||
50 | (1 << NF_INET_FORWARD))) | ||
51 | return -EINVAL; | ||
52 | |||
53 | if (info->flags & XT_DEVGROUP_MATCH_DST && | ||
54 | par->hook_mask & ~((1 << NF_INET_FORWARD) | | ||
55 | (1 << NF_INET_LOCAL_OUT) | | ||
56 | (1 << NF_INET_POST_ROUTING))) | ||
57 | return -EINVAL; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static struct xt_match devgroup_mt_reg __read_mostly = { | ||
63 | .name = "devgroup", | ||
64 | .match = devgroup_mt, | ||
65 | .checkentry = devgroup_mt_checkentry, | ||
66 | .matchsize = sizeof(struct xt_devgroup_info), | ||
67 | .family = NFPROTO_UNSPEC, | ||
68 | .me = THIS_MODULE | ||
69 | }; | ||
70 | |||
71 | static int __init devgroup_mt_init(void) | ||
72 | { | ||
73 | return xt_register_match(&devgroup_mt_reg); | ||
74 | } | ||
75 | |||
76 | static void __exit devgroup_mt_exit(void) | ||
77 | { | ||
78 | xt_unregister_match(&devgroup_mt_reg); | ||
79 | } | ||
80 | |||
81 | module_init(devgroup_mt_init); | ||
82 | module_exit(devgroup_mt_exit); | ||
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c new file mode 100644 index 000000000000..061d48cec137 --- /dev/null +++ b/net/netfilter/xt_set.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
2 | * Patrick Schaaf <bof@bof.de> | ||
3 | * Martin Josefsson <gandalf@wlug.westbo.se> | ||
4 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | /* Kernel module which implements the set match and SET target | ||
12 | * for netfilter/iptables. */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/version.h> | ||
17 | |||
18 | #include <linux/netfilter/x_tables.h> | ||
19 | #include <linux/netfilter/xt_set.h> | ||
20 | |||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
23 | MODULE_DESCRIPTION("Xtables: IP set match and target module"); | ||
24 | MODULE_ALIAS("xt_SET"); | ||
25 | MODULE_ALIAS("ipt_set"); | ||
26 | MODULE_ALIAS("ip6t_set"); | ||
27 | MODULE_ALIAS("ipt_SET"); | ||
28 | MODULE_ALIAS("ip6t_SET"); | ||
29 | |||
30 | static inline int | ||
31 | match_set(ip_set_id_t index, const struct sk_buff *skb, | ||
32 | u8 pf, u8 dim, u8 flags, int inv) | ||
33 | { | ||
34 | if (ip_set_test(index, skb, pf, dim, flags)) | ||
35 | inv = !inv; | ||
36 | return inv; | ||
37 | } | ||
38 | |||
39 | /* Revision 0 interface: backward compatible with netfilter/iptables */ | ||
40 | |||
41 | static bool | ||
42 | set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) | ||
43 | { | ||
44 | const struct xt_set_info_match_v0 *info = par->matchinfo; | ||
45 | |||
46 | return match_set(info->match_set.index, skb, par->family, | ||
47 | info->match_set.u.compat.dim, | ||
48 | info->match_set.u.compat.flags, | ||
49 | info->match_set.u.compat.flags & IPSET_INV_MATCH); | ||
50 | } | ||
51 | |||
52 | static void | ||
53 | compat_flags(struct xt_set_info_v0 *info) | ||
54 | { | ||
55 | u_int8_t i; | ||
56 | |||
57 | /* Fill out compatibility data according to enum ip_set_kopt */ | ||
58 | info->u.compat.dim = IPSET_DIM_ZERO; | ||
59 | if (info->u.flags[0] & IPSET_MATCH_INV) | ||
60 | info->u.compat.flags |= IPSET_INV_MATCH; | ||
61 | for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { | ||
62 | info->u.compat.dim++; | ||
63 | if (info->u.flags[i] & IPSET_SRC) | ||
64 | info->u.compat.flags |= (1<<info->u.compat.dim); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int | ||
69 | set_match_v0_checkentry(const struct xt_mtchk_param *par) | ||
70 | { | ||
71 | struct xt_set_info_match_v0 *info = par->matchinfo; | ||
72 | ip_set_id_t index; | ||
73 | |||
74 | index = ip_set_nfnl_get_byindex(info->match_set.index); | ||
75 | |||
76 | if (index == IPSET_INVALID_ID) { | ||
77 | pr_warning("Cannot find set indentified by id %u to match\n", | ||
78 | info->match_set.index); | ||
79 | return -ENOENT; | ||
80 | } | ||
81 | if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { | ||
82 | pr_warning("Protocol error: set match dimension " | ||
83 | "is over the limit!\n"); | ||
84 | return -ERANGE; | ||
85 | } | ||
86 | |||
87 | /* Fill out compatibility data */ | ||
88 | compat_flags(&info->match_set); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static void | ||
94 | set_match_v0_destroy(const struct xt_mtdtor_param *par) | ||
95 | { | ||
96 | struct xt_set_info_match_v0 *info = par->matchinfo; | ||
97 | |||
98 | ip_set_nfnl_put(info->match_set.index); | ||
99 | } | ||
100 | |||
101 | static unsigned int | ||
102 | set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) | ||
103 | { | ||
104 | const struct xt_set_info_target_v0 *info = par->targinfo; | ||
105 | |||
106 | if (info->add_set.index != IPSET_INVALID_ID) | ||
107 | ip_set_add(info->add_set.index, skb, par->family, | ||
108 | info->add_set.u.compat.dim, | ||
109 | info->add_set.u.compat.flags); | ||
110 | if (info->del_set.index != IPSET_INVALID_ID) | ||
111 | ip_set_del(info->del_set.index, skb, par->family, | ||
112 | info->del_set.u.compat.dim, | ||
113 | info->del_set.u.compat.flags); | ||
114 | |||
115 | return XT_CONTINUE; | ||
116 | } | ||
117 | |||
118 | static int | ||
119 | set_target_v0_checkentry(const struct xt_tgchk_param *par) | ||
120 | { | ||
121 | struct xt_set_info_target_v0 *info = par->targinfo; | ||
122 | ip_set_id_t index; | ||
123 | |||
124 | if (info->add_set.index != IPSET_INVALID_ID) { | ||
125 | index = ip_set_nfnl_get_byindex(info->add_set.index); | ||
126 | if (index == IPSET_INVALID_ID) { | ||
127 | pr_warning("Cannot find add_set index %u as target\n", | ||
128 | info->add_set.index); | ||
129 | return -ENOENT; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | if (info->del_set.index != IPSET_INVALID_ID) { | ||
134 | index = ip_set_nfnl_get_byindex(info->del_set.index); | ||
135 | if (index == IPSET_INVALID_ID) { | ||
136 | pr_warning("Cannot find del_set index %u as target\n", | ||
137 | info->del_set.index); | ||
138 | return -ENOENT; | ||
139 | } | ||
140 | } | ||
141 | if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || | ||
142 | info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { | ||
143 | pr_warning("Protocol error: SET target dimension " | ||
144 | "is over the limit!\n"); | ||
145 | return -ERANGE; | ||
146 | } | ||
147 | |||
148 | /* Fill out compatibility data */ | ||
149 | compat_flags(&info->add_set); | ||
150 | compat_flags(&info->del_set); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void | ||
156 | set_target_v0_destroy(const struct xt_tgdtor_param *par) | ||
157 | { | ||
158 | const struct xt_set_info_target_v0 *info = par->targinfo; | ||
159 | |||
160 | if (info->add_set.index != IPSET_INVALID_ID) | ||
161 | ip_set_nfnl_put(info->add_set.index); | ||
162 | if (info->del_set.index != IPSET_INVALID_ID) | ||
163 | ip_set_nfnl_put(info->del_set.index); | ||
164 | } | ||
165 | |||
166 | /* Revision 1: current interface to netfilter/iptables */ | ||
167 | |||
168 | static bool | ||
169 | set_match(const struct sk_buff *skb, struct xt_action_param *par) | ||
170 | { | ||
171 | const struct xt_set_info_match *info = par->matchinfo; | ||
172 | |||
173 | return match_set(info->match_set.index, skb, par->family, | ||
174 | info->match_set.dim, | ||
175 | info->match_set.flags, | ||
176 | info->match_set.flags & IPSET_INV_MATCH); | ||
177 | } | ||
178 | |||
179 | static int | ||
180 | set_match_checkentry(const struct xt_mtchk_param *par) | ||
181 | { | ||
182 | struct xt_set_info_match *info = par->matchinfo; | ||
183 | ip_set_id_t index; | ||
184 | |||
185 | index = ip_set_nfnl_get_byindex(info->match_set.index); | ||
186 | |||
187 | if (index == IPSET_INVALID_ID) { | ||
188 | pr_warning("Cannot find set indentified by id %u to match\n", | ||
189 | info->match_set.index); | ||
190 | return -ENOENT; | ||
191 | } | ||
192 | if (info->match_set.dim > IPSET_DIM_MAX) { | ||
193 | pr_warning("Protocol error: set match dimension " | ||
194 | "is over the limit!\n"); | ||
195 | return -ERANGE; | ||
196 | } | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void | ||
202 | set_match_destroy(const struct xt_mtdtor_param *par) | ||
203 | { | ||
204 | struct xt_set_info_match *info = par->matchinfo; | ||
205 | |||
206 | ip_set_nfnl_put(info->match_set.index); | ||
207 | } | ||
208 | |||
209 | static unsigned int | ||
210 | set_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
211 | { | ||
212 | const struct xt_set_info_target *info = par->targinfo; | ||
213 | |||
214 | if (info->add_set.index != IPSET_INVALID_ID) | ||
215 | ip_set_add(info->add_set.index, | ||
216 | skb, par->family, | ||
217 | info->add_set.dim, | ||
218 | info->add_set.flags); | ||
219 | if (info->del_set.index != IPSET_INVALID_ID) | ||
220 | ip_set_del(info->del_set.index, | ||
221 | skb, par->family, | ||
222 | info->add_set.dim, | ||
223 | info->del_set.flags); | ||
224 | |||
225 | return XT_CONTINUE; | ||
226 | } | ||
227 | |||
228 | static int | ||
229 | set_target_checkentry(const struct xt_tgchk_param *par) | ||
230 | { | ||
231 | const struct xt_set_info_target *info = par->targinfo; | ||
232 | ip_set_id_t index; | ||
233 | |||
234 | if (info->add_set.index != IPSET_INVALID_ID) { | ||
235 | index = ip_set_nfnl_get_byindex(info->add_set.index); | ||
236 | if (index == IPSET_INVALID_ID) { | ||
237 | pr_warning("Cannot find add_set index %u as target\n", | ||
238 | info->add_set.index); | ||
239 | return -ENOENT; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if (info->del_set.index != IPSET_INVALID_ID) { | ||
244 | index = ip_set_nfnl_get_byindex(info->del_set.index); | ||
245 | if (index == IPSET_INVALID_ID) { | ||
246 | pr_warning("Cannot find del_set index %u as target\n", | ||
247 | info->del_set.index); | ||
248 | return -ENOENT; | ||
249 | } | ||
250 | } | ||
251 | if (info->add_set.dim > IPSET_DIM_MAX || | ||
252 | info->del_set.flags > IPSET_DIM_MAX) { | ||
253 | pr_warning("Protocol error: SET target dimension " | ||
254 | "is over the limit!\n"); | ||
255 | return -ERANGE; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static void | ||
262 | set_target_destroy(const struct xt_tgdtor_param *par) | ||
263 | { | ||
264 | const struct xt_set_info_target *info = par->targinfo; | ||
265 | |||
266 | if (info->add_set.index != IPSET_INVALID_ID) | ||
267 | ip_set_nfnl_put(info->add_set.index); | ||
268 | if (info->del_set.index != IPSET_INVALID_ID) | ||
269 | ip_set_nfnl_put(info->del_set.index); | ||
270 | } | ||
271 | |||
272 | static struct xt_match set_matches[] __read_mostly = { | ||
273 | { | ||
274 | .name = "set", | ||
275 | .family = NFPROTO_IPV4, | ||
276 | .revision = 0, | ||
277 | .match = set_match_v0, | ||
278 | .matchsize = sizeof(struct xt_set_info_match_v0), | ||
279 | .checkentry = set_match_v0_checkentry, | ||
280 | .destroy = set_match_v0_destroy, | ||
281 | .me = THIS_MODULE | ||
282 | }, | ||
283 | { | ||
284 | .name = "set", | ||
285 | .family = NFPROTO_IPV4, | ||
286 | .revision = 1, | ||
287 | .match = set_match, | ||
288 | .matchsize = sizeof(struct xt_set_info_match), | ||
289 | .checkentry = set_match_checkentry, | ||
290 | .destroy = set_match_destroy, | ||
291 | .me = THIS_MODULE | ||
292 | }, | ||
293 | { | ||
294 | .name = "set", | ||
295 | .family = NFPROTO_IPV6, | ||
296 | .revision = 1, | ||
297 | .match = set_match, | ||
298 | .matchsize = sizeof(struct xt_set_info_match), | ||
299 | .checkentry = set_match_checkentry, | ||
300 | .destroy = set_match_destroy, | ||
301 | .me = THIS_MODULE | ||
302 | }, | ||
303 | }; | ||
304 | |||
305 | static struct xt_target set_targets[] __read_mostly = { | ||
306 | { | ||
307 | .name = "SET", | ||
308 | .revision = 0, | ||
309 | .family = NFPROTO_IPV4, | ||
310 | .target = set_target_v0, | ||
311 | .targetsize = sizeof(struct xt_set_info_target_v0), | ||
312 | .checkentry = set_target_v0_checkentry, | ||
313 | .destroy = set_target_v0_destroy, | ||
314 | .me = THIS_MODULE | ||
315 | }, | ||
316 | { | ||
317 | .name = "SET", | ||
318 | .revision = 1, | ||
319 | .family = NFPROTO_IPV4, | ||
320 | .target = set_target, | ||
321 | .targetsize = sizeof(struct xt_set_info_target), | ||
322 | .checkentry = set_target_checkentry, | ||
323 | .destroy = set_target_destroy, | ||
324 | .me = THIS_MODULE | ||
325 | }, | ||
326 | { | ||
327 | .name = "SET", | ||
328 | .revision = 1, | ||
329 | .family = NFPROTO_IPV6, | ||
330 | .target = set_target, | ||
331 | .targetsize = sizeof(struct xt_set_info_target), | ||
332 | .checkentry = set_target_checkentry, | ||
333 | .destroy = set_target_destroy, | ||
334 | .me = THIS_MODULE | ||
335 | }, | ||
336 | }; | ||
337 | |||
338 | static int __init xt_set_init(void) | ||
339 | { | ||
340 | int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); | ||
341 | |||
342 | if (!ret) { | ||
343 | ret = xt_register_targets(set_targets, | ||
344 | ARRAY_SIZE(set_targets)); | ||
345 | if (ret) | ||
346 | xt_unregister_matches(set_matches, | ||
347 | ARRAY_SIZE(set_matches)); | ||
348 | } | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static void __exit xt_set_fini(void) | ||
353 | { | ||
354 | xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); | ||
355 | xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); | ||
356 | } | ||
357 | |||
358 | module_init(xt_set_init); | ||
359 | module_exit(xt_set_fini); | ||