]>
Commit | Line | Data |
---|---|---|
c6410bf7 | 1 | include/linux/netfilter_ipv4/ip_set.h | 498 +++++ |
2 | include/linux/netfilter_ipv4/ip_set_iphash.h | 29 | |
3 | include/linux/netfilter_ipv4/ip_set_ipmap.h | 56 | |
4 | include/linux/netfilter_ipv4/ip_set_ipporthash.h | 33 | |
5 | include/linux/netfilter_ipv4/ip_set_iptree.h | 39 | |
6 | include/linux/netfilter_ipv4/ip_set_jhash.h | 148 + | |
7 | include/linux/netfilter_ipv4/ip_set_macipmap.h | 38 | |
8 | include/linux/netfilter_ipv4/ip_set_malloc.h | 116 + | |
9 | include/linux/netfilter_ipv4/ip_set_nethash.h | 54 | |
10 | include/linux/netfilter_ipv4/ip_set_portmap.h | 25 | |
11 | include/linux/netfilter_ipv4/ipt_set.h | 21 | |
12 | net/ipv4/netfilter/Kconfig | 109 + | |
13 | net/ipv4/netfilter/Makefile | 12 | |
14 | net/ipv4/netfilter/ip_set.c | 1992 +++++++++++++++++++++++ | |
15 | net/ipv4/netfilter/ip_set_iphash.c | 398 ++++ | |
16 | net/ipv4/netfilter/ip_set_ipmap.c | 327 +++ | |
17 | net/ipv4/netfilter/ip_set_ipporthash.c | 524 ++++++ | |
18 | net/ipv4/netfilter/ip_set_iptree.c | 544 ++++++ | |
19 | net/ipv4/netfilter/ip_set_macipmap.c | 353 ++++ | |
20 | net/ipv4/netfilter/ip_set_nethash.c | 466 +++++ | |
21 | net/ipv4/netfilter/ip_set_portmap.c | 334 +++ | |
22 | net/ipv4/netfilter/ipt_SET.c | 128 + | |
23 | net/ipv4/netfilter/ipt_set.c | 113 + | |
24 | 23 files changed, 6357 insertions(+) | |
25 | ||
26 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set.h linux/include/linux/netfilter_ipv4/ip_set.h | |
27 | --- linux.org/include/linux/netfilter_ipv4/ip_set.h 1970-01-01 01:00:00.000000000 +0100 | |
28 | +++ linux/include/linux/netfilter_ipv4/ip_set.h 2006-05-04 10:26:33.000000000 +0200 | |
29 | @@ -0,0 +1,498 @@ | |
30 | +#ifndef _IP_SET_H | |
31 | +#define _IP_SET_H | |
32 | + | |
33 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
34 | + * Patrick Schaaf <bof@bof.de> | |
35 | + * Martin Josefsson <gandalf@wlug.westbo.se> | |
36 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
37 | + * | |
38 | + * This program is free software; you can redistribute it and/or modify | |
39 | + * it under the terms of the GNU General Public License version 2 as | |
40 | + * published by the Free Software Foundation. | |
41 | + */ | |
42 | + | |
43 | +#if 0 | |
44 | +#define IP_SET_DEBUG | |
45 | +#endif | |
46 | + | |
47 | +/* | |
48 | + * A sockopt of such quality has hardly ever been seen before on the open | |
49 | + * market! This little beauty, hardly ever used: above 64, so it's | |
50 | + * traditionally used for firewalling, not touched (even once!) by the | |
51 | + * 2.0, 2.2 and 2.4 kernels! | |
52 | + * | |
53 | + * Comes with its own certificate of authenticity, valid anywhere in the | |
54 | + * Free world! | |
55 | + * | |
56 | + * Rusty, 19.4.2000 | |
57 | + */ | |
58 | +#define SO_IP_SET 83 | |
59 | + | |
60 | +/* | |
61 | + * Heavily modify by Joakim Axelsson 08.03.2002 | |
62 | + * - Made it more modulebased | |
63 | + * | |
64 | + * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 | |
65 | + * - bindings added | |
66 | + * - in order to "deal with" backward compatibility, renamed to ipset | |
67 | + */ | |
68 | + | |
69 | +/* | |
70 | + * Used so that the kernel module and ipset-binary can match their versions | |
71 | + */ | |
72 | +#define IP_SET_PROTOCOL_VERSION 2 | |
73 | + | |
74 | +#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ | |
75 | + | |
76 | +/* Lets work with our own typedef for representing an IP address. | |
77 | + * We hope to make the code more portable, possibly to IPv6... | |
78 | + * | |
79 | + * The representation works in HOST byte order, because most set types | |
80 | + * will perform arithmetic operations and compare operations. | |
81 | + * | |
82 | + * For now the type is an uint32_t. | |
83 | + * | |
84 | + * Make sure to ONLY use the functions when translating and parsing | |
85 | + * in order to keep the host byte order and make it more portable: | |
86 | + * parse_ip() | |
87 | + * parse_mask() | |
88 | + * parse_ipandmask() | |
89 | + * ip_tostring() | |
90 | + * (Joakim: where are they???) | |
91 | + */ | |
92 | + | |
93 | +typedef uint32_t ip_set_ip_t; | |
94 | + | |
95 | +/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t | |
96 | + * and IP_SET_INVALID_ID if you want to increase the max number of sets. | |
97 | + */ | |
98 | +typedef uint16_t ip_set_id_t; | |
99 | + | |
100 | +#define IP_SET_INVALID_ID 65535 | |
101 | + | |
102 | +/* How deep we follow bindings */ | |
103 | +#define IP_SET_MAX_BINDINGS 6 | |
104 | + | |
105 | +/* | |
106 | + * Option flags for kernel operations (ipt_set_info) | |
107 | + */ | |
108 | +#define IPSET_SRC 0x01 /* Source match/add */ | |
109 | +#define IPSET_DST 0x02 /* Destination match/add */ | |
110 | +#define IPSET_MATCH_INV 0x04 /* Inverse matching */ | |
111 | + | |
112 | +/* | |
113 | + * Set features | |
114 | + */ | |
115 | +#define IPSET_TYPE_IP 0x01 /* IP address type of set */ | |
116 | +#define IPSET_TYPE_PORT 0x02 /* Port type of set */ | |
117 | +#define IPSET_DATA_SINGLE 0x04 /* Single data storage */ | |
118 | +#define IPSET_DATA_DOUBLE 0x08 /* Double data storage */ | |
119 | + | |
120 | +/* Reserved keywords */ | |
121 | +#define IPSET_TOKEN_DEFAULT ":default:" | |
122 | +#define IPSET_TOKEN_ALL ":all:" | |
123 | + | |
124 | +/* SO_IP_SET operation constants, and their request struct types. | |
125 | + * | |
126 | + * Operation ids: | |
127 | + * 0-99: commands with version checking | |
128 | + * 100-199: add/del/test/bind/unbind | |
129 | + * 200-299: list, save, restore | |
130 | + */ | |
131 | + | |
132 | +/* Single shot operations: | |
133 | + * version, create, destroy, flush, rename and swap | |
134 | + * | |
135 | + * Sets are identified by name. | |
136 | + */ | |
137 | + | |
138 | +#define IP_SET_REQ_STD \ | |
139 | + unsigned op; \ | |
140 | + unsigned version; \ | |
141 | + char name[IP_SET_MAXNAMELEN] | |
142 | + | |
143 | +#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ | |
144 | +struct ip_set_req_create { | |
145 | + IP_SET_REQ_STD; | |
146 | + char typename[IP_SET_MAXNAMELEN]; | |
147 | +}; | |
148 | + | |
149 | +#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ | |
150 | +struct ip_set_req_std { | |
151 | + IP_SET_REQ_STD; | |
152 | +}; | |
153 | + | |
154 | +#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ | |
155 | +/* Uses ip_set_req_std */ | |
156 | + | |
157 | +#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ | |
158 | +/* Uses ip_set_req_create */ | |
159 | + | |
160 | +#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ | |
161 | +/* Uses ip_set_req_create */ | |
162 | + | |
163 | +union ip_set_name_index { | |
164 | + char name[IP_SET_MAXNAMELEN]; | |
165 | + ip_set_id_t index; | |
166 | +}; | |
167 | + | |
168 | +#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ | |
169 | +struct ip_set_req_get_set { | |
170 | + unsigned op; | |
171 | + unsigned version; | |
172 | + union ip_set_name_index set; | |
173 | +}; | |
174 | + | |
175 | +#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ | |
176 | +/* Uses ip_set_req_get_set */ | |
177 | + | |
178 | +#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ | |
179 | +struct ip_set_req_version { | |
180 | + unsigned op; | |
181 | + unsigned version; | |
182 | +}; | |
183 | + | |
184 | +/* Double shots operations: | |
185 | + * add, del, test, bind and unbind. | |
186 | + * | |
187 | + * First we query the kernel to get the index and type of the target set, | |
188 | + * then issue the command. Validity of IP is checked in kernel in order | |
189 | + * to minimalize sockopt operations. | |
190 | + */ | |
191 | + | |
192 | +/* Get minimal set data for add/del/test/bind/unbind IP */ | |
193 | +#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ | |
194 | +struct ip_set_req_adt_get { | |
195 | + unsigned op; | |
196 | + unsigned version; | |
197 | + union ip_set_name_index set; | |
198 | + char typename[IP_SET_MAXNAMELEN]; | |
199 | +}; | |
200 | + | |
201 | +#define IP_SET_REQ_BYINDEX \ | |
202 | + unsigned op; \ | |
203 | + ip_set_id_t index; | |
204 | + | |
205 | +struct ip_set_req_adt { | |
206 | + IP_SET_REQ_BYINDEX; | |
207 | +}; | |
208 | + | |
209 | +#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ | |
210 | +/* Uses ip_set_req_adt, with type specific addage */ | |
211 | + | |
212 | +#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ | |
213 | +/* Uses ip_set_req_adt, with type specific addage */ | |
214 | + | |
215 | +#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ | |
216 | +/* Uses ip_set_req_adt, with type specific addage */ | |
217 | + | |
218 | +#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ | |
219 | +/* Uses ip_set_req_bind, with type specific addage */ | |
220 | +struct ip_set_req_bind { | |
221 | + IP_SET_REQ_BYINDEX; | |
222 | + char binding[IP_SET_MAXNAMELEN]; | |
223 | +}; | |
224 | + | |
225 | +#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ | |
226 | +/* Uses ip_set_req_bind, with type speficic addage | |
227 | + * index = 0 means unbinding for all sets */ | |
228 | + | |
229 | +#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ | |
230 | +/* Uses ip_set_req_bind, with type specific addage */ | |
231 | + | |
232 | +/* Multiple shots operations: list, save, restore. | |
233 | + * | |
234 | + * - check kernel version and query the max number of sets | |
235 | + * - get the basic information on all sets | |
236 | + * and size required for the next step | |
237 | + * - get actual set data: header, data, bindings | |
238 | + */ | |
239 | + | |
240 | +/* Get max_sets and the index of a queried set | |
241 | + */ | |
242 | +#define IP_SET_OP_MAX_SETS 0x00000020 | |
243 | +struct ip_set_req_max_sets { | |
244 | + unsigned op; | |
245 | + unsigned version; | |
246 | + ip_set_id_t max_sets; /* max_sets */ | |
247 | + ip_set_id_t sets; /* real number of sets */ | |
248 | + union ip_set_name_index set; /* index of set if name used */ | |
249 | +}; | |
250 | + | |
251 | +/* Get the id and name of the sets plus size for next step */ | |
252 | +#define IP_SET_OP_LIST_SIZE 0x00000201 | |
253 | +#define IP_SET_OP_SAVE_SIZE 0x00000202 | |
254 | +struct ip_set_req_setnames { | |
255 | + unsigned op; | |
256 | + ip_set_id_t index; /* set to list/save */ | |
257 | + size_t size; /* size to get setdata/bindings */ | |
258 | + /* followed by sets number of struct ip_set_name_list */ | |
259 | +}; | |
260 | + | |
261 | +struct ip_set_name_list { | |
262 | + char name[IP_SET_MAXNAMELEN]; | |
263 | + char typename[IP_SET_MAXNAMELEN]; | |
264 | + ip_set_id_t index; | |
265 | + ip_set_id_t id; | |
266 | +}; | |
267 | + | |
268 | +/* The actual list operation */ | |
269 | +#define IP_SET_OP_LIST 0x00000203 | |
270 | +struct ip_set_req_list { | |
271 | + IP_SET_REQ_BYINDEX; | |
272 | + /* sets number of struct ip_set_list in reply */ | |
273 | +}; | |
274 | + | |
275 | +struct ip_set_list { | |
276 | + ip_set_id_t index; | |
277 | + ip_set_id_t binding; | |
278 | + u_int32_t ref; | |
279 | + size_t header_size; /* Set header data of header_size */ | |
280 | + size_t members_size; /* Set members data of members_size */ | |
281 | + size_t bindings_size; /* Set bindings data of bindings_size */ | |
282 | +}; | |
283 | + | |
284 | +struct ip_set_hash_list { | |
285 | + ip_set_ip_t ip; | |
286 | + ip_set_id_t binding; | |
287 | +}; | |
288 | + | |
289 | +/* The save operation */ | |
290 | +#define IP_SET_OP_SAVE 0x00000204 | |
291 | +/* Uses ip_set_req_list, in the reply replaced by | |
292 | + * sets number of struct ip_set_save plus a marker | |
293 | + * ip_set_save followed by ip_set_hash_save structures. | |
294 | + */ | |
295 | +struct ip_set_save { | |
296 | + ip_set_id_t index; | |
297 | + ip_set_id_t binding; | |
298 | + size_t header_size; /* Set header data of header_size */ | |
299 | + size_t members_size; /* Set members data of members_size */ | |
300 | +}; | |
301 | + | |
302 | +/* At restoring, ip == 0 means default binding for the given set: */ | |
303 | +struct ip_set_hash_save { | |
304 | + ip_set_ip_t ip; | |
305 | + ip_set_id_t id; | |
306 | + ip_set_id_t binding; | |
307 | +}; | |
308 | + | |
309 | +/* The restore operation */ | |
310 | +#define IP_SET_OP_RESTORE 0x00000205 | |
311 | +/* Uses ip_set_req_setnames followed by ip_set_restore structures | |
312 | + * plus a marker ip_set_restore, followed by ip_set_hash_save | |
313 | + * structures. | |
314 | + */ | |
315 | +struct ip_set_restore { | |
316 | + char name[IP_SET_MAXNAMELEN]; | |
317 | + char typename[IP_SET_MAXNAMELEN]; | |
318 | + ip_set_id_t index; | |
319 | + size_t header_size; /* Create data of header_size */ | |
320 | + size_t members_size; /* Set members data of members_size */ | |
321 | +}; | |
322 | + | |
323 | +static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) | |
324 | +{ | |
325 | + return 4 * ((((b - a + 8) / 8) + 3) / 4); | |
326 | +} | |
327 | + | |
328 | +#ifdef __KERNEL__ | |
329 | + | |
330 | +#define ip_set_printk(format, args...) \ | |
331 | + do { \ | |
332 | + printk("%s: %s: ", __FILE__, __FUNCTION__); \ | |
333 | + printk(format "\n" , ## args); \ | |
334 | + } while (0) | |
335 | + | |
336 | +#if defined(IP_SET_DEBUG) | |
337 | +#define DP(format, args...) \ | |
338 | + do { \ | |
339 | + printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ | |
340 | + printk(format "\n" , ## args); \ | |
341 | + } while (0) | |
342 | +#define IP_SET_ASSERT(x) \ | |
343 | + do { \ | |
344 | + if (!(x)) \ | |
345 | + printk("IP_SET_ASSERT: %s:%i(%s)\n", \ | |
346 | + __FILE__, __LINE__, __FUNCTION__); \ | |
347 | + } while (0) | |
348 | +#else | |
349 | +#define DP(format, args...) | |
350 | +#define IP_SET_ASSERT(x) | |
351 | +#endif | |
352 | + | |
353 | +struct ip_set; | |
354 | + | |
355 | +/* | |
356 | + * The ip_set_type definition - one per set type, e.g. "ipmap". | |
357 | + * | |
358 | + * Each individual set has a pointer, set->type, going to one | |
359 | + * of these structures. Function pointers inside the structure implement | |
360 | + * the real behaviour of the sets. | |
361 | + * | |
362 | + * If not mentioned differently, the implementation behind the function | |
363 | + * pointers of a set_type, is expected to return 0 if ok, and a negative | |
364 | + * errno (e.g. -EINVAL) on error. | |
365 | + */ | |
366 | +struct ip_set_type { | |
367 | + struct list_head list; /* next in list of set types */ | |
368 | + | |
369 | + /* test for IP in set (kernel: iptables -m set src|dst) | |
370 | + * return 0 if not in set, 1 if in set. | |
371 | + */ | |
372 | + int (*testip_kernel) (struct ip_set *set, | |
373 | + const struct sk_buff * skb, | |
374 | + ip_set_ip_t *ip, | |
375 | + const u_int32_t *flags, | |
376 | + unsigned char index); | |
377 | + | |
378 | + /* test for IP in set (userspace: ipset -T set IP) | |
379 | + * return 0 if not in set, 1 if in set. | |
380 | + */ | |
381 | + int (*testip) (struct ip_set *set, | |
382 | + const void *data, size_t size, | |
383 | + ip_set_ip_t *ip); | |
384 | + | |
385 | + /* | |
386 | + * Size of the data structure passed by when | |
387 | + * adding/deletin/testing an entry. | |
388 | + */ | |
389 | + size_t reqsize; | |
390 | + | |
391 | + /* Add IP into set (userspace: ipset -A set IP) | |
392 | + * Return -EEXIST if the address is already in the set, | |
393 | + * and -ERANGE if the address lies outside the set bounds. | |
394 | + * If the address was not already in the set, 0 is returned. | |
395 | + */ | |
396 | + int (*addip) (struct ip_set *set, | |
397 | + const void *data, size_t size, | |
398 | + ip_set_ip_t *ip); | |
399 | + | |
400 | + /* Add IP into set (kernel: iptables ... -j SET set src|dst) | |
401 | + * Return -EEXIST if the address is already in the set, | |
402 | + * and -ERANGE if the address lies outside the set bounds. | |
403 | + * If the address was not already in the set, 0 is returned. | |
404 | + */ | |
405 | + int (*addip_kernel) (struct ip_set *set, | |
406 | + const struct sk_buff * skb, | |
407 | + ip_set_ip_t *ip, | |
408 | + const u_int32_t *flags, | |
409 | + unsigned char index); | |
410 | + | |
411 | + /* remove IP from set (userspace: ipset -D set --entry x) | |
412 | + * Return -EEXIST if the address is NOT in the set, | |
413 | + * and -ERANGE if the address lies outside the set bounds. | |
414 | + * If the address really was in the set, 0 is returned. | |
415 | + */ | |
416 | + int (*delip) (struct ip_set *set, | |
417 | + const void *data, size_t size, | |
418 | + ip_set_ip_t *ip); | |
419 | + | |
420 | + /* remove IP from set (kernel: iptables ... -j SET --entry x) | |
421 | + * Return -EEXIST if the address is NOT in the set, | |
422 | + * and -ERANGE if the address lies outside the set bounds. | |
423 | + * If the address really was in the set, 0 is returned. | |
424 | + */ | |
425 | + int (*delip_kernel) (struct ip_set *set, | |
426 | + const struct sk_buff * skb, | |
427 | + ip_set_ip_t *ip, | |
428 | + const u_int32_t *flags, | |
429 | + unsigned char index); | |
430 | + | |
431 | + /* new set creation - allocated type specific items | |
432 | + */ | |
433 | + int (*create) (struct ip_set *set, | |
434 | + const void *data, size_t size); | |
435 | + | |
436 | + /* retry the operation after successfully tweaking the set | |
437 | + */ | |
438 | + int (*retry) (struct ip_set *set); | |
439 | + | |
440 | + /* set destruction - free type specific items | |
441 | + * There is no return value. | |
442 | + * Can be called only when child sets are destroyed. | |
443 | + */ | |
444 | + void (*destroy) (struct ip_set *set); | |
445 | + | |
446 | + /* set flushing - reset all bits in the set, or something similar. | |
447 | + * There is no return value. | |
448 | + */ | |
449 | + void (*flush) (struct ip_set *set); | |
450 | + | |
451 | + /* Listing: size needed for header | |
452 | + */ | |
453 | + size_t header_size; | |
454 | + | |
455 | + /* Listing: Get the header | |
456 | + * | |
457 | + * Fill in the information in "data". | |
458 | + * This function is always run after list_header_size() under a | |
459 | + * writelock on the set. Therefor is the length of "data" always | |
460 | + * correct. | |
461 | + */ | |
462 | + void (*list_header) (const struct ip_set *set, | |
463 | + void *data); | |
464 | + | |
465 | + /* Listing: Get the size for the set members | |
466 | + */ | |
467 | + int (*list_members_size) (const struct ip_set *set); | |
468 | + | |
469 | + /* Listing: Get the set members | |
470 | + * | |
471 | + * Fill in the information in "data". | |
472 | + * This function is always run after list_member_size() under a | |
473 | + * writelock on the set. Therefor is the length of "data" always | |
474 | + * correct. | |
475 | + */ | |
476 | + void (*list_members) (const struct ip_set *set, | |
477 | + void *data); | |
478 | + | |
479 | + char typename[IP_SET_MAXNAMELEN]; | |
480 | + unsigned char features; | |
481 | + int protocol_version; | |
482 | + | |
483 | + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ | |
484 | + struct module *me; | |
485 | +}; | |
486 | + | |
487 | +extern int ip_set_register_set_type(struct ip_set_type *set_type); | |
488 | +extern void ip_set_unregister_set_type(struct ip_set_type *set_type); | |
489 | + | |
490 | +/* A generic ipset */ | |
491 | +struct ip_set { | |
492 | + char name[IP_SET_MAXNAMELEN]; /* the name of the set */ | |
493 | + rwlock_t lock; /* lock for concurrency control */ | |
494 | + ip_set_id_t id; /* set id for swapping */ | |
495 | + ip_set_id_t binding; /* default binding for the set */ | |
496 | + atomic_t ref; /* in kernel and in hash references */ | |
497 | + struct ip_set_type *type; /* the set types */ | |
498 | + void *data; /* pooltype specific data */ | |
499 | +}; | |
500 | + | |
501 | +/* Structure to bind set elements to sets */ | |
502 | +struct ip_set_hash { | |
503 | + struct list_head list; /* list of clashing entries in hash */ | |
504 | + ip_set_ip_t ip; /* ip from set */ | |
505 | + ip_set_id_t id; /* set id */ | |
506 | + ip_set_id_t binding; /* set we bind the element to */ | |
507 | +}; | |
508 | + | |
509 | +/* register and unregister set references */ | |
510 | +extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); | |
511 | +extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); | |
512 | +extern void ip_set_put(ip_set_id_t id); | |
513 | + | |
514 | +/* API for iptables set match, and SET target */ | |
515 | +extern void ip_set_addip_kernel(ip_set_id_t id, | |
516 | + const struct sk_buff *skb, | |
517 | + const u_int32_t *flags); | |
518 | +extern void ip_set_delip_kernel(ip_set_id_t id, | |
519 | + const struct sk_buff *skb, | |
520 | + const u_int32_t *flags); | |
521 | +extern int ip_set_testip_kernel(ip_set_id_t id, | |
522 | + const struct sk_buff *skb, | |
523 | + const u_int32_t *flags); | |
524 | + | |
525 | +#endif /* __KERNEL__ */ | |
526 | + | |
527 | +#endif /*_IP_SET_H*/ | |
528 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_iphash.h linux/include/linux/netfilter_ipv4/ip_set_iphash.h | |
529 | --- linux.org/include/linux/netfilter_ipv4/ip_set_iphash.h 1970-01-01 01:00:00.000000000 +0100 | |
530 | +++ linux/include/linux/netfilter_ipv4/ip_set_iphash.h 2006-05-04 10:26:33.000000000 +0200 | |
531 | @@ -0,0 +1,29 @@ | |
532 | +#ifndef __IP_SET_IPHASH_H | |
533 | +#define __IP_SET_IPHASH_H | |
534 | + | |
535 | +#include <linux/netfilter_ipv4/ip_set.h> | |
536 | + | |
537 | +#define SETTYPE_NAME "iphash" | |
538 | +#define MAX_RANGE 0x0000FFFF | |
539 | + | |
540 | +struct ip_set_iphash { | |
541 | + ip_set_ip_t *members; /* the iphash proper */ | |
542 | + uint32_t hashsize; /* hash size */ | |
543 | + uint16_t probes; /* max number of probes */ | |
544 | + uint16_t resize; /* resize factor in percent */ | |
545 | + ip_set_ip_t netmask; /* netmask */ | |
546 | + void *initval[0]; /* initvals for jhash_1word */ | |
547 | +}; | |
548 | + | |
549 | +struct ip_set_req_iphash_create { | |
550 | + uint32_t hashsize; | |
551 | + uint16_t probes; | |
552 | + uint16_t resize; | |
553 | + ip_set_ip_t netmask; | |
554 | +}; | |
555 | + | |
556 | +struct ip_set_req_iphash { | |
557 | + ip_set_ip_t ip; | |
558 | +}; | |
559 | + | |
560 | +#endif /* __IP_SET_IPHASH_H */ | |
561 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_ipmap.h linux/include/linux/netfilter_ipv4/ip_set_ipmap.h | |
562 | --- linux.org/include/linux/netfilter_ipv4/ip_set_ipmap.h 1970-01-01 01:00:00.000000000 +0100 | |
563 | +++ linux/include/linux/netfilter_ipv4/ip_set_ipmap.h 2006-05-04 10:26:33.000000000 +0200 | |
564 | @@ -0,0 +1,56 @@ | |
565 | +#ifndef __IP_SET_IPMAP_H | |
566 | +#define __IP_SET_IPMAP_H | |
567 | + | |
568 | +#include <linux/netfilter_ipv4/ip_set.h> | |
569 | + | |
570 | +#define SETTYPE_NAME "ipmap" | |
571 | +#define MAX_RANGE 0x0000FFFF | |
572 | + | |
573 | +struct ip_set_ipmap { | |
574 | + void *members; /* the ipmap proper */ | |
575 | + ip_set_ip_t first_ip; /* host byte order, included in range */ | |
576 | + ip_set_ip_t last_ip; /* host byte order, included in range */ | |
577 | + ip_set_ip_t netmask; /* subnet netmask */ | |
578 | + ip_set_ip_t sizeid; /* size of set in IPs */ | |
579 | + ip_set_ip_t hosts; /* number of hosts in a subnet */ | |
580 | +}; | |
581 | + | |
582 | +struct ip_set_req_ipmap_create { | |
583 | + ip_set_ip_t from; | |
584 | + ip_set_ip_t to; | |
585 | + ip_set_ip_t netmask; | |
586 | +}; | |
587 | + | |
588 | +struct ip_set_req_ipmap { | |
589 | + ip_set_ip_t ip; | |
590 | +}; | |
591 | + | |
592 | +unsigned int | |
593 | +mask_to_bits(ip_set_ip_t mask) | |
594 | +{ | |
595 | + unsigned int bits = 32; | |
596 | + ip_set_ip_t maskaddr; | |
597 | + | |
598 | + if (mask == 0xFFFFFFFF) | |
599 | + return bits; | |
600 | + | |
601 | + maskaddr = 0xFFFFFFFE; | |
602 | + while (--bits >= 0 && maskaddr != mask) | |
603 | + maskaddr <<= 1; | |
604 | + | |
605 | + return bits; | |
606 | +} | |
607 | + | |
608 | +ip_set_ip_t | |
609 | +range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) | |
610 | +{ | |
611 | + ip_set_ip_t mask = 0xFFFFFFFE; | |
612 | + | |
613 | + *bits = 32; | |
614 | + while (--(*bits) >= 0 && mask && (to & mask) != from) | |
615 | + mask <<= 1; | |
616 | + | |
617 | + return mask; | |
618 | +} | |
619 | + | |
620 | +#endif /* __IP_SET_IPMAP_H */ | |
621 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_ipporthash.h linux/include/linux/netfilter_ipv4/ip_set_ipporthash.h | |
622 | --- linux.org/include/linux/netfilter_ipv4/ip_set_ipporthash.h 1970-01-01 01:00:00.000000000 +0100 | |
623 | +++ linux/include/linux/netfilter_ipv4/ip_set_ipporthash.h 2006-05-04 10:26:33.000000000 +0200 | |
624 | @@ -0,0 +1,33 @@ | |
625 | +#ifndef __IP_SET_IPPORTHASH_H | |
626 | +#define __IP_SET_IPPORTHASH_H | |
627 | + | |
628 | +#include <linux/netfilter_ipv4/ip_set.h> | |
629 | + | |
630 | +#define SETTYPE_NAME "ipporthash" | |
631 | +#define MAX_RANGE 0x0000FFFF | |
632 | +#define INVALID_PORT (MAX_RANGE + 1) | |
633 | + | |
634 | +struct ip_set_ipporthash { | |
635 | + ip_set_ip_t *members; /* the ipporthash proper */ | |
636 | + uint32_t hashsize; /* hash size */ | |
637 | + uint16_t probes; /* max number of probes */ | |
638 | + uint16_t resize; /* resize factor in percent */ | |
639 | + ip_set_ip_t first_ip; /* host byte order, included in range */ | |
640 | + ip_set_ip_t last_ip; /* host byte order, included in range */ | |
641 | + void *initval[0]; /* initvals for jhash_1word */ | |
642 | +}; | |
643 | + | |
644 | +struct ip_set_req_ipporthash_create { | |
645 | + uint32_t hashsize; | |
646 | + uint16_t probes; | |
647 | + uint16_t resize; | |
648 | + ip_set_ip_t from; | |
649 | + ip_set_ip_t to; | |
650 | +}; | |
651 | + | |
652 | +struct ip_set_req_ipporthash { | |
653 | + ip_set_ip_t ip; | |
654 | + ip_set_ip_t port; | |
655 | +}; | |
656 | + | |
657 | +#endif /* __IP_SET_IPPORTHASH_H */ | |
658 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_iptree.h linux/include/linux/netfilter_ipv4/ip_set_iptree.h | |
659 | --- linux.org/include/linux/netfilter_ipv4/ip_set_iptree.h 1970-01-01 01:00:00.000000000 +0100 | |
660 | +++ linux/include/linux/netfilter_ipv4/ip_set_iptree.h 2006-05-04 10:26:33.000000000 +0200 | |
661 | @@ -0,0 +1,39 @@ | |
662 | +#ifndef __IP_SET_IPTREE_H | |
663 | +#define __IP_SET_IPTREE_H | |
664 | + | |
665 | +#include <linux/netfilter_ipv4/ip_set.h> | |
666 | + | |
667 | +#define SETTYPE_NAME "iptree" | |
668 | +#define MAX_RANGE 0x0000FFFF | |
669 | + | |
670 | +struct ip_set_iptreed { | |
671 | + unsigned long expires[256]; /* x.x.x.ADDR */ | |
672 | +}; | |
673 | + | |
674 | +struct ip_set_iptreec { | |
675 | + struct ip_set_iptreed *tree[256]; /* x.x.ADDR.* */ | |
676 | +}; | |
677 | + | |
678 | +struct ip_set_iptreeb { | |
679 | + struct ip_set_iptreec *tree[256]; /* x.ADDR.*.* */ | |
680 | +}; | |
681 | + | |
682 | +struct ip_set_iptree { | |
683 | + unsigned int timeout; | |
684 | + unsigned int gc_interval; | |
685 | +#ifdef __KERNEL__ | |
686 | + struct timer_list gc; | |
687 | + struct ip_set_iptreeb *tree[256]; /* ADDR.*.*.* */ | |
688 | +#endif | |
689 | +}; | |
690 | + | |
691 | +struct ip_set_req_iptree_create { | |
692 | + unsigned int timeout; | |
693 | +}; | |
694 | + | |
695 | +struct ip_set_req_iptree { | |
696 | + ip_set_ip_t ip; | |
697 | + unsigned int timeout; | |
698 | +}; | |
699 | + | |
700 | +#endif /* __IP_SET_IPTREE_H */ | |
701 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_jhash.h linux/include/linux/netfilter_ipv4/ip_set_jhash.h | |
702 | --- linux.org/include/linux/netfilter_ipv4/ip_set_jhash.h 1970-01-01 01:00:00.000000000 +0100 | |
703 | +++ linux/include/linux/netfilter_ipv4/ip_set_jhash.h 2006-05-04 10:26:33.000000000 +0200 | |
704 | @@ -0,0 +1,148 @@ | |
705 | +#ifndef _LINUX_IPSET_JHASH_H | |
706 | +#define _LINUX_IPSET_JHASH_H | |
707 | + | |
708 | +/* This is a copy of linux/jhash.h but the types u32/u8 are changed | |
709 | + * to __u32/__u8 so that the header file can be included into | |
710 | + * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) | |
711 | + */ | |
712 | + | |
713 | +/* jhash.h: Jenkins hash support. | |
714 | + * | |
715 | + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) | |
716 | + * | |
717 | + * http://burtleburtle.net/bob/hash/ | |
718 | + * | |
719 | + * These are the credits from Bob's sources: | |
720 | + * | |
721 | + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. | |
722 | + * hash(), hash2(), hash3, and mix() are externally useful functions. | |
723 | + * Routines to test the hash are included if SELF_TEST is defined. | |
724 | + * You can use this free for any purpose. It has no warranty. | |
725 | + * | |
726 | + * Copyright (C) 2003 David S. Miller (davem@redhat.com) | |
727 | + * | |
728 | + * I've modified Bob's hash to be useful in the Linux kernel, and | |
729 | + * any bugs present are surely my fault. -DaveM | |
730 | + */ | |
731 | + | |
732 | +/* NOTE: Arguments are modified. */ | |
733 | +#define __jhash_mix(a, b, c) \ | |
734 | +{ \ | |
735 | + a -= b; a -= c; a ^= (c>>13); \ | |
736 | + b -= c; b -= a; b ^= (a<<8); \ | |
737 | + c -= a; c -= b; c ^= (b>>13); \ | |
738 | + a -= b; a -= c; a ^= (c>>12); \ | |
739 | + b -= c; b -= a; b ^= (a<<16); \ | |
740 | + c -= a; c -= b; c ^= (b>>5); \ | |
741 | + a -= b; a -= c; a ^= (c>>3); \ | |
742 | + b -= c; b -= a; b ^= (a<<10); \ | |
743 | + c -= a; c -= b; c ^= (b>>15); \ | |
744 | +} | |
745 | + | |
746 | +/* The golden ration: an arbitrary value */ | |
747 | +#define JHASH_GOLDEN_RATIO 0x9e3779b9 | |
748 | + | |
749 | +/* The most generic version, hashes an arbitrary sequence | |
750 | + * of bytes. No alignment or length assumptions are made about | |
751 | + * the input key. | |
752 | + */ | |
753 | +static inline __u32 jhash(void *key, __u32 length, __u32 initval) | |
754 | +{ | |
755 | + __u32 a, b, c, len; | |
756 | + __u8 *k = key; | |
757 | + | |
758 | + len = length; | |
759 | + a = b = JHASH_GOLDEN_RATIO; | |
760 | + c = initval; | |
761 | + | |
762 | + while (len >= 12) { | |
763 | + a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); | |
764 | + b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); | |
765 | + c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); | |
766 | + | |
767 | + __jhash_mix(a,b,c); | |
768 | + | |
769 | + k += 12; | |
770 | + len -= 12; | |
771 | + } | |
772 | + | |
773 | + c += length; | |
774 | + switch (len) { | |
775 | + case 11: c += ((__u32)k[10]<<24); | |
776 | + case 10: c += ((__u32)k[9]<<16); | |
777 | + case 9 : c += ((__u32)k[8]<<8); | |
778 | + case 8 : b += ((__u32)k[7]<<24); | |
779 | + case 7 : b += ((__u32)k[6]<<16); | |
780 | + case 6 : b += ((__u32)k[5]<<8); | |
781 | + case 5 : b += k[4]; | |
782 | + case 4 : a += ((__u32)k[3]<<24); | |
783 | + case 3 : a += ((__u32)k[2]<<16); | |
784 | + case 2 : a += ((__u32)k[1]<<8); | |
785 | + case 1 : a += k[0]; | |
786 | + }; | |
787 | + | |
788 | + __jhash_mix(a,b,c); | |
789 | + | |
790 | + return c; | |
791 | +} | |
792 | + | |
793 | +/* A special optimized version that handles 1 or more of __u32s. | |
794 | + * The length parameter here is the number of __u32s in the key. | |
795 | + */ | |
796 | +static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) | |
797 | +{ | |
798 | + __u32 a, b, c, len; | |
799 | + | |
800 | + a = b = JHASH_GOLDEN_RATIO; | |
801 | + c = initval; | |
802 | + len = length; | |
803 | + | |
804 | + while (len >= 3) { | |
805 | + a += k[0]; | |
806 | + b += k[1]; | |
807 | + c += k[2]; | |
808 | + __jhash_mix(a, b, c); | |
809 | + k += 3; len -= 3; | |
810 | + } | |
811 | + | |
812 | + c += length * 4; | |
813 | + | |
814 | + switch (len) { | |
815 | + case 2 : b += k[1]; | |
816 | + case 1 : a += k[0]; | |
817 | + }; | |
818 | + | |
819 | + __jhash_mix(a,b,c); | |
820 | + | |
821 | + return c; | |
822 | +} | |
823 | + | |
824 | + | |
825 | +/* A special ultra-optimized versions that knows they are hashing exactly | |
826 | + * 3, 2 or 1 word(s). | |
827 | + * | |
828 | + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally | |
829 | + * done at the end is not done here. | |
830 | + */ | |
831 | +static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) | |
832 | +{ | |
833 | + a += JHASH_GOLDEN_RATIO; | |
834 | + b += JHASH_GOLDEN_RATIO; | |
835 | + c += initval; | |
836 | + | |
837 | + __jhash_mix(a, b, c); | |
838 | + | |
839 | + return c; | |
840 | +} | |
841 | + | |
842 | +static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) | |
843 | +{ | |
844 | + return jhash_3words(a, b, 0, initval); | |
845 | +} | |
846 | + | |
847 | +static inline __u32 jhash_1word(__u32 a, __u32 initval) | |
848 | +{ | |
849 | + return jhash_3words(a, 0, 0, initval); | |
850 | +} | |
851 | + | |
852 | +#endif /* _LINUX_IPSET_JHASH_H */ | |
853 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_macipmap.h linux/include/linux/netfilter_ipv4/ip_set_macipmap.h | |
854 | --- linux.org/include/linux/netfilter_ipv4/ip_set_macipmap.h 1970-01-01 01:00:00.000000000 +0100 | |
855 | +++ linux/include/linux/netfilter_ipv4/ip_set_macipmap.h 2006-05-04 10:26:33.000000000 +0200 | |
856 | @@ -0,0 +1,38 @@ | |
857 | +#ifndef __IP_SET_MACIPMAP_H | |
858 | +#define __IP_SET_MACIPMAP_H | |
859 | + | |
860 | +#include <linux/netfilter_ipv4/ip_set.h> | |
861 | + | |
862 | +#define SETTYPE_NAME "macipmap" | |
863 | +#define MAX_RANGE 0x0000FFFF | |
864 | + | |
865 | +/* general flags */ | |
866 | +#define IPSET_MACIP_MATCHUNSET 1 | |
867 | + | |
868 | +/* per ip flags */ | |
869 | +#define IPSET_MACIP_ISSET 1 | |
870 | + | |
871 | +struct ip_set_macipmap { | |
872 | + void *members; /* the macipmap proper */ | |
873 | + ip_set_ip_t first_ip; /* host byte order, included in range */ | |
874 | + ip_set_ip_t last_ip; /* host byte order, included in range */ | |
875 | + u_int32_t flags; | |
876 | +}; | |
877 | + | |
878 | +struct ip_set_req_macipmap_create { | |
879 | + ip_set_ip_t from; | |
880 | + ip_set_ip_t to; | |
881 | + u_int32_t flags; | |
882 | +}; | |
883 | + | |
884 | +struct ip_set_req_macipmap { | |
885 | + ip_set_ip_t ip; | |
886 | + unsigned char ethernet[ETH_ALEN]; | |
887 | +}; | |
888 | + | |
889 | +struct ip_set_macip { | |
890 | + unsigned short flags; | |
891 | + unsigned char ethernet[ETH_ALEN]; | |
892 | +}; | |
893 | + | |
894 | +#endif /* __IP_SET_MACIPMAP_H */ | |
895 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_malloc.h linux/include/linux/netfilter_ipv4/ip_set_malloc.h | |
896 | --- linux.org/include/linux/netfilter_ipv4/ip_set_malloc.h 1970-01-01 01:00:00.000000000 +0100 | |
897 | +++ linux/include/linux/netfilter_ipv4/ip_set_malloc.h 2006-05-04 10:26:33.000000000 +0200 | |
898 | @@ -0,0 +1,116 @@ | |
899 | +#ifndef _IP_SET_MALLOC_H | |
900 | +#define _IP_SET_MALLOC_H | |
901 | + | |
902 | +#ifdef __KERNEL__ | |
903 | + | |
904 | +/* Memory allocation and deallocation */ | |
905 | +static size_t max_malloc_size = 0; | |
906 | + | |
907 | +static inline void init_max_malloc_size(void) | |
908 | +{ | |
909 | +#define CACHE(x) max_malloc_size = x; | |
910 | +#include <linux/kmalloc_sizes.h> | |
911 | +#undef CACHE | |
912 | +} | |
913 | + | |
914 | +static inline void * ip_set_malloc(size_t bytes) | |
915 | +{ | |
916 | + if (bytes > max_malloc_size) | |
917 | + return vmalloc(bytes); | |
918 | + else | |
919 | + return kmalloc(bytes, GFP_KERNEL); | |
920 | +} | |
921 | + | |
922 | +static inline void ip_set_free(void * data, size_t bytes) | |
923 | +{ | |
924 | + if (bytes > max_malloc_size) | |
925 | + vfree(data); | |
926 | + else | |
927 | + kfree(data); | |
928 | +} | |
929 | + | |
930 | +struct harray { | |
931 | + size_t max_elements; | |
932 | + void *arrays[0]; | |
933 | +}; | |
934 | + | |
935 | +static inline void * | |
936 | +harray_malloc(size_t hashsize, size_t typesize, int flags) | |
937 | +{ | |
938 | + struct harray *harray; | |
939 | + size_t max_elements, size, i, j; | |
940 | + | |
941 | + if (!max_malloc_size) | |
942 | + init_max_malloc_size(); | |
943 | + | |
944 | + if (typesize > max_malloc_size) | |
945 | + return NULL; | |
946 | + | |
947 | + max_elements = max_malloc_size/typesize; | |
948 | + size = hashsize/max_elements; | |
949 | + if (hashsize % max_elements) | |
950 | + size++; | |
951 | + | |
952 | + /* Last pointer signals end of arrays */ | |
953 | + harray = kmalloc(sizeof(struct harray) + (size + 1) * sizeof(void *), | |
954 | + flags); | |
955 | + | |
956 | + if (!harray) | |
957 | + return NULL; | |
958 | + | |
959 | + for (i = 0; i < size - 1; i++) { | |
960 | + harray->arrays[i] = kmalloc(max_elements * typesize, flags); | |
961 | + if (!harray->arrays[i]) | |
962 | + goto undo; | |
963 | + memset(harray->arrays[i], 0, max_elements * typesize); | |
964 | + } | |
965 | + harray->arrays[i] = kmalloc((hashsize - i * max_elements) * typesize, | |
966 | + flags); | |
967 | + if (!harray->arrays[i]) | |
968 | + goto undo; | |
969 | + memset(harray->arrays[i], 0, (hashsize - i * max_elements) * typesize); | |
970 | + | |
971 | + harray->max_elements = max_elements; | |
972 | + harray->arrays[size] = NULL; | |
973 | + | |
974 | + return (void *)harray; | |
975 | + | |
976 | + undo: | |
977 | + for (j = 0; j < i; j++) { | |
978 | + kfree(harray->arrays[j]); | |
979 | + } | |
980 | + kfree(harray); | |
981 | + return NULL; | |
982 | +} | |
983 | + | |
984 | +static inline void harray_free(void *h) | |
985 | +{ | |
986 | + struct harray *harray = (struct harray *) h; | |
987 | + size_t i; | |
988 | + | |
989 | + for (i = 0; harray->arrays[i] != NULL; i++) | |
990 | + kfree(harray->arrays[i]); | |
991 | + kfree(harray); | |
992 | +} | |
993 | + | |
994 | +static inline void harray_flush(void *h, size_t hashsize, size_t typesize) | |
995 | +{ | |
996 | + struct harray *harray = (struct harray *) h; | |
997 | + size_t i; | |
998 | + | |
999 | + for (i = 0; harray->arrays[i+1] != NULL; i++) | |
1000 | + memset(harray->arrays[i], 0, harray->max_elements * typesize); | |
1001 | + memset(harray->arrays[i], 0, | |
1002 | + (hashsize - i * harray->max_elements) * typesize); | |
1003 | +} | |
1004 | + | |
1005 | +#define HARRAY_ELEM(h, type, which) \ | |
1006 | +({ \ | |
1007 | + struct harray *__h = (struct harray *)(h); \ | |
1008 | + ((type)((__h)->arrays[(which)/(__h)->max_elements]) \ | |
1009 | + + (which)%(__h)->max_elements); \ | |
1010 | +}) | |
1011 | + | |
1012 | +#endif /* __KERNEL__ */ | |
1013 | + | |
1014 | +#endif /*_IP_SET_MALLOC_H*/ | |
1015 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_nethash.h linux/include/linux/netfilter_ipv4/ip_set_nethash.h | |
1016 | --- linux.org/include/linux/netfilter_ipv4/ip_set_nethash.h 1970-01-01 01:00:00.000000000 +0100 | |
1017 | +++ linux/include/linux/netfilter_ipv4/ip_set_nethash.h 2006-05-04 10:26:33.000000000 +0200 | |
1018 | @@ -0,0 +1,54 @@ | |
1019 | +#ifndef __IP_SET_NETHASH_H | |
1020 | +#define __IP_SET_NETHASH_H | |
1021 | + | |
1022 | +#include <linux/netfilter_ipv4/ip_set.h> | |
1023 | + | |
1024 | +#define SETTYPE_NAME "nethash" | |
1025 | +#define MAX_RANGE 0x0000FFFF | |
1026 | + | |
1027 | +struct ip_set_nethash { | |
1028 | + ip_set_ip_t *members; /* the nethash proper */ | |
1029 | + uint32_t hashsize; /* hash size */ | |
1030 | + uint16_t probes; /* max number of probes */ | |
1031 | + uint16_t resize; /* resize factor in percent */ | |
1032 | + unsigned char cidr[30]; /* CIDR sizes */ | |
1033 | + void *initval[0]; /* initvals for jhash_1word */ | |
1034 | +}; | |
1035 | + | |
1036 | +struct ip_set_req_nethash_create { | |
1037 | + uint32_t hashsize; | |
1038 | + uint16_t probes; | |
1039 | + uint16_t resize; | |
1040 | +}; | |
1041 | + | |
1042 | +struct ip_set_req_nethash { | |
1043 | + ip_set_ip_t ip; | |
1044 | + unsigned char cidr; | |
1045 | +}; | |
1046 | + | |
1047 | +static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; | |
1048 | + | |
1049 | +static inline ip_set_ip_t | |
1050 | +pack(ip_set_ip_t ip, unsigned char cidr) | |
1051 | +{ | |
1052 | + ip_set_ip_t addr, *paddr = &addr; | |
1053 | + unsigned char n, t, *a; | |
1054 | + | |
1055 | + addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); | |
1056 | +#ifdef __KERNEL__ | |
1057 | + DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); | |
1058 | +#endif | |
1059 | + n = cidr / 8; | |
1060 | + t = cidr % 8; | |
1061 | + a = &((unsigned char *)paddr)[n]; | |
1062 | + *a = *a /(1 << (8 - t)) + shifts[t]; | |
1063 | +#ifdef __KERNEL__ | |
1064 | + DP("n: %u, t: %u, a: %u", n, t, *a); | |
1065 | + DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", | |
1066 | + HIPQUAD(ip), cidr, NIPQUAD(addr)); | |
1067 | +#endif | |
1068 | + | |
1069 | + return ntohl(addr); | |
1070 | +} | |
1071 | + | |
1072 | +#endif /* __IP_SET_NETHASH_H */ | |
1073 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ip_set_portmap.h linux/include/linux/netfilter_ipv4/ip_set_portmap.h | |
1074 | --- linux.org/include/linux/netfilter_ipv4/ip_set_portmap.h 1970-01-01 01:00:00.000000000 +0100 | |
1075 | +++ linux/include/linux/netfilter_ipv4/ip_set_portmap.h 2006-05-04 10:26:33.000000000 +0200 | |
1076 | @@ -0,0 +1,25 @@ | |
1077 | +#ifndef __IP_SET_PORTMAP_H | |
1078 | +#define __IP_SET_PORTMAP_H | |
1079 | + | |
1080 | +#include <linux/netfilter_ipv4/ip_set.h> | |
1081 | + | |
1082 | +#define SETTYPE_NAME "portmap" | |
1083 | +#define MAX_RANGE 0x0000FFFF | |
1084 | +#define INVALID_PORT (MAX_RANGE + 1) | |
1085 | + | |
1086 | +struct ip_set_portmap { | |
1087 | + void *members; /* the portmap proper */ | |
1088 | + ip_set_ip_t first_port; /* host byte order, included in range */ | |
1089 | + ip_set_ip_t last_port; /* host byte order, included in range */ | |
1090 | +}; | |
1091 | + | |
1092 | +struct ip_set_req_portmap_create { | |
1093 | + ip_set_ip_t from; | |
1094 | + ip_set_ip_t to; | |
1095 | +}; | |
1096 | + | |
1097 | +struct ip_set_req_portmap { | |
1098 | + ip_set_ip_t port; | |
1099 | +}; | |
1100 | + | |
1101 | +#endif /* __IP_SET_PORTMAP_H */ | |
1102 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ipt_set.h linux/include/linux/netfilter_ipv4/ipt_set.h | |
1103 | --- linux.org/include/linux/netfilter_ipv4/ipt_set.h 1970-01-01 01:00:00.000000000 +0100 | |
1104 | +++ linux/include/linux/netfilter_ipv4/ipt_set.h 2006-05-04 10:26:33.000000000 +0200 | |
1105 | @@ -0,0 +1,21 @@ | |
1106 | +#ifndef _IPT_SET_H | |
1107 | +#define _IPT_SET_H | |
1108 | + | |
1109 | +#include <linux/netfilter_ipv4/ip_set.h> | |
1110 | + | |
1111 | +struct ipt_set_info { | |
1112 | + ip_set_id_t index; | |
1113 | + u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; | |
1114 | +}; | |
1115 | + | |
1116 | +/* match info */ | |
1117 | +struct ipt_set_info_match { | |
1118 | + struct ipt_set_info match_set; | |
1119 | +}; | |
1120 | + | |
1121 | +struct ipt_set_info_target { | |
1122 | + struct ipt_set_info add_set; | |
1123 | + struct ipt_set_info del_set; | |
1124 | +}; | |
1125 | + | |
1126 | +#endif /*_IPT_SET_H*/ | |
1127 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig | |
1128 | --- linux.org/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200 | |
1129 | +++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 10:26:33.000000000 +0200 | |
1130 | @@ -606,5 +606,114 @@ | |
1131 | Allows altering the ARP packet payload: source and destination | |
1132 | hardware and network addresses. | |
1133 | ||
1134 | +config IP_NF_SET | |
1135 | + tristate "IP set support" | |
1136 | + depends on INET && NETFILTER | |
1137 | + help | |
1138 | + This option adds IP set support to the kernel. | |
1139 | + In order to define and use sets, you need the userspace utility | |
1140 | + ipset(8). | |
1141 | + | |
1142 | + To compile it as a module, choose M here. If unsure, say N. | |
1143 | + | |
1144 | +config IP_NF_SET_MAX | |
1145 | + int "Maximum number of IP sets" | |
1146 | + default 256 | |
1147 | + range 2 65534 | |
1148 | + depends on IP_NF_SET | |
1149 | + help | |
1150 | + You can define here default value of the maximum number | |
1151 | + of IP sets for the kernel. | |
1152 | + | |
1153 | + The value can be overriden by the 'max_sets' module | |
1154 | + parameter of the 'ip_set' module. | |
1155 | + | |
1156 | +config IP_NF_SET_HASHSIZE | |
1157 | + int "Hash size for bindings of IP sets" | |
1158 | + default 1024 | |
1159 | + depends on IP_NF_SET | |
1160 | + help | |
1161 | + You can define here default value of the hash size for | |
1162 | + bindings of IP sets. | |
1163 | + | |
1164 | + The value can be overriden by the 'hash_size' module | |
1165 | + parameter of the 'ip_set' module. | |
1166 | + | |
1167 | +config IP_NF_SET_IPMAP | |
1168 | + tristate "ipmap set support" | |
1169 | + depends on IP_NF_SET | |
1170 | + help | |
1171 | + This option adds the ipmap set type support. | |
1172 | + | |
1173 | + To compile it as a module, choose M here. If unsure, say N. | |
1174 | + | |
1175 | +config IP_NF_SET_MACIPMAP | |
1176 | + tristate "macipmap set support" | |
1177 | + depends on IP_NF_SET | |
1178 | + help | |
1179 | + This option adds the macipmap set type support. | |
1180 | + | |
1181 | + To compile it as a module, choose M here. If unsure, say N. | |
1182 | + | |
1183 | +config IP_NF_SET_PORTMAP | |
1184 | + tristate "portmap set support" | |
1185 | + depends on IP_NF_SET | |
1186 | + help | |
1187 | + This option adds the portmap set type support. | |
1188 | + | |
1189 | + To compile it as a module, choose M here. If unsure, say N. | |
1190 | + | |
1191 | +config IP_NF_SET_IPHASH | |
1192 | + tristate "iphash set support" | |
1193 | + depends on IP_NF_SET | |
1194 | + help | |
1195 | + This option adds the iphash set type support. | |
1196 | + | |
1197 | + To compile it as a module, choose M here. If unsure, say N. | |
1198 | + | |
1199 | +config IP_NF_SET_NETHASH | |
1200 | + tristate "nethash set support" | |
1201 | + depends on IP_NF_SET | |
1202 | + help | |
1203 | + This option adds the nethash set type support. | |
1204 | + | |
1205 | + To compile it as a module, choose M here. If unsure, say N. | |
1206 | + | |
1207 | +config IP_NF_SET_IPPORTHASH | |
1208 | + tristate "ipporthash set support" | |
1209 | + depends on IP_NF_SET | |
1210 | + help | |
1211 | + This option adds the ipporthash set type support. | |
1212 | + | |
1213 | + To compile it as a module, choose M here. If unsure, say N. | |
1214 | + | |
1215 | +config IP_NF_SET_IPTREE | |
1216 | + tristate "iptree set support" | |
1217 | + depends on IP_NF_SET | |
1218 | + help | |
1219 | + This option adds the iptree set type support. | |
1220 | + | |
1221 | + To compile it as a module, choose M here. If unsure, say N. | |
1222 | + | |
1223 | +config IP_NF_MATCH_SET | |
1224 | + tristate "set match support" | |
1225 | + depends on IP_NF_SET | |
1226 | + help | |
1227 | + Set matching matches against given IP sets. | |
1228 | + You need the ipset utility to create and set up the sets. | |
1229 | + | |
1230 | + To compile it as a module, choose M here. If unsure, say N. | |
1231 | + | |
1232 | +config IP_NF_TARGET_SET | |
1233 | + tristate "SET target support" | |
1234 | + depends on IP_NF_SET | |
1235 | + help | |
1236 | + The SET target makes possible to add/delete entries | |
1237 | + in IP sets. | |
1238 | + You need the ipset utility to create and set up the sets. | |
1239 | + | |
1240 | + To compile it as a module, choose M here. If unsure, say N. | |
1241 | + | |
1242 | + | |
1243 | endmenu | |
1244 | ||
1245 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile | |
1246 | --- linux.org/net/ipv4/netfilter/Makefile 2006-05-02 23:38:44.000000000 +0200 | |
1247 | +++ linux/net/ipv4/netfilter/Makefile 2006-05-04 10:26:33.000000000 +0200 | |
1248 | @@ -0,0 +0,12 @@ | |
1249 | +obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o | |
1250 | +obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o | |
1251 | + | |
1252 | +# sets | |
1253 | +obj-$(CONFIG_IP_NF_SET) += ip_set.o | |
1254 | +obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o | |
1255 | +obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o | |
1256 | +obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o | |
1257 | +obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o | |
1258 | +obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o | |
1259 | +obj-$(CONFIG_IP_NF_SET_IPPORTHASH) += ip_set_ipporthash.o | |
1260 | +obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o | |
1261 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set.c linux/net/ipv4/netfilter/ip_set.c | |
1262 | --- linux.org/net/ipv4/netfilter/ip_set.c 1970-01-01 01:00:00.000000000 +0100 | |
1263 | +++ linux/net/ipv4/netfilter/ip_set.c 2006-05-04 10:26:33.000000000 +0200 | |
1264 | @@ -0,0 +1,1992 @@ | |
1265 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
1266 | + * Patrick Schaaf <bof@bof.de> | |
1267 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
1268 | + * | |
1269 | + * This program is free software; you can redistribute it and/or modify | |
1270 | + * it under the terms of the GNU General Public License version 2 as | |
1271 | + * published by the Free Software Foundation. | |
1272 | + */ | |
1273 | + | |
1274 | +/* Kernel module for IP set management */ | |
1275 | + | |
1276 | +#include <linux/config.h> | |
1277 | +#include <linux/module.h> | |
1278 | +#include <linux/moduleparam.h> | |
1279 | +#include <linux/kmod.h> | |
1280 | +#include <linux/ip.h> | |
1281 | +#include <linux/skbuff.h> | |
1282 | +#include <linux/random.h> | |
1283 | +#include <linux/jhash.h> | |
1284 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
1285 | +#include <linux/errno.h> | |
1286 | +#include <asm/uaccess.h> | |
1287 | +#include <asm/bitops.h> | |
1288 | +#include <asm/semaphore.h> | |
1289 | +#include <linux/spinlock.h> | |
1290 | +#include <linux/vmalloc.h> | |
1291 | + | |
1292 | +#define ASSERT_READ_LOCK(x) /* dont use that */ | |
1293 | +#define ASSERT_WRITE_LOCK(x) | |
1294 | +#include <linux/netfilter_ipv4/listhelp.h> | |
1295 | +#include <linux/netfilter_ipv4/ip_set.h> | |
1296 | + | |
1297 | +static struct list_head set_type_list; /* all registered sets */ | |
1298 | +static struct ip_set **ip_set_list; /* all individual sets */ | |
1299 | +static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ | |
1300 | +static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ | |
1301 | +static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; | |
1302 | +static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; | |
1303 | +static struct list_head *ip_set_hash; /* hash of bindings */ | |
1304 | +static unsigned int ip_set_hash_random; /* random seed */ | |
1305 | + | |
1306 | +/* | |
1307 | + * Sets are identified either by the index in ip_set_list or by id. | |
1308 | + * The id never changes and is used to find a key in the hash. | |
1309 | + * The index may change by swapping and used at all other places | |
1310 | + * (set/SET netfilter modules, binding value, etc.) | |
1311 | + * | |
1312 | + * Userspace requests are serialized by ip_set_mutex and sets can | |
1313 | + * be deleted only from userspace. Therefore ip_set_list locking | |
1314 | + * must obey the following rules: | |
1315 | + * | |
1316 | + * - kernel requests: read and write locking mandatory | |
1317 | + * - user requests: read locking optional, write locking mandatory | |
1318 | + */ | |
1319 | + | |
1320 | +static inline void | |
1321 | +__ip_set_get(ip_set_id_t index) | |
1322 | +{ | |
1323 | + atomic_inc(&ip_set_list[index]->ref); | |
1324 | +} | |
1325 | + | |
1326 | +static inline void | |
1327 | +__ip_set_put(ip_set_id_t index) | |
1328 | +{ | |
1329 | + atomic_dec(&ip_set_list[index]->ref); | |
1330 | +} | |
1331 | + | |
1332 | +/* | |
1333 | + * Binding routines | |
1334 | + */ | |
1335 | + | |
1336 | +static inline int | |
1337 | +ip_hash_cmp(const struct ip_set_hash *set_hash, | |
1338 | + ip_set_id_t id, ip_set_ip_t ip) | |
1339 | +{ | |
1340 | + return set_hash->id == id && set_hash->ip == ip; | |
1341 | +} | |
1342 | + | |
1343 | +static ip_set_id_t | |
1344 | +ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) | |
1345 | +{ | |
1346 | + u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) | |
1347 | + % ip_set_bindings_hash_size; | |
1348 | + struct ip_set_hash *set_hash; | |
1349 | + | |
1350 | + ASSERT_READ_LOCK(&ip_set_lock); | |
1351 | + IP_SET_ASSERT(ip_set_list[id]); | |
1352 | + DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); | |
1353 | + | |
1354 | + set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, | |
1355 | + struct ip_set_hash *, id, ip); | |
1356 | + | |
1357 | + DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, | |
1358 | + HIPQUAD(ip), | |
1359 | + set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); | |
1360 | + | |
1361 | + return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); | |
1362 | +} | |
1363 | + | |
1364 | +static inline void | |
1365 | +__set_hash_del(struct ip_set_hash *set_hash) | |
1366 | +{ | |
1367 | + ASSERT_WRITE_LOCK(&ip_set_lock); | |
1368 | + IP_SET_ASSERT(ip_set_list[set_hash->binding]); | |
1369 | + | |
1370 | + __ip_set_put(set_hash->binding); | |
1371 | + list_del(&set_hash->list); | |
1372 | + kfree(set_hash); | |
1373 | +} | |
1374 | + | |
1375 | +static int | |
1376 | +ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) | |
1377 | +{ | |
1378 | + u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) | |
1379 | + % ip_set_bindings_hash_size; | |
1380 | + struct ip_set_hash *set_hash; | |
1381 | + | |
1382 | + IP_SET_ASSERT(ip_set_list[id]); | |
1383 | + DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); | |
1384 | + write_lock_bh(&ip_set_lock); | |
1385 | + set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, | |
1386 | + struct ip_set_hash *, id, ip); | |
1387 | + DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, | |
1388 | + HIPQUAD(ip), | |
1389 | + set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); | |
1390 | + | |
1391 | + if (set_hash != NULL) | |
1392 | + __set_hash_del(set_hash); | |
1393 | + write_unlock_bh(&ip_set_lock); | |
1394 | + return 0; | |
1395 | +} | |
1396 | + | |
1397 | +static int | |
1398 | +ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) | |
1399 | +{ | |
1400 | + u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) | |
1401 | + % ip_set_bindings_hash_size; | |
1402 | + struct ip_set_hash *set_hash; | |
1403 | + int ret = 0; | |
1404 | + | |
1405 | + IP_SET_ASSERT(ip_set_list[id]); | |
1406 | + IP_SET_ASSERT(ip_set_list[binding]); | |
1407 | + DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, | |
1408 | + HIPQUAD(ip), ip_set_list[binding]->name); | |
1409 | + write_lock_bh(&ip_set_lock); | |
1410 | + set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, | |
1411 | + struct ip_set_hash *, id, ip); | |
1412 | + if (!set_hash) { | |
1413 | + set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL); | |
1414 | + if (!set_hash) { | |
1415 | + ret = -ENOMEM; | |
1416 | + goto unlock; | |
1417 | + } | |
1418 | + INIT_LIST_HEAD(&set_hash->list); | |
1419 | + set_hash->id = id; | |
1420 | + set_hash->ip = ip; | |
1421 | + list_add(&ip_set_hash[key], &set_hash->list); | |
1422 | + } else { | |
1423 | + IP_SET_ASSERT(ip_set_list[set_hash->binding]); | |
1424 | + DP("overwrite binding: %s", | |
1425 | + ip_set_list[set_hash->binding]->name); | |
1426 | + __ip_set_put(set_hash->binding); | |
1427 | + } | |
1428 | + set_hash->binding = binding; | |
1429 | + __ip_set_get(set_hash->binding); | |
1430 | + unlock: | |
1431 | + write_unlock_bh(&ip_set_lock); | |
1432 | + return ret; | |
1433 | +} | |
1434 | + | |
1435 | +#define FOREACH_HASH_DO(fn, args...) \ | |
1436 | +({ \ | |
1437 | + ip_set_id_t __key; \ | |
1438 | + struct ip_set_hash *__set_hash; \ | |
1439 | + \ | |
1440 | + for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ | |
1441 | + list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ | |
1442 | + fn(__set_hash , ## args); \ | |
1443 | + } \ | |
1444 | +}) | |
1445 | + | |
1446 | +#define FOREACH_HASH_RW_DO(fn, args...) \ | |
1447 | +({ \ | |
1448 | + ip_set_id_t __key; \ | |
1449 | + struct ip_set_hash *__set_hash, *__n; \ | |
1450 | + \ | |
1451 | + ASSERT_WRITE_LOCK(&ip_set_lock); \ | |
1452 | + for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ | |
1453 | + list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ | |
1454 | + fn(__set_hash , ## args); \ | |
1455 | + } \ | |
1456 | +}) | |
1457 | + | |
1458 | +/* Add, del and test set entries from kernel */ | |
1459 | + | |
1460 | +#define follow_bindings(index, set, ip) \ | |
1461 | +((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ | |
1462 | + || (index = (set)->binding) != IP_SET_INVALID_ID) | |
1463 | + | |
1464 | +int | |
1465 | +ip_set_testip_kernel(ip_set_id_t index, | |
1466 | + const struct sk_buff *skb, | |
1467 | + const u_int32_t *flags) | |
1468 | +{ | |
1469 | + struct ip_set *set; | |
1470 | + ip_set_ip_t ip; | |
1471 | + int res; | |
1472 | + unsigned char i = 0; | |
1473 | + | |
1474 | + IP_SET_ASSERT(flags[i]); | |
1475 | + read_lock_bh(&ip_set_lock); | |
1476 | + do { | |
1477 | + set = ip_set_list[index]; | |
1478 | + IP_SET_ASSERT(set); | |
1479 | + DP("set %s, index %u", set->name, index); | |
1480 | + read_lock_bh(&set->lock); | |
1481 | + res = set->type->testip_kernel(set, skb, &ip, flags, i++); | |
1482 | + read_unlock_bh(&set->lock); | |
1483 | + i += !!(set->type->features & IPSET_DATA_DOUBLE); | |
1484 | + } while (res > 0 | |
1485 | + && flags[i] | |
1486 | + && follow_bindings(index, set, ip)); | |
1487 | + read_unlock_bh(&ip_set_lock); | |
1488 | + | |
1489 | + return res; | |
1490 | +} | |
1491 | + | |
1492 | +void | |
1493 | +ip_set_addip_kernel(ip_set_id_t index, | |
1494 | + const struct sk_buff *skb, | |
1495 | + const u_int32_t *flags) | |
1496 | +{ | |
1497 | + struct ip_set *set; | |
1498 | + ip_set_ip_t ip; | |
1499 | + int res; | |
1500 | + unsigned char i = 0; | |
1501 | + | |
1502 | + IP_SET_ASSERT(flags[i]); | |
1503 | + retry: | |
1504 | + read_lock_bh(&ip_set_lock); | |
1505 | + do { | |
1506 | + set = ip_set_list[index]; | |
1507 | + IP_SET_ASSERT(set); | |
1508 | + DP("set %s, index %u", set->name, index); | |
1509 | + write_lock_bh(&set->lock); | |
1510 | + res = set->type->addip_kernel(set, skb, &ip, flags, i++); | |
1511 | + write_unlock_bh(&set->lock); | |
1512 | + i += !!(set->type->features & IPSET_DATA_DOUBLE); | |
1513 | + } while ((res == 0 || res == -EEXIST) | |
1514 | + && flags[i] | |
1515 | + && follow_bindings(index, set, ip)); | |
1516 | + read_unlock_bh(&ip_set_lock); | |
1517 | + | |
1518 | + if (res == -EAGAIN | |
1519 | + && set->type->retry | |
1520 | + && (res = set->type->retry(set)) == 0) | |
1521 | + goto retry; | |
1522 | +} | |
1523 | + | |
1524 | +void | |
1525 | +ip_set_delip_kernel(ip_set_id_t index, | |
1526 | + const struct sk_buff *skb, | |
1527 | + const u_int32_t *flags) | |
1528 | +{ | |
1529 | + struct ip_set *set; | |
1530 | + ip_set_ip_t ip; | |
1531 | + int res; | |
1532 | + unsigned char i = 0; | |
1533 | + | |
1534 | + IP_SET_ASSERT(flags[i]); | |
1535 | + read_lock_bh(&ip_set_lock); | |
1536 | + do { | |
1537 | + set = ip_set_list[index]; | |
1538 | + IP_SET_ASSERT(set); | |
1539 | + DP("set %s, index %u", set->name, index); | |
1540 | + write_lock_bh(&set->lock); | |
1541 | + res = set->type->delip_kernel(set, skb, &ip, flags, i++); | |
1542 | + write_unlock_bh(&set->lock); | |
1543 | + i += !!(set->type->features & IPSET_DATA_DOUBLE); | |
1544 | + } while ((res == 0 || res == -EEXIST) | |
1545 | + && flags[i] | |
1546 | + && follow_bindings(index, set, ip)); | |
1547 | + read_unlock_bh(&ip_set_lock); | |
1548 | +} | |
1549 | + | |
1550 | +/* Register and deregister settype */ | |
1551 | + | |
1552 | +static inline int | |
1553 | +set_type_equal(const struct ip_set_type *set_type, const char *str2) | |
1554 | +{ | |
1555 | + return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1); | |
1556 | +} | |
1557 | + | |
1558 | +static inline struct ip_set_type * | |
1559 | +find_set_type(const char *name) | |
1560 | +{ | |
1561 | + return LIST_FIND(&set_type_list, | |
1562 | + set_type_equal, | |
1563 | + struct ip_set_type *, | |
1564 | + name); | |
1565 | +} | |
1566 | + | |
1567 | +int | |
1568 | +ip_set_register_set_type(struct ip_set_type *set_type) | |
1569 | +{ | |
1570 | + int ret = 0; | |
1571 | + | |
1572 | + if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { | |
1573 | + ip_set_printk("'%s' uses wrong protocol version %u (want %u)", | |
1574 | + set_type->typename, | |
1575 | + set_type->protocol_version, | |
1576 | + IP_SET_PROTOCOL_VERSION); | |
1577 | + return -EINVAL; | |
1578 | + } | |
1579 | + | |
1580 | + write_lock_bh(&ip_set_lock); | |
1581 | + if (find_set_type(set_type->typename)) { | |
1582 | + /* Duplicate! */ | |
1583 | + ip_set_printk("'%s' already registered!", | |
1584 | + set_type->typename); | |
1585 | + ret = -EINVAL; | |
1586 | + goto unlock; | |
1587 | + } | |
1588 | + if (!try_module_get(THIS_MODULE)) { | |
1589 | + ret = -EFAULT; | |
1590 | + goto unlock; | |
1591 | + } | |
1592 | + list_append(&set_type_list, set_type); | |
1593 | + DP("'%s' registered.", set_type->typename); | |
1594 | + unlock: | |
1595 | + write_unlock_bh(&ip_set_lock); | |
1596 | + return ret; | |
1597 | +} | |
1598 | + | |
1599 | +void | |
1600 | +ip_set_unregister_set_type(struct ip_set_type *set_type) | |
1601 | +{ | |
1602 | + write_lock_bh(&ip_set_lock); | |
1603 | + if (!find_set_type(set_type->typename)) { | |
1604 | + ip_set_printk("'%s' not registered?", | |
1605 | + set_type->typename); | |
1606 | + goto unlock; | |
1607 | + } | |
1608 | + LIST_DELETE(&set_type_list, set_type); | |
1609 | + module_put(THIS_MODULE); | |
1610 | + DP("'%s' unregistered.", set_type->typename); | |
1611 | + unlock: | |
1612 | + write_unlock_bh(&ip_set_lock); | |
1613 | + | |
1614 | +} | |
1615 | + | |
1616 | +/* | |
1617 | + * Userspace routines | |
1618 | + */ | |
1619 | + | |
1620 | +/* | |
1621 | + * Find set by name, reference it once. The reference makes sure the | |
1622 | + * thing pointed to, does not go away under our feet. Drop the reference | |
1623 | + * later, using ip_set_put(). | |
1624 | + */ | |
1625 | +ip_set_id_t | |
1626 | +ip_set_get_byname(const char *name) | |
1627 | +{ | |
1628 | + ip_set_id_t i, index = IP_SET_INVALID_ID; | |
1629 | + | |
1630 | + down(&ip_set_app_mutex); | |
1631 | + for (i = 0; i < ip_set_max; i++) { | |
1632 | + if (ip_set_list[i] != NULL | |
1633 | + && strcmp(ip_set_list[i]->name, name) == 0) { | |
1634 | + __ip_set_get(i); | |
1635 | + index = i; | |
1636 | + break; | |
1637 | + } | |
1638 | + } | |
1639 | + up(&ip_set_app_mutex); | |
1640 | + return index; | |
1641 | +} | |
1642 | + | |
1643 | +/* | |
1644 | + * Find set by index, reference it once. The reference makes sure the | |
1645 | + * thing pointed to, does not go away under our feet. Drop the reference | |
1646 | + * later, using ip_set_put(). | |
1647 | + */ | |
1648 | +ip_set_id_t | |
1649 | +ip_set_get_byindex(ip_set_id_t index) | |
1650 | +{ | |
1651 | + down(&ip_set_app_mutex); | |
1652 | + | |
1653 | + if (index >= ip_set_max) | |
1654 | + return IP_SET_INVALID_ID; | |
1655 | + | |
1656 | + if (ip_set_list[index]) | |
1657 | + __ip_set_get(index); | |
1658 | + else | |
1659 | + index = IP_SET_INVALID_ID; | |
1660 | + | |
1661 | + up(&ip_set_app_mutex); | |
1662 | + return index; | |
1663 | +} | |
1664 | + | |
1665 | +/* | |
1666 | + * If the given set pointer points to a valid set, decrement | |
1667 | + * reference count by 1. The caller shall not assume the index | |
1668 | + * to be valid, after calling this function. | |
1669 | + */ | |
1670 | +void ip_set_put(ip_set_id_t index) | |
1671 | +{ | |
1672 | + down(&ip_set_app_mutex); | |
1673 | + if (ip_set_list[index]) | |
1674 | + __ip_set_put(index); | |
1675 | + up(&ip_set_app_mutex); | |
1676 | +} | |
1677 | + | |
1678 | +/* Find a set by name or index */ | |
1679 | +static ip_set_id_t | |
1680 | +ip_set_find_byname(const char *name) | |
1681 | +{ | |
1682 | + ip_set_id_t i, index = IP_SET_INVALID_ID; | |
1683 | + | |
1684 | + for (i = 0; i < ip_set_max; i++) { | |
1685 | + if (ip_set_list[i] != NULL | |
1686 | + && strcmp(ip_set_list[i]->name, name) == 0) { | |
1687 | + index = i; | |
1688 | + break; | |
1689 | + } | |
1690 | + } | |
1691 | + return index; | |
1692 | +} | |
1693 | + | |
1694 | +static ip_set_id_t | |
1695 | +ip_set_find_byindex(ip_set_id_t index) | |
1696 | +{ | |
1697 | + if (index >= ip_set_max || ip_set_list[index] == NULL) | |
1698 | + index = IP_SET_INVALID_ID; | |
1699 | + | |
1700 | + return index; | |
1701 | +} | |
1702 | + | |
1703 | +/* | |
1704 | + * Add, del, test, bind and unbind | |
1705 | + */ | |
1706 | + | |
1707 | +static inline int | |
1708 | +__ip_set_testip(struct ip_set *set, | |
1709 | + const void *data, | |
1710 | + size_t size, | |
1711 | + ip_set_ip_t *ip) | |
1712 | +{ | |
1713 | + int res; | |
1714 | + | |
1715 | + read_lock_bh(&set->lock); | |
1716 | + res = set->type->testip(set, data, size, ip); | |
1717 | + read_unlock_bh(&set->lock); | |
1718 | + | |
1719 | + return res; | |
1720 | +} | |
1721 | + | |
1722 | +static int | |
1723 | +__ip_set_addip(ip_set_id_t index, | |
1724 | + const void *data, | |
1725 | + size_t size) | |
1726 | +{ | |
1727 | + struct ip_set *set = ip_set_list[index]; | |
1728 | + ip_set_ip_t ip; | |
1729 | + int res; | |
1730 | + | |
1731 | + IP_SET_ASSERT(set); | |
1732 | + do { | |
1733 | + write_lock_bh(&set->lock); | |
1734 | + res = set->type->addip(set, data, size, &ip); | |
1735 | + write_unlock_bh(&set->lock); | |
1736 | + } while (res == -EAGAIN | |
1737 | + && set->type->retry | |
1738 | + && (res = set->type->retry(set)) == 0); | |
1739 | + | |
1740 | + return res; | |
1741 | +} | |
1742 | + | |
1743 | +static int | |
1744 | +ip_set_addip(ip_set_id_t index, | |
1745 | + const void *data, | |
1746 | + size_t size) | |
1747 | +{ | |
1748 | + | |
1749 | + return __ip_set_addip(index, | |
1750 | + data + sizeof(struct ip_set_req_adt), | |
1751 | + size - sizeof(struct ip_set_req_adt)); | |
1752 | +} | |
1753 | + | |
1754 | +static int | |
1755 | +ip_set_delip(ip_set_id_t index, | |
1756 | + const void *data, | |
1757 | + size_t size) | |
1758 | +{ | |
1759 | + struct ip_set *set = ip_set_list[index]; | |
1760 | + ip_set_ip_t ip; | |
1761 | + int res; | |
1762 | + | |
1763 | + IP_SET_ASSERT(set); | |
1764 | + write_lock_bh(&set->lock); | |
1765 | + res = set->type->delip(set, | |
1766 | + data + sizeof(struct ip_set_req_adt), | |
1767 | + size - sizeof(struct ip_set_req_adt), | |
1768 | + &ip); | |
1769 | + write_unlock_bh(&set->lock); | |
1770 | + | |
1771 | + return res; | |
1772 | +} | |
1773 | + | |
1774 | +static int | |
1775 | +ip_set_testip(ip_set_id_t index, | |
1776 | + const void *data, | |
1777 | + size_t size) | |
1778 | +{ | |
1779 | + struct ip_set *set = ip_set_list[index]; | |
1780 | + ip_set_ip_t ip; | |
1781 | + int res; | |
1782 | + | |
1783 | + IP_SET_ASSERT(set); | |
1784 | + res = __ip_set_testip(set, | |
1785 | + data + sizeof(struct ip_set_req_adt), | |
1786 | + size - sizeof(struct ip_set_req_adt), | |
1787 | + &ip); | |
1788 | + | |
1789 | + return (res > 0 ? -EEXIST : res); | |
1790 | +} | |
1791 | + | |
1792 | +static int | |
1793 | +ip_set_bindip(ip_set_id_t index, | |
1794 | + const void *data, | |
1795 | + size_t size) | |
1796 | +{ | |
1797 | + struct ip_set *set = ip_set_list[index]; | |
1798 | + struct ip_set_req_bind *req_bind; | |
1799 | + ip_set_id_t binding; | |
1800 | + ip_set_ip_t ip; | |
1801 | + int res; | |
1802 | + | |
1803 | + IP_SET_ASSERT(set); | |
1804 | + if (size < sizeof(struct ip_set_req_bind)) | |
1805 | + return -EINVAL; | |
1806 | + | |
1807 | + req_bind = (struct ip_set_req_bind *) data; | |
1808 | + req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; | |
1809 | + | |
1810 | + if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { | |
1811 | + /* Default binding of a set */ | |
1812 | + char *binding_name; | |
1813 | + | |
1814 | + if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) | |
1815 | + return -EINVAL; | |
1816 | + | |
1817 | + binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); | |
1818 | + binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
1819 | + | |
1820 | + binding = ip_set_find_byname(binding_name); | |
1821 | + if (binding == IP_SET_INVALID_ID) | |
1822 | + return -ENOENT; | |
1823 | + | |
1824 | + write_lock_bh(&ip_set_lock); | |
1825 | + /* Sets as binding values are referenced */ | |
1826 | + if (set->binding != IP_SET_INVALID_ID) | |
1827 | + __ip_set_put(set->binding); | |
1828 | + set->binding = binding; | |
1829 | + __ip_set_get(set->binding); | |
1830 | + write_unlock_bh(&ip_set_lock); | |
1831 | + | |
1832 | + return 0; | |
1833 | + } | |
1834 | + binding = ip_set_find_byname(req_bind->binding); | |
1835 | + if (binding == IP_SET_INVALID_ID) | |
1836 | + return -ENOENT; | |
1837 | + | |
1838 | + res = __ip_set_testip(set, | |
1839 | + data + sizeof(struct ip_set_req_bind), | |
1840 | + size - sizeof(struct ip_set_req_bind), | |
1841 | + &ip); | |
1842 | + DP("set %s, ip: %u.%u.%u.%u, binding %s", | |
1843 | + set->name, HIPQUAD(ip), ip_set_list[binding]->name); | |
1844 | + | |
1845 | + if (res >= 0) | |
1846 | + res = ip_set_hash_add(set->id, ip, binding); | |
1847 | + | |
1848 | + return res; | |
1849 | +} | |
1850 | + | |
1851 | +#define FOREACH_SET_DO(fn, args...) \ | |
1852 | +({ \ | |
1853 | + ip_set_id_t __i; \ | |
1854 | + struct ip_set *__set; \ | |
1855 | + \ | |
1856 | + for (__i = 0; __i < ip_set_max; __i++) { \ | |
1857 | + __set = ip_set_list[__i]; \ | |
1858 | + if (__set != NULL) \ | |
1859 | + fn(__set , ##args); \ | |
1860 | + } \ | |
1861 | +}) | |
1862 | + | |
1863 | +static inline void | |
1864 | +__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) | |
1865 | +{ | |
1866 | + if (set_hash->id == id) | |
1867 | + __set_hash_del(set_hash); | |
1868 | +} | |
1869 | + | |
1870 | +static inline void | |
1871 | +__unbind_default(struct ip_set *set) | |
1872 | +{ | |
1873 | + if (set->binding != IP_SET_INVALID_ID) { | |
1874 | + /* Sets as binding values are referenced */ | |
1875 | + __ip_set_put(set->binding); | |
1876 | + set->binding = IP_SET_INVALID_ID; | |
1877 | + } | |
1878 | +} | |
1879 | + | |
1880 | +static int | |
1881 | +ip_set_unbindip(ip_set_id_t index, | |
1882 | + const void *data, | |
1883 | + size_t size) | |
1884 | +{ | |
1885 | + struct ip_set *set; | |
1886 | + struct ip_set_req_bind *req_bind; | |
1887 | + ip_set_ip_t ip; | |
1888 | + int res; | |
1889 | + | |
1890 | + DP(""); | |
1891 | + if (size < sizeof(struct ip_set_req_bind)) | |
1892 | + return -EINVAL; | |
1893 | + | |
1894 | + req_bind = (struct ip_set_req_bind *) data; | |
1895 | + req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; | |
1896 | + | |
1897 | + DP("%u %s", index, req_bind->binding); | |
1898 | + if (index == IP_SET_INVALID_ID) { | |
1899 | + /* unbind :all: */ | |
1900 | + if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { | |
1901 | + /* Default binding of sets */ | |
1902 | + write_lock_bh(&ip_set_lock); | |
1903 | + FOREACH_SET_DO(__unbind_default); | |
1904 | + write_unlock_bh(&ip_set_lock); | |
1905 | + return 0; | |
1906 | + } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { | |
1907 | + /* Flush all bindings of all sets*/ | |
1908 | + write_lock_bh(&ip_set_lock); | |
1909 | + FOREACH_HASH_RW_DO(__set_hash_del); | |
1910 | + write_unlock_bh(&ip_set_lock); | |
1911 | + return 0; | |
1912 | + } | |
1913 | + DP("unreachable reached!"); | |
1914 | + return -EINVAL; | |
1915 | + } | |
1916 | + | |
1917 | + set = ip_set_list[index]; | |
1918 | + IP_SET_ASSERT(set); | |
1919 | + if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { | |
1920 | + /* Default binding of set */ | |
1921 | + ip_set_id_t binding = ip_set_find_byindex(set->binding); | |
1922 | + | |
1923 | + if (binding == IP_SET_INVALID_ID) | |
1924 | + return -ENOENT; | |
1925 | + | |
1926 | + write_lock_bh(&ip_set_lock); | |
1927 | + /* Sets in hash values are referenced */ | |
1928 | + __ip_set_put(set->binding); | |
1929 | + set->binding = IP_SET_INVALID_ID; | |
1930 | + write_unlock_bh(&ip_set_lock); | |
1931 | + | |
1932 | + return 0; | |
1933 | + } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { | |
1934 | + /* Flush all bindings */ | |
1935 | + | |
1936 | + write_lock_bh(&ip_set_lock); | |
1937 | + FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); | |
1938 | + write_unlock_bh(&ip_set_lock); | |
1939 | + return 0; | |
1940 | + } | |
1941 | + | |
1942 | + res = __ip_set_testip(set, | |
1943 | + data + sizeof(struct ip_set_req_bind), | |
1944 | + size - sizeof(struct ip_set_req_bind), | |
1945 | + &ip); | |
1946 | + | |
1947 | + DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); | |
1948 | + if (res >= 0) | |
1949 | + res = ip_set_hash_del(set->id, ip); | |
1950 | + | |
1951 | + return res; | |
1952 | +} | |
1953 | + | |
1954 | +static int | |
1955 | +ip_set_testbind(ip_set_id_t index, | |
1956 | + const void *data, | |
1957 | + size_t size) | |
1958 | +{ | |
1959 | + struct ip_set *set = ip_set_list[index]; | |
1960 | + struct ip_set_req_bind *req_bind; | |
1961 | + ip_set_id_t binding; | |
1962 | + ip_set_ip_t ip; | |
1963 | + int res; | |
1964 | + | |
1965 | + IP_SET_ASSERT(set); | |
1966 | + if (size < sizeof(struct ip_set_req_bind)) | |
1967 | + return -EINVAL; | |
1968 | + | |
1969 | + req_bind = (struct ip_set_req_bind *) data; | |
1970 | + req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; | |
1971 | + | |
1972 | + if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { | |
1973 | + /* Default binding of set */ | |
1974 | + char *binding_name; | |
1975 | + | |
1976 | + if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) | |
1977 | + return -EINVAL; | |
1978 | + | |
1979 | + binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); | |
1980 | + binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
1981 | + | |
1982 | + binding = ip_set_find_byname(binding_name); | |
1983 | + if (binding == IP_SET_INVALID_ID) | |
1984 | + return -ENOENT; | |
1985 | + | |
1986 | + res = (set->binding == binding) ? -EEXIST : 0; | |
1987 | + | |
1988 | + return res; | |
1989 | + } | |
1990 | + binding = ip_set_find_byname(req_bind->binding); | |
1991 | + if (binding == IP_SET_INVALID_ID) | |
1992 | + return -ENOENT; | |
1993 | + | |
1994 | + | |
1995 | + res = __ip_set_testip(set, | |
1996 | + data + sizeof(struct ip_set_req_bind), | |
1997 | + size - sizeof(struct ip_set_req_bind), | |
1998 | + &ip); | |
1999 | + DP("set %s, ip: %u.%u.%u.%u, binding %s", | |
2000 | + set->name, HIPQUAD(ip), ip_set_list[binding]->name); | |
2001 | + | |
2002 | + if (res >= 0) | |
2003 | + res = (ip_set_find_in_hash(set->id, ip) == binding) | |
2004 | + ? -EEXIST : 0; | |
2005 | + | |
2006 | + return res; | |
2007 | +} | |
2008 | + | |
2009 | +static struct ip_set_type * | |
2010 | +find_set_type_rlock(const char *typename) | |
2011 | +{ | |
2012 | + struct ip_set_type *type; | |
2013 | + | |
2014 | + read_lock_bh(&ip_set_lock); | |
2015 | + type = find_set_type(typename); | |
2016 | + if (type == NULL) | |
2017 | + read_unlock_bh(&ip_set_lock); | |
2018 | + | |
2019 | + return type; | |
2020 | +} | |
2021 | + | |
2022 | +static int | |
2023 | +find_free_id(const char *name, | |
2024 | + ip_set_id_t *index, | |
2025 | + ip_set_id_t *id) | |
2026 | +{ | |
2027 | + ip_set_id_t i; | |
2028 | + | |
2029 | + *id = IP_SET_INVALID_ID; | |
2030 | + for (i = 0; i < ip_set_max; i++) { | |
2031 | + if (ip_set_list[i] == NULL) { | |
2032 | + if (*id == IP_SET_INVALID_ID) | |
2033 | + *id = *index = i; | |
2034 | + } else if (strcmp(name, ip_set_list[i]->name) == 0) | |
2035 | + /* Name clash */ | |
2036 | + return -EEXIST; | |
2037 | + } | |
2038 | + if (*id == IP_SET_INVALID_ID) | |
2039 | + /* No free slot remained */ | |
2040 | + return -ERANGE; | |
2041 | + /* Check that index is usable as id (swapping) */ | |
2042 | + check: | |
2043 | + for (i = 0; i < ip_set_max; i++) { | |
2044 | + if (ip_set_list[i] != NULL | |
2045 | + && ip_set_list[i]->id == *id) { | |
2046 | + *id = i; | |
2047 | + goto check; | |
2048 | + } | |
2049 | + } | |
2050 | + return 0; | |
2051 | +} | |
2052 | + | |
2053 | +/* | |
2054 | + * Create a set | |
2055 | + */ | |
2056 | +static int | |
2057 | +ip_set_create(const char *name, | |
2058 | + const char *typename, | |
2059 | + ip_set_id_t restore, | |
2060 | + const void *data, | |
2061 | + size_t size) | |
2062 | +{ | |
2063 | + struct ip_set *set; | |
2064 | + ip_set_id_t index, id; | |
2065 | + int res = 0; | |
2066 | + | |
2067 | + DP("setname: %s, typename: %s, id: %u", name, typename, restore); | |
2068 | + /* | |
2069 | + * First, and without any locks, allocate and initialize | |
2070 | + * a normal base set structure. | |
2071 | + */ | |
2072 | + set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); | |
2073 | + if (!set) | |
2074 | + return -ENOMEM; | |
2075 | + set->lock = RW_LOCK_UNLOCKED; | |
2076 | + strncpy(set->name, name, IP_SET_MAXNAMELEN); | |
2077 | + set->binding = IP_SET_INVALID_ID; | |
2078 | + atomic_set(&set->ref, 0); | |
2079 | + | |
2080 | + /* | |
2081 | + * Next, take the &ip_set_lock, check that we know the type, | |
2082 | + * and take a reference on the type, to make sure it | |
2083 | + * stays available while constructing our new set. | |
2084 | + * | |
2085 | + * After referencing the type, we drop the &ip_set_lock, | |
2086 | + * and let the new set construction run without locks. | |
2087 | + */ | |
2088 | + set->type = find_set_type_rlock(typename); | |
2089 | + if (set->type == NULL) { | |
2090 | + /* Try loading the module */ | |
2091 | + char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; | |
2092 | + strcpy(modulename, "ip_set_"); | |
2093 | + strcat(modulename, typename); | |
2094 | + DP("try to load %s", modulename); | |
2095 | + request_module(modulename); | |
2096 | + set->type = find_set_type_rlock(typename); | |
2097 | + } | |
2098 | + if (set->type == NULL) { | |
2099 | + ip_set_printk("no set type '%s', set '%s' not created", | |
2100 | + typename, name); | |
2101 | + res = -ENOENT; | |
2102 | + goto out; | |
2103 | + } | |
2104 | + if (!try_module_get(set->type->me)) { | |
2105 | + read_unlock_bh(&ip_set_lock); | |
2106 | + res = -EFAULT; | |
2107 | + goto out; | |
2108 | + } | |
2109 | + read_unlock_bh(&ip_set_lock); | |
2110 | + | |
2111 | + /* | |
2112 | + * Without holding any locks, create private part. | |
2113 | + */ | |
2114 | + res = set->type->create(set, data, size); | |
2115 | + if (res != 0) | |
2116 | + goto put_out; | |
2117 | + | |
2118 | + /* BTW, res==0 here. */ | |
2119 | + | |
2120 | + /* | |
2121 | + * Here, we have a valid, constructed set. &ip_set_lock again, | |
2122 | + * find free id/index and check that it is not already in | |
2123 | + * ip_set_list. | |
2124 | + */ | |
2125 | + write_lock_bh(&ip_set_lock); | |
2126 | + if ((res = find_free_id(set->name, &index, &id)) != 0) { | |
2127 | + DP("no free id!"); | |
2128 | + goto cleanup; | |
2129 | + } | |
2130 | + | |
2131 | + /* Make sure restore gets the same index */ | |
2132 | + if (restore != IP_SET_INVALID_ID && index != restore) { | |
2133 | + DP("Can't restore, sets are screwed up"); | |
2134 | + res = -ERANGE; | |
2135 | + goto cleanup; | |
2136 | + } | |
2137 | + | |
2138 | + /* | |
2139 | + * Finally! Add our shiny new set to the list, and be done. | |
2140 | + */ | |
2141 | + DP("create: '%s' created with index %u, id %u!", set->name, index, id); | |
2142 | + set->id = id; | |
2143 | + ip_set_list[index] = set; | |
2144 | + write_unlock_bh(&ip_set_lock); | |
2145 | + return res; | |
2146 | + | |
2147 | + cleanup: | |
2148 | + write_unlock_bh(&ip_set_lock); | |
2149 | + set->type->destroy(set); | |
2150 | + put_out: | |
2151 | + module_put(set->type->me); | |
2152 | + out: | |
2153 | + kfree(set); | |
2154 | + return res; | |
2155 | +} | |
2156 | + | |
2157 | +/* | |
2158 | + * Destroy a given existing set | |
2159 | + */ | |
2160 | +static void | |
2161 | +ip_set_destroy_set(ip_set_id_t index) | |
2162 | +{ | |
2163 | + struct ip_set *set = ip_set_list[index]; | |
2164 | + | |
2165 | + IP_SET_ASSERT(set); | |
2166 | + DP("set: %s", set->name); | |
2167 | + write_lock_bh(&ip_set_lock); | |
2168 | + FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); | |
2169 | + if (set->binding != IP_SET_INVALID_ID) | |
2170 | + __ip_set_put(set->binding); | |
2171 | + ip_set_list[index] = NULL; | |
2172 | + write_unlock_bh(&ip_set_lock); | |
2173 | + | |
2174 | + /* Must call it without holding any lock */ | |
2175 | + set->type->destroy(set); | |
2176 | + module_put(set->type->me); | |
2177 | + kfree(set); | |
2178 | +} | |
2179 | + | |
2180 | +/* | |
2181 | + * Destroy a set - or all sets | |
2182 | + * Sets must not be referenced/used. | |
2183 | + */ | |
2184 | +static int | |
2185 | +ip_set_destroy(ip_set_id_t index) | |
2186 | +{ | |
2187 | + ip_set_id_t i; | |
2188 | + | |
2189 | + /* ref modification always protected by the mutex */ | |
2190 | + if (index != IP_SET_INVALID_ID) { | |
2191 | + if (atomic_read(&ip_set_list[index]->ref)) | |
2192 | + return -EBUSY; | |
2193 | + ip_set_destroy_set(index); | |
2194 | + } else { | |
2195 | + for (i = 0; i < ip_set_max; i++) { | |
2196 | + if (ip_set_list[i] != NULL | |
2197 | + && (atomic_read(&ip_set_list[i]->ref))) | |
2198 | + return -EBUSY; | |
2199 | + } | |
2200 | + | |
2201 | + for (i = 0; i < ip_set_max; i++) { | |
2202 | + if (ip_set_list[i] != NULL) | |
2203 | + ip_set_destroy_set(i); | |
2204 | + } | |
2205 | + } | |
2206 | + return 0; | |
2207 | +} | |
2208 | + | |
2209 | +static void | |
2210 | +ip_set_flush_set(struct ip_set *set) | |
2211 | +{ | |
2212 | + DP("set: %s %u", set->name, set->id); | |
2213 | + | |
2214 | + write_lock_bh(&set->lock); | |
2215 | + set->type->flush(set); | |
2216 | + write_unlock_bh(&set->lock); | |
2217 | +} | |
2218 | + | |
2219 | +/* | |
2220 | + * Flush data in a set - or in all sets | |
2221 | + */ | |
2222 | +static int | |
2223 | +ip_set_flush(ip_set_id_t index) | |
2224 | +{ | |
2225 | + if (index != IP_SET_INVALID_ID) { | |
2226 | + IP_SET_ASSERT(ip_set_list[index]); | |
2227 | + ip_set_flush_set(ip_set_list[index]); | |
2228 | + } else | |
2229 | + FOREACH_SET_DO(ip_set_flush_set); | |
2230 | + | |
2231 | + return 0; | |
2232 | +} | |
2233 | + | |
2234 | +/* Rename a set */ | |
2235 | +static int | |
2236 | +ip_set_rename(ip_set_id_t index, const char *name) | |
2237 | +{ | |
2238 | + struct ip_set *set = ip_set_list[index]; | |
2239 | + ip_set_id_t i; | |
2240 | + int res = 0; | |
2241 | + | |
2242 | + DP("set: %s to %s", set->name, name); | |
2243 | + write_lock_bh(&ip_set_lock); | |
2244 | + for (i = 0; i < ip_set_max; i++) { | |
2245 | + if (ip_set_list[i] != NULL | |
2246 | + && strncmp(ip_set_list[i]->name, | |
2247 | + name, | |
2248 | + IP_SET_MAXNAMELEN - 1) == 0) { | |
2249 | + res = -EEXIST; | |
2250 | + goto unlock; | |
2251 | + } | |
2252 | + } | |
2253 | + strncpy(set->name, name, IP_SET_MAXNAMELEN); | |
2254 | + unlock: | |
2255 | + write_unlock_bh(&ip_set_lock); | |
2256 | + return res; | |
2257 | +} | |
2258 | + | |
2259 | +/* | |
2260 | + * Swap two sets so that name/index points to the other. | |
2261 | + * References are also swapped. | |
2262 | + */ | |
2263 | +static int | |
2264 | +ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) | |
2265 | +{ | |
2266 | + struct ip_set *from = ip_set_list[from_index]; | |
2267 | + struct ip_set *to = ip_set_list[to_index]; | |
2268 | + char from_name[IP_SET_MAXNAMELEN]; | |
2269 | + u_int32_t from_ref; | |
2270 | + | |
2271 | + DP("set: %s to %s", from->name, to->name); | |
2272 | + /* Features must not change. Artifical restriction. */ | |
2273 | + if (from->type->features != to->type->features) | |
2274 | + return -ENOEXEC; | |
2275 | + | |
2276 | + /* No magic here: ref munging protected by the mutex */ | |
2277 | + write_lock_bh(&ip_set_lock); | |
2278 | + strncpy(from_name, from->name, IP_SET_MAXNAMELEN); | |
2279 | + from_ref = atomic_read(&from->ref); | |
2280 | + | |
2281 | + strncpy(from->name, to->name, IP_SET_MAXNAMELEN); | |
2282 | + atomic_set(&from->ref, atomic_read(&to->ref)); | |
2283 | + strncpy(to->name, from_name, IP_SET_MAXNAMELEN); | |
2284 | + atomic_set(&to->ref, from_ref); | |
2285 | + | |
2286 | + ip_set_list[from_index] = to; | |
2287 | + ip_set_list[to_index] = from; | |
2288 | + | |
2289 | + write_unlock_bh(&ip_set_lock); | |
2290 | + return 0; | |
2291 | +} | |
2292 | + | |
2293 | +/* | |
2294 | + * List set data | |
2295 | + */ | |
2296 | + | |
2297 | +static inline void | |
2298 | +__set_hash_bindings_size_list(struct ip_set_hash *set_hash, | |
2299 | + ip_set_id_t id, size_t *size) | |
2300 | +{ | |
2301 | + if (set_hash->id == id) | |
2302 | + *size += sizeof(struct ip_set_hash_list); | |
2303 | +} | |
2304 | + | |
2305 | +static inline void | |
2306 | +__set_hash_bindings_size_save(struct ip_set_hash *set_hash, | |
2307 | + ip_set_id_t id, size_t *size) | |
2308 | +{ | |
2309 | + if (set_hash->id == id) | |
2310 | + *size += sizeof(struct ip_set_hash_save); | |
2311 | +} | |
2312 | + | |
2313 | +static inline void | |
2314 | +__set_hash_bindings(struct ip_set_hash *set_hash, | |
2315 | + ip_set_id_t id, void *data, int *used) | |
2316 | +{ | |
2317 | + if (set_hash->id == id) { | |
2318 | + struct ip_set_hash_list *hash_list = | |
2319 | + (struct ip_set_hash_list *)(data + *used); | |
2320 | + | |
2321 | + hash_list->ip = set_hash->ip; | |
2322 | + hash_list->binding = set_hash->binding; | |
2323 | + *used += sizeof(struct ip_set_hash_list); | |
2324 | + } | |
2325 | +} | |
2326 | + | |
2327 | +static int ip_set_list_set(ip_set_id_t index, | |
2328 | + void *data, | |
2329 | + int *used, | |
2330 | + int len) | |
2331 | +{ | |
2332 | + struct ip_set *set = ip_set_list[index]; | |
2333 | + struct ip_set_list *set_list; | |
2334 | + | |
2335 | + /* Pointer to our header */ | |
2336 | + set_list = (struct ip_set_list *) (data + *used); | |
2337 | + | |
2338 | + DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); | |
2339 | + | |
2340 | + /* Get and ensure header size */ | |
2341 | + if (*used + sizeof(struct ip_set_list) > len) | |
2342 | + goto not_enough_mem; | |
2343 | + *used += sizeof(struct ip_set_list); | |
2344 | + | |
2345 | + read_lock_bh(&set->lock); | |
2346 | + /* Get and ensure set specific header size */ | |
2347 | + set_list->header_size = set->type->header_size; | |
2348 | + if (*used + set_list->header_size > len) | |
2349 | + goto unlock_set; | |
2350 | + | |
2351 | + /* Fill in the header */ | |
2352 | + set_list->index = index; | |
2353 | + set_list->binding = set->binding; | |
2354 | + set_list->ref = atomic_read(&set->ref); | |
2355 | + | |
2356 | + /* Fill in set spefific header data */ | |
2357 | + set->type->list_header(set, data + *used); | |
2358 | + *used += set_list->header_size; | |
2359 | + | |
2360 | + /* Get and ensure set specific members size */ | |
2361 | + set_list->members_size = set->type->list_members_size(set); | |
2362 | + if (*used + set_list->members_size > len) | |
2363 | + goto unlock_set; | |
2364 | + | |
2365 | + /* Fill in set spefific members data */ | |
2366 | + set->type->list_members(set, data + *used); | |
2367 | + *used += set_list->members_size; | |
2368 | + read_unlock_bh(&set->lock); | |
2369 | + | |
2370 | + /* Bindings */ | |
2371 | + | |
2372 | + /* Get and ensure set specific bindings size */ | |
2373 | + set_list->bindings_size = 0; | |
2374 | + FOREACH_HASH_DO(__set_hash_bindings_size_list, | |
2375 | + set->id, &set_list->bindings_size); | |
2376 | + if (*used + set_list->bindings_size > len) | |
2377 | + goto not_enough_mem; | |
2378 | + | |
2379 | + /* Fill in set spefific bindings data */ | |
2380 | + FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); | |
2381 | + | |
2382 | + return 0; | |
2383 | + | |
2384 | + unlock_set: | |
2385 | + read_unlock_bh(&set->lock); | |
2386 | + not_enough_mem: | |
2387 | + DP("not enough mem, try again"); | |
2388 | + return -EAGAIN; | |
2389 | +} | |
2390 | + | |
2391 | +/* | |
2392 | + * Save sets | |
2393 | + */ | |
2394 | +static int ip_set_save_set(ip_set_id_t index, | |
2395 | + void *data, | |
2396 | + int *used, | |
2397 | + int len) | |
2398 | +{ | |
2399 | + struct ip_set *set; | |
2400 | + struct ip_set_save *set_save; | |
2401 | + | |
2402 | + /* Pointer to our header */ | |
2403 | + set_save = (struct ip_set_save *) (data + *used); | |
2404 | + | |
2405 | + /* Get and ensure header size */ | |
2406 | + if (*used + sizeof(struct ip_set_save) > len) | |
2407 | + goto not_enough_mem; | |
2408 | + *used += sizeof(struct ip_set_save); | |
2409 | + | |
2410 | + set = ip_set_list[index]; | |
2411 | + DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, | |
2412 | + data, data + *used); | |
2413 | + | |
2414 | + read_lock_bh(&set->lock); | |
2415 | + /* Get and ensure set specific header size */ | |
2416 | + set_save->header_size = set->type->header_size; | |
2417 | + if (*used + set_save->header_size > len) | |
2418 | + goto unlock_set; | |
2419 | + | |
2420 | + /* Fill in the header */ | |
2421 | + set_save->index = index; | |
2422 | + set_save->binding = set->binding; | |
2423 | + | |
2424 | + /* Fill in set spefific header data */ | |
2425 | + set->type->list_header(set, data + *used); | |
2426 | + *used += set_save->header_size; | |
2427 | + | |
2428 | + DP("set header filled: %s, used: %u %p %p", set->name, *used, | |
2429 | + data, data + *used); | |
2430 | + /* Get and ensure set specific members size */ | |
2431 | + set_save->members_size = set->type->list_members_size(set); | |
2432 | + if (*used + set_save->members_size > len) | |
2433 | + goto unlock_set; | |
2434 | + | |
2435 | + /* Fill in set spefific members data */ | |
2436 | + set->type->list_members(set, data + *used); | |
2437 | + *used += set_save->members_size; | |
2438 | + read_unlock_bh(&set->lock); | |
2439 | + DP("set members filled: %s, used: %u %p %p", set->name, *used, | |
2440 | + data, data + *used); | |
2441 | + return 0; | |
2442 | + | |
2443 | + unlock_set: | |
2444 | + read_unlock_bh(&set->lock); | |
2445 | + not_enough_mem: | |
2446 | + DP("not enough mem, try again"); | |
2447 | + return -EAGAIN; | |
2448 | +} | |
2449 | + | |
2450 | +static inline void | |
2451 | +__set_hash_save_bindings(struct ip_set_hash *set_hash, | |
2452 | + ip_set_id_t id, | |
2453 | + void *data, | |
2454 | + int *used, | |
2455 | + int len, | |
2456 | + int *res) | |
2457 | +{ | |
2458 | + if (*res == 0 | |
2459 | + && (id == IP_SET_INVALID_ID || set_hash->id == id)) { | |
2460 | + struct ip_set_hash_save *hash_save = | |
2461 | + (struct ip_set_hash_save *)(data + *used); | |
2462 | + /* Ensure bindings size */ | |
2463 | + if (*used + sizeof(struct ip_set_hash_save) > len) { | |
2464 | + *res = -ENOMEM; | |
2465 | + return; | |
2466 | + } | |
2467 | + hash_save->id = set_hash->id; | |
2468 | + hash_save->ip = set_hash->ip; | |
2469 | + hash_save->binding = set_hash->binding; | |
2470 | + *used += sizeof(struct ip_set_hash_save); | |
2471 | + } | |
2472 | +} | |
2473 | + | |
2474 | +static int ip_set_save_bindings(ip_set_id_t index, | |
2475 | + void *data, | |
2476 | + int *used, | |
2477 | + int len) | |
2478 | +{ | |
2479 | + int res = 0; | |
2480 | + struct ip_set_save *set_save; | |
2481 | + | |
2482 | + DP("used %u, len %u", *used, len); | |
2483 | + /* Get and ensure header size */ | |
2484 | + if (*used + sizeof(struct ip_set_save) > len) | |
2485 | + return -ENOMEM; | |
2486 | + | |
2487 | + /* Marker */ | |
2488 | + set_save = (struct ip_set_save *) (data + *used); | |
2489 | + set_save->index = IP_SET_INVALID_ID; | |
2490 | + *used += sizeof(struct ip_set_save); | |
2491 | + | |
2492 | + DP("marker added used %u, len %u", *used, len); | |
2493 | + /* Fill in bindings data */ | |
2494 | + if (index != IP_SET_INVALID_ID) | |
2495 | + /* Sets are identified by id in hash */ | |
2496 | + index = ip_set_list[index]->id; | |
2497 | + FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); | |
2498 | + | |
2499 | + return res; | |
2500 | +} | |
2501 | + | |
2502 | +/* | |
2503 | + * Restore sets | |
2504 | + */ | |
2505 | +static int ip_set_restore(void *data, | |
2506 | + int len) | |
2507 | +{ | |
2508 | + int res = 0; | |
2509 | + int line = 0, used = 0, members_size; | |
2510 | + struct ip_set *set; | |
2511 | + struct ip_set_hash_save *hash_save; | |
2512 | + struct ip_set_restore *set_restore; | |
2513 | + ip_set_id_t index; | |
2514 | + | |
2515 | + /* Loop to restore sets */ | |
2516 | + while (1) { | |
2517 | + line++; | |
2518 | + | |
2519 | + DP("%u %u %u", used, sizeof(struct ip_set_restore), len); | |
2520 | + /* Get and ensure header size */ | |
2521 | + if (used + sizeof(struct ip_set_restore) > len) | |
2522 | + return line; | |
2523 | + set_restore = (struct ip_set_restore *) (data + used); | |
2524 | + used += sizeof(struct ip_set_restore); | |
2525 | + | |
2526 | + /* Ensure data size */ | |
2527 | + if (used | |
2528 | + + set_restore->header_size | |
2529 | + + set_restore->members_size > len) | |
2530 | + return line; | |
2531 | + | |
2532 | + /* Check marker */ | |
2533 | + if (set_restore->index == IP_SET_INVALID_ID) { | |
2534 | + line--; | |
2535 | + goto bindings; | |
2536 | + } | |
2537 | + | |
2538 | + /* Try to create the set */ | |
2539 | + DP("restore %s %s", set_restore->name, set_restore->typename); | |
2540 | + res = ip_set_create(set_restore->name, | |
2541 | + set_restore->typename, | |
2542 | + set_restore->index, | |
2543 | + data + used, | |
2544 | + set_restore->header_size); | |
2545 | + | |
2546 | + if (res != 0) | |
2547 | + return line; | |
2548 | + used += set_restore->header_size; | |
2549 | + | |
2550 | + index = ip_set_find_byindex(set_restore->index); | |
2551 | + DP("index %u, restore_index %u", index, set_restore->index); | |
2552 | + if (index != set_restore->index) | |
2553 | + return line; | |
2554 | + /* Try to restore members data */ | |
2555 | + set = ip_set_list[index]; | |
2556 | + members_size = 0; | |
2557 | + DP("members_size %u reqsize %u", | |
2558 | + set_restore->members_size, set->type->reqsize); | |
2559 | + while (members_size + set->type->reqsize <= | |
2560 | + set_restore->members_size) { | |
2561 | + line++; | |
2562 | + DP("members: %u, line %u", members_size, line); | |
2563 | + res = __ip_set_addip(index, | |
2564 | + data + used + members_size, | |
2565 | + set->type->reqsize); | |
2566 | + if (!(res == 0 || res == -EEXIST)) | |
2567 | + return line; | |
2568 | + members_size += set->type->reqsize; | |
2569 | + } | |
2570 | + | |
2571 | + DP("members_size %u %u", | |
2572 | + set_restore->members_size, members_size); | |
2573 | + if (members_size != set_restore->members_size) | |
2574 | + return line++; | |
2575 | + used += set_restore->members_size; | |
2576 | + } | |
2577 | + | |
2578 | + bindings: | |
2579 | + /* Loop to restore bindings */ | |
2580 | + while (used < len) { | |
2581 | + line++; | |
2582 | + | |
2583 | + DP("restore binding, line %u", line); | |
2584 | + /* Get and ensure size */ | |
2585 | + if (used + sizeof(struct ip_set_hash_save) > len) | |
2586 | + return line; | |
2587 | + hash_save = (struct ip_set_hash_save *) (data + used); | |
2588 | + used += sizeof(struct ip_set_hash_save); | |
2589 | + | |
2590 | + /* hash_save->id is used to store the index */ | |
2591 | + index = ip_set_find_byindex(hash_save->id); | |
2592 | + DP("restore binding index %u, id %u, %u -> %u", | |
2593 | + index, hash_save->id, hash_save->ip, hash_save->binding); | |
2594 | + if (index != hash_save->id) | |
2595 | + return line; | |
2596 | + | |
2597 | + set = ip_set_list[hash_save->id]; | |
2598 | + /* Null valued IP means default binding */ | |
2599 | + if (hash_save->ip) | |
2600 | + res = ip_set_hash_add(set->id, | |
2601 | + hash_save->ip, | |
2602 | + hash_save->binding); | |
2603 | + else { | |
2604 | + IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); | |
2605 | + write_lock_bh(&ip_set_lock); | |
2606 | + set->binding = hash_save->binding; | |
2607 | + __ip_set_get(set->binding); | |
2608 | + write_unlock_bh(&ip_set_lock); | |
2609 | + DP("default binding: %u", set->binding); | |
2610 | + } | |
2611 | + if (res != 0) | |
2612 | + return line; | |
2613 | + } | |
2614 | + if (used != len) | |
2615 | + return line; | |
2616 | + | |
2617 | + return 0; | |
2618 | +} | |
2619 | + | |
2620 | +static int | |
2621 | +ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) | |
2622 | +{ | |
2623 | + void *data; | |
2624 | + int res = 0; /* Assume OK */ | |
2625 | + unsigned *op; | |
2626 | + struct ip_set_req_adt *req_adt; | |
2627 | + ip_set_id_t index = IP_SET_INVALID_ID; | |
2628 | + int (*adtfn)(ip_set_id_t index, | |
2629 | + const void *data, size_t size); | |
2630 | + struct fn_table { | |
2631 | + int (*fn)(ip_set_id_t index, | |
2632 | + const void *data, size_t size); | |
2633 | + } adtfn_table[] = | |
2634 | + { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, | |
2635 | + { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, | |
2636 | + }; | |
2637 | + | |
2638 | + DP("optval=%d, user=%p, len=%d", optval, user, len); | |
2639 | + if (!capable(CAP_NET_ADMIN)) | |
2640 | + return -EPERM; | |
2641 | + if (optval != SO_IP_SET) | |
2642 | + return -EBADF; | |
2643 | + if (len <= sizeof(unsigned)) { | |
2644 | + ip_set_printk("short userdata (want >%zu, got %u)", | |
2645 | + sizeof(unsigned), len); | |
2646 | + return -EINVAL; | |
2647 | + } | |
2648 | + data = vmalloc(len); | |
2649 | + if (!data) { | |
2650 | + DP("out of mem for %u bytes", len); | |
2651 | + return -ENOMEM; | |
2652 | + } | |
2653 | + if (copy_from_user(data, user, len) != 0) { | |
2654 | + res = -EFAULT; | |
2655 | + goto done; | |
2656 | + } | |
2657 | + if (down_interruptible(&ip_set_app_mutex)) { | |
2658 | + res = -EINTR; | |
2659 | + goto done; | |
2660 | + } | |
2661 | + | |
2662 | + op = (unsigned *)data; | |
2663 | + DP("op=%x", *op); | |
2664 | + | |
2665 | + if (*op < IP_SET_OP_VERSION) { | |
2666 | + /* Check the version at the beginning of operations */ | |
2667 | + struct ip_set_req_version *req_version = | |
2668 | + (struct ip_set_req_version *) data; | |
2669 | + if (req_version->version != IP_SET_PROTOCOL_VERSION) { | |
2670 | + res = -EPROTO; | |
2671 | + goto done; | |
2672 | + } | |
2673 | + } | |
2674 | + | |
2675 | + switch (*op) { | |
2676 | + case IP_SET_OP_CREATE:{ | |
2677 | + struct ip_set_req_create *req_create | |
2678 | + = (struct ip_set_req_create *) data; | |
2679 | + | |
2680 | + if (len <= sizeof(struct ip_set_req_create)) { | |
2681 | + ip_set_printk("short CREATE data (want >%zu, got %u)", | |
2682 | + sizeof(struct ip_set_req_create), len); | |
2683 | + res = -EINVAL; | |
2684 | + goto done; | |
2685 | + } | |
2686 | + req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2687 | + req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2688 | + res = ip_set_create(req_create->name, | |
2689 | + req_create->typename, | |
2690 | + IP_SET_INVALID_ID, | |
2691 | + data + sizeof(struct ip_set_req_create), | |
2692 | + len - sizeof(struct ip_set_req_create)); | |
2693 | + goto done; | |
2694 | + } | |
2695 | + case IP_SET_OP_DESTROY:{ | |
2696 | + struct ip_set_req_std *req_destroy | |
2697 | + = (struct ip_set_req_std *) data; | |
2698 | + | |
2699 | + if (len != sizeof(struct ip_set_req_std)) { | |
2700 | + ip_set_printk("invalid DESTROY data (want %zu, got %u)", | |
2701 | + sizeof(struct ip_set_req_std), len); | |
2702 | + res = -EINVAL; | |
2703 | + goto done; | |
2704 | + } | |
2705 | + if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { | |
2706 | + /* Destroy all sets */ | |
2707 | + index = IP_SET_INVALID_ID; | |
2708 | + } else { | |
2709 | + req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2710 | + index = ip_set_find_byname(req_destroy->name); | |
2711 | + | |
2712 | + if (index == IP_SET_INVALID_ID) { | |
2713 | + res = -ENOENT; | |
2714 | + goto done; | |
2715 | + } | |
2716 | + } | |
2717 | + | |
2718 | + res = ip_set_destroy(index); | |
2719 | + goto done; | |
2720 | + } | |
2721 | + case IP_SET_OP_FLUSH:{ | |
2722 | + struct ip_set_req_std *req_flush = | |
2723 | + (struct ip_set_req_std *) data; | |
2724 | + | |
2725 | + if (len != sizeof(struct ip_set_req_std)) { | |
2726 | + ip_set_printk("invalid FLUSH data (want %zu, got %u)", | |
2727 | + sizeof(struct ip_set_req_std), len); | |
2728 | + res = -EINVAL; | |
2729 | + goto done; | |
2730 | + } | |
2731 | + if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { | |
2732 | + /* Flush all sets */ | |
2733 | + index = IP_SET_INVALID_ID; | |
2734 | + } else { | |
2735 | + req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2736 | + index = ip_set_find_byname(req_flush->name); | |
2737 | + | |
2738 | + if (index == IP_SET_INVALID_ID) { | |
2739 | + res = -ENOENT; | |
2740 | + goto done; | |
2741 | + } | |
2742 | + } | |
2743 | + res = ip_set_flush(index); | |
2744 | + goto done; | |
2745 | + } | |
2746 | + case IP_SET_OP_RENAME:{ | |
2747 | + struct ip_set_req_create *req_rename | |
2748 | + = (struct ip_set_req_create *) data; | |
2749 | + | |
2750 | + if (len != sizeof(struct ip_set_req_create)) { | |
2751 | + ip_set_printk("invalid RENAME data (want %zu, got %u)", | |
2752 | + sizeof(struct ip_set_req_create), len); | |
2753 | + res = -EINVAL; | |
2754 | + goto done; | |
2755 | + } | |
2756 | + | |
2757 | + req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2758 | + req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2759 | + | |
2760 | + index = ip_set_find_byname(req_rename->name); | |
2761 | + if (index == IP_SET_INVALID_ID) { | |
2762 | + res = -ENOENT; | |
2763 | + goto done; | |
2764 | + } | |
2765 | + res = ip_set_rename(index, req_rename->typename); | |
2766 | + goto done; | |
2767 | + } | |
2768 | + case IP_SET_OP_SWAP:{ | |
2769 | + struct ip_set_req_create *req_swap | |
2770 | + = (struct ip_set_req_create *) data; | |
2771 | + ip_set_id_t to_index; | |
2772 | + | |
2773 | + if (len != sizeof(struct ip_set_req_create)) { | |
2774 | + ip_set_printk("invalid SWAP data (want %zu, got %u)", | |
2775 | + sizeof(struct ip_set_req_create), len); | |
2776 | + res = -EINVAL; | |
2777 | + goto done; | |
2778 | + } | |
2779 | + | |
2780 | + req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2781 | + req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2782 | + | |
2783 | + index = ip_set_find_byname(req_swap->name); | |
2784 | + if (index == IP_SET_INVALID_ID) { | |
2785 | + res = -ENOENT; | |
2786 | + goto done; | |
2787 | + } | |
2788 | + to_index = ip_set_find_byname(req_swap->typename); | |
2789 | + if (to_index == IP_SET_INVALID_ID) { | |
2790 | + res = -ENOENT; | |
2791 | + goto done; | |
2792 | + } | |
2793 | + res = ip_set_swap(index, to_index); | |
2794 | + goto done; | |
2795 | + } | |
2796 | + default: | |
2797 | + break; /* Set identified by id */ | |
2798 | + } | |
2799 | + | |
2800 | + /* There we may have add/del/test/bind/unbind/test_bind operations */ | |
2801 | + if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { | |
2802 | + res = -EBADMSG; | |
2803 | + goto done; | |
2804 | + } | |
2805 | + adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; | |
2806 | + | |
2807 | + if (len < sizeof(struct ip_set_req_adt)) { | |
2808 | + ip_set_printk("short data in adt request (want >=%zu, got %u)", | |
2809 | + sizeof(struct ip_set_req_adt), len); | |
2810 | + res = -EINVAL; | |
2811 | + goto done; | |
2812 | + } | |
2813 | + req_adt = (struct ip_set_req_adt *) data; | |
2814 | + | |
2815 | + /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ | |
2816 | + if (!(*op == IP_SET_OP_UNBIND_SET | |
2817 | + && req_adt->index == IP_SET_INVALID_ID)) { | |
2818 | + index = ip_set_find_byindex(req_adt->index); | |
2819 | + if (index == IP_SET_INVALID_ID) { | |
2820 | + res = -ENOENT; | |
2821 | + goto done; | |
2822 | + } | |
2823 | + } | |
2824 | + res = adtfn(index, data, len); | |
2825 | + | |
2826 | + done: | |
2827 | + up(&ip_set_app_mutex); | |
2828 | + vfree(data); | |
2829 | + if (res > 0) | |
2830 | + res = 0; | |
2831 | + DP("final result %d", res); | |
2832 | + return res; | |
2833 | +} | |
2834 | + | |
2835 | +static int | |
2836 | +ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) | |
2837 | +{ | |
2838 | + int res = 0; | |
2839 | + unsigned *op; | |
2840 | + ip_set_id_t index = IP_SET_INVALID_ID; | |
2841 | + void *data; | |
2842 | + int copylen = *len; | |
2843 | + | |
2844 | + DP("optval=%d, user=%p, len=%d", optval, user, *len); | |
2845 | + if (!capable(CAP_NET_ADMIN)) | |
2846 | + return -EPERM; | |
2847 | + if (optval != SO_IP_SET) | |
2848 | + return -EBADF; | |
2849 | + if (*len < sizeof(unsigned)) { | |
2850 | + ip_set_printk("short userdata (want >=%zu, got %d)", | |
2851 | + sizeof(unsigned), *len); | |
2852 | + return -EINVAL; | |
2853 | + } | |
2854 | + data = vmalloc(*len); | |
2855 | + if (!data) { | |
2856 | + DP("out of mem for %d bytes", *len); | |
2857 | + return -ENOMEM; | |
2858 | + } | |
2859 | + if (copy_from_user(data, user, *len) != 0) { | |
2860 | + res = -EFAULT; | |
2861 | + goto done; | |
2862 | + } | |
2863 | + if (down_interruptible(&ip_set_app_mutex)) { | |
2864 | + res = -EINTR; | |
2865 | + goto done; | |
2866 | + } | |
2867 | + | |
2868 | + op = (unsigned *) data; | |
2869 | + DP("op=%x", *op); | |
2870 | + | |
2871 | + if (*op < IP_SET_OP_VERSION) { | |
2872 | + /* Check the version at the beginning of operations */ | |
2873 | + struct ip_set_req_version *req_version = | |
2874 | + (struct ip_set_req_version *) data; | |
2875 | + if (req_version->version != IP_SET_PROTOCOL_VERSION) { | |
2876 | + res = -EPROTO; | |
2877 | + goto done; | |
2878 | + } | |
2879 | + } | |
2880 | + | |
2881 | + switch (*op) { | |
2882 | + case IP_SET_OP_VERSION: { | |
2883 | + struct ip_set_req_version *req_version = | |
2884 | + (struct ip_set_req_version *) data; | |
2885 | + | |
2886 | + if (*len != sizeof(struct ip_set_req_version)) { | |
2887 | + ip_set_printk("invalid VERSION (want %zu, got %d)", | |
2888 | + sizeof(struct ip_set_req_version), | |
2889 | + *len); | |
2890 | + res = -EINVAL; | |
2891 | + goto done; | |
2892 | + } | |
2893 | + | |
2894 | + req_version->version = IP_SET_PROTOCOL_VERSION; | |
2895 | + res = copy_to_user(user, req_version, | |
2896 | + sizeof(struct ip_set_req_version)); | |
2897 | + goto done; | |
2898 | + } | |
2899 | + case IP_SET_OP_GET_BYNAME: { | |
2900 | + struct ip_set_req_get_set *req_get | |
2901 | + = (struct ip_set_req_get_set *) data; | |
2902 | + | |
2903 | + if (*len != sizeof(struct ip_set_req_get_set)) { | |
2904 | + ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", | |
2905 | + sizeof(struct ip_set_req_get_set), *len); | |
2906 | + res = -EINVAL; | |
2907 | + goto done; | |
2908 | + } | |
2909 | + req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2910 | + index = ip_set_find_byname(req_get->set.name); | |
2911 | + req_get->set.index = index; | |
2912 | + goto copy; | |
2913 | + } | |
2914 | + case IP_SET_OP_GET_BYINDEX: { | |
2915 | + struct ip_set_req_get_set *req_get | |
2916 | + = (struct ip_set_req_get_set *) data; | |
2917 | + | |
2918 | + if (*len != sizeof(struct ip_set_req_get_set)) { | |
2919 | + ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", | |
2920 | + sizeof(struct ip_set_req_get_set), *len); | |
2921 | + res = -EINVAL; | |
2922 | + goto done; | |
2923 | + } | |
2924 | + req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2925 | + index = ip_set_find_byindex(req_get->set.index); | |
2926 | + strncpy(req_get->set.name, | |
2927 | + index == IP_SET_INVALID_ID ? "" | |
2928 | + : ip_set_list[index]->name, IP_SET_MAXNAMELEN); | |
2929 | + goto copy; | |
2930 | + } | |
2931 | + case IP_SET_OP_ADT_GET: { | |
2932 | + struct ip_set_req_adt_get *req_get | |
2933 | + = (struct ip_set_req_adt_get *) data; | |
2934 | + | |
2935 | + if (*len != sizeof(struct ip_set_req_adt_get)) { | |
2936 | + ip_set_printk("invalid ADT_GET (want %zu, got %d)", | |
2937 | + sizeof(struct ip_set_req_adt_get), *len); | |
2938 | + res = -EINVAL; | |
2939 | + goto done; | |
2940 | + } | |
2941 | + req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2942 | + index = ip_set_find_byname(req_get->set.name); | |
2943 | + if (index != IP_SET_INVALID_ID) { | |
2944 | + req_get->set.index = index; | |
2945 | + strncpy(req_get->typename, | |
2946 | + ip_set_list[index]->type->typename, | |
2947 | + IP_SET_MAXNAMELEN - 1); | |
2948 | + } else { | |
2949 | + res = -ENOENT; | |
2950 | + goto done; | |
2951 | + } | |
2952 | + goto copy; | |
2953 | + } | |
2954 | + case IP_SET_OP_MAX_SETS: { | |
2955 | + struct ip_set_req_max_sets *req_max_sets | |
2956 | + = (struct ip_set_req_max_sets *) data; | |
2957 | + ip_set_id_t i; | |
2958 | + | |
2959 | + if (*len != sizeof(struct ip_set_req_max_sets)) { | |
2960 | + ip_set_printk("invalid MAX_SETS (want %zu, got %d)", | |
2961 | + sizeof(struct ip_set_req_max_sets), *len); | |
2962 | + res = -EINVAL; | |
2963 | + goto done; | |
2964 | + } | |
2965 | + | |
2966 | + if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { | |
2967 | + req_max_sets->set.index = IP_SET_INVALID_ID; | |
2968 | + } else { | |
2969 | + req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; | |
2970 | + req_max_sets->set.index = | |
2971 | + ip_set_find_byname(req_max_sets->set.name); | |
2972 | + if (req_max_sets->set.index == IP_SET_INVALID_ID) { | |
2973 | + res = -ENOENT; | |
2974 | + goto done; | |
2975 | + } | |
2976 | + } | |
2977 | + req_max_sets->max_sets = ip_set_max; | |
2978 | + req_max_sets->sets = 0; | |
2979 | + for (i = 0; i < ip_set_max; i++) { | |
2980 | + if (ip_set_list[i] != NULL) | |
2981 | + req_max_sets->sets++; | |
2982 | + } | |
2983 | + goto copy; | |
2984 | + } | |
2985 | + case IP_SET_OP_LIST_SIZE: | |
2986 | + case IP_SET_OP_SAVE_SIZE: { | |
2987 | + struct ip_set_req_setnames *req_setnames | |
2988 | + = (struct ip_set_req_setnames *) data; | |
2989 | + struct ip_set_name_list *name_list; | |
2990 | + struct ip_set *set; | |
2991 | + ip_set_id_t i; | |
2992 | + int used; | |
2993 | + | |
2994 | + if (*len < sizeof(struct ip_set_req_setnames)) { | |
2995 | + ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", | |
2996 | + sizeof(struct ip_set_req_setnames), *len); | |
2997 | + res = -EINVAL; | |
2998 | + goto done; | |
2999 | + } | |
3000 | + | |
3001 | + req_setnames->size = 0; | |
3002 | + used = sizeof(struct ip_set_req_setnames); | |
3003 | + for (i = 0; i < ip_set_max; i++) { | |
3004 | + if (ip_set_list[i] == NULL) | |
3005 | + continue; | |
3006 | + name_list = (struct ip_set_name_list *) | |
3007 | + (data + used); | |
3008 | + used += sizeof(struct ip_set_name_list); | |
3009 | + if (used > copylen) { | |
3010 | + res = -EAGAIN; | |
3011 | + goto done; | |
3012 | + } | |
3013 | + set = ip_set_list[i]; | |
3014 | + /* Fill in index, name, etc. */ | |
3015 | + name_list->index = i; | |
3016 | + name_list->id = set->id; | |
3017 | + strncpy(name_list->name, | |
3018 | + set->name, | |
3019 | + IP_SET_MAXNAMELEN - 1); | |
3020 | + strncpy(name_list->typename, | |
3021 | + set->type->typename, | |
3022 | + IP_SET_MAXNAMELEN - 1); | |
3023 | + DP("filled %s of type %s, index %u\n", | |
3024 | + name_list->name, name_list->typename, | |
3025 | + name_list->index); | |
3026 | + if (!(req_setnames->index == IP_SET_INVALID_ID | |
3027 | + || req_setnames->index == i)) | |
3028 | + continue; | |
3029 | + /* Update size */ | |
3030 | + switch (*op) { | |
3031 | + case IP_SET_OP_LIST_SIZE: { | |
3032 | + req_setnames->size += sizeof(struct ip_set_list) | |
3033 | + + set->type->header_size | |
3034 | + + set->type->list_members_size(set); | |
3035 | + FOREACH_HASH_DO(__set_hash_bindings_size_list, | |
3036 | + i, &req_setnames->size); | |
3037 | + break; | |
3038 | + } | |
3039 | + case IP_SET_OP_SAVE_SIZE: { | |
3040 | + req_setnames->size += sizeof(struct ip_set_save) | |
3041 | + + set->type->header_size | |
3042 | + + set->type->list_members_size(set); | |
3043 | + FOREACH_HASH_DO(__set_hash_bindings_size_save, | |
3044 | + i, &req_setnames->size); | |
3045 | + break; | |
3046 | + } | |
3047 | + default: | |
3048 | + break; | |
3049 | + } | |
3050 | + } | |
3051 | + if (copylen != used) { | |
3052 | + res = -EAGAIN; | |
3053 | + goto done; | |
3054 | + } | |
3055 | + goto copy; | |
3056 | + } | |
3057 | + case IP_SET_OP_LIST: { | |
3058 | + struct ip_set_req_list *req_list | |
3059 | + = (struct ip_set_req_list *) data; | |
3060 | + ip_set_id_t i; | |
3061 | + int used; | |
3062 | + | |
3063 | + if (*len < sizeof(struct ip_set_req_list)) { | |
3064 | + ip_set_printk("short LIST (want >=%zu, got %d)", | |
3065 | + sizeof(struct ip_set_req_list), *len); | |
3066 | + res = -EINVAL; | |
3067 | + goto done; | |
3068 | + } | |
3069 | + index = req_list->index; | |
3070 | + if (index != IP_SET_INVALID_ID | |
3071 | + && ip_set_find_byindex(index) != index) { | |
3072 | + res = -ENOENT; | |
3073 | + goto done; | |
3074 | + } | |
3075 | + used = 0; | |
3076 | + if (index == IP_SET_INVALID_ID) { | |
3077 | + /* List all sets */ | |
3078 | + for (i = 0; i < ip_set_max && res == 0; i++) { | |
3079 | + if (ip_set_list[i] != NULL) | |
3080 | + res = ip_set_list_set(i, data, &used, *len); | |
3081 | + } | |
3082 | + } else { | |
3083 | + /* List an individual set */ | |
3084 | + res = ip_set_list_set(index, data, &used, *len); | |
3085 | + } | |
3086 | + if (res != 0) | |
3087 | + goto done; | |
3088 | + else if (copylen != used) { | |
3089 | + res = -EAGAIN; | |
3090 | + goto done; | |
3091 | + } | |
3092 | + goto copy; | |
3093 | + } | |
3094 | + case IP_SET_OP_SAVE: { | |
3095 | + struct ip_set_req_list *req_save | |
3096 | + = (struct ip_set_req_list *) data; | |
3097 | + ip_set_id_t i; | |
3098 | + int used; | |
3099 | + | |
3100 | + if (*len < sizeof(struct ip_set_req_list)) { | |
3101 | + ip_set_printk("short SAVE (want >=%zu, got %d)", | |
3102 | + sizeof(struct ip_set_req_list), *len); | |
3103 | + res = -EINVAL; | |
3104 | + goto done; | |
3105 | + } | |
3106 | + index = req_save->index; | |
3107 | + if (index != IP_SET_INVALID_ID | |
3108 | + && ip_set_find_byindex(index) != index) { | |
3109 | + res = -ENOENT; | |
3110 | + goto done; | |
3111 | + } | |
3112 | + used = 0; | |
3113 | + if (index == IP_SET_INVALID_ID) { | |
3114 | + /* Save all sets */ | |
3115 | + for (i = 0; i < ip_set_max && res == 0; i++) { | |
3116 | + if (ip_set_list[i] != NULL) | |
3117 | + res = ip_set_save_set(i, data, &used, *len); | |
3118 | + } | |
3119 | + } else { | |
3120 | + /* Save an individual set */ | |
3121 | + res = ip_set_save_set(index, data, &used, *len); | |
3122 | + } | |
3123 | + if (res == 0) | |
3124 | + res = ip_set_save_bindings(index, data, &used, *len); | |
3125 | + | |
3126 | + if (res != 0) | |
3127 | + goto done; | |
3128 | + else if (copylen != used) { | |
3129 | + res = -EAGAIN; | |
3130 | + goto done; | |
3131 | + } | |
3132 | + goto copy; | |
3133 | + } | |
3134 | + case IP_SET_OP_RESTORE: { | |
3135 | + struct ip_set_req_setnames *req_restore | |
3136 | + = (struct ip_set_req_setnames *) data; | |
3137 | + int line; | |
3138 | + | |
3139 | + if (*len < sizeof(struct ip_set_req_setnames) | |
3140 | + || *len != req_restore->size) { | |
3141 | + ip_set_printk("invalid RESTORE (want =%zu, got %d)", | |
3142 | + req_restore->size, *len); | |
3143 | + res = -EINVAL; | |
3144 | + goto done; | |
3145 | + } | |
3146 | + line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), | |
3147 | + req_restore->size - sizeof(struct ip_set_req_setnames)); | |
3148 | + DP("ip_set_restore: %u", line); | |
3149 | + if (line != 0) { | |
3150 | + res = -EAGAIN; | |
3151 | + req_restore->size = line; | |
3152 | + copylen = sizeof(struct ip_set_req_setnames); | |
3153 | + goto copy; | |
3154 | + } | |
3155 | + goto done; | |
3156 | + } | |
3157 | + default: | |
3158 | + res = -EBADMSG; | |
3159 | + goto done; | |
3160 | + } /* end of switch(op) */ | |
3161 | + | |
3162 | + copy: | |
3163 | + DP("set %s, copylen %u", index != IP_SET_INVALID_ID | |
3164 | + && ip_set_list[index] | |
3165 | + ? ip_set_list[index]->name | |
3166 | + : ":all:", copylen); | |
3167 | + res = copy_to_user(user, data, copylen); | |
3168 | + | |
3169 | + done: | |
3170 | + up(&ip_set_app_mutex); | |
3171 | + vfree(data); | |
3172 | + if (res > 0) | |
3173 | + res = 0; | |
3174 | + DP("final result %d", res); | |
3175 | + return res; | |
3176 | +} | |
3177 | + | |
3178 | +static struct nf_sockopt_ops so_set = { | |
3179 | + .pf = PF_INET, | |
3180 | + .set_optmin = SO_IP_SET, | |
3181 | + .set_optmax = SO_IP_SET + 1, | |
3182 | + .set = &ip_set_sockfn_set, | |
3183 | + .get_optmin = SO_IP_SET, | |
3184 | + .get_optmax = SO_IP_SET + 1, | |
3185 | + .get = &ip_set_sockfn_get, | |
3186 | + .use = 0 | |
3187 | +}; | |
3188 | + | |
3189 | +static int max_sets, hash_size; | |
3190 | +module_param(max_sets, int, 0600); | |
3191 | +MODULE_PARM_DESC(max_sets, "maximal number of sets"); | |
3192 | +module_param(hash_size, int, 0600); | |
3193 | +MODULE_PARM_DESC(hash_size, "hash size for bindings"); | |
3194 | +MODULE_LICENSE("GPL"); | |
3195 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
3196 | +MODULE_DESCRIPTION("module implementing core IP set support"); | |
3197 | + | |
3198 | +static int __init init(void) | |
3199 | +{ | |
3200 | + int res; | |
3201 | + ip_set_id_t i; | |
3202 | + | |
3203 | + get_random_bytes(&ip_set_hash_random, 4); | |
3204 | + if (max_sets) | |
3205 | + ip_set_max = max_sets; | |
3206 | + ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); | |
3207 | + if (!ip_set_list) { | |
3208 | + printk(KERN_ERR "Unable to create ip_set_list\n"); | |
3209 | + return -ENOMEM; | |
3210 | + } | |
3211 | + memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); | |
3212 | + if (hash_size) | |
3213 | + ip_set_bindings_hash_size = hash_size; | |
3214 | + ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); | |
3215 | + if (!ip_set_hash) { | |
3216 | + printk(KERN_ERR "Unable to create ip_set_hash\n"); | |
3217 | + vfree(ip_set_list); | |
3218 | + return -ENOMEM; | |
3219 | + } | |
3220 | + for (i = 0; i < ip_set_bindings_hash_size; i++) | |
3221 | + INIT_LIST_HEAD(&ip_set_hash[i]); | |
3222 | + | |
3223 | + INIT_LIST_HEAD(&set_type_list); | |
3224 | + | |
3225 | + res = nf_register_sockopt(&so_set); | |
3226 | + if (res != 0) { | |
3227 | + ip_set_printk("SO_SET registry failed: %d", res); | |
3228 | + vfree(ip_set_list); | |
3229 | + vfree(ip_set_hash); | |
3230 | + return res; | |
3231 | + } | |
3232 | + return 0; | |
3233 | +} | |
3234 | + | |
3235 | +static void __exit fini(void) | |
3236 | +{ | |
3237 | + /* There can't be any existing set or binding */ | |
3238 | + nf_unregister_sockopt(&so_set); | |
3239 | + vfree(ip_set_list); | |
3240 | + vfree(ip_set_hash); | |
3241 | + DP("these are the famous last words"); | |
3242 | +} | |
3243 | + | |
3244 | +EXPORT_SYMBOL(ip_set_register_set_type); | |
3245 | +EXPORT_SYMBOL(ip_set_unregister_set_type); | |
3246 | + | |
3247 | +EXPORT_SYMBOL(ip_set_get_byname); | |
3248 | +EXPORT_SYMBOL(ip_set_get_byindex); | |
3249 | +EXPORT_SYMBOL(ip_set_put); | |
3250 | + | |
3251 | +EXPORT_SYMBOL(ip_set_addip_kernel); | |
3252 | +EXPORT_SYMBOL(ip_set_delip_kernel); | |
3253 | +EXPORT_SYMBOL(ip_set_testip_kernel); | |
3254 | + | |
3255 | +module_init(init); | |
3256 | +module_exit(fini); | |
3257 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_iphash.c linux/net/ipv4/netfilter/ip_set_iphash.c | |
3258 | --- linux.org/net/ipv4/netfilter/ip_set_iphash.c 1970-01-01 01:00:00.000000000 +0100 | |
3259 | +++ linux/net/ipv4/netfilter/ip_set_iphash.c 2006-05-04 10:26:33.000000000 +0200 | |
3260 | @@ -0,0 +1,398 @@ | |
3261 | +/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
3262 | + * | |
3263 | + * This program is free software; you can redistribute it and/or modify | |
3264 | + * it under the terms of the GNU General Public License version 2 as | |
3265 | + * published by the Free Software Foundation. | |
3266 | + */ | |
3267 | + | |
3268 | +/* Kernel module implementing an ip hash set */ | |
3269 | + | |
3270 | +#include <linux/module.h> | |
3271 | +#include <linux/ip.h> | |
3272 | +#include <linux/skbuff.h> | |
3273 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
3274 | +#include <linux/netfilter_ipv4/ip_set.h> | |
3275 | +#include <linux/errno.h> | |
3276 | +#include <asm/uaccess.h> | |
3277 | +#include <asm/bitops.h> | |
3278 | +#include <linux/spinlock.h> | |
3279 | +#include <linux/vmalloc.h> | |
3280 | +#include <linux/random.h> | |
3281 | + | |
3282 | +#include <net/ip.h> | |
3283 | + | |
3284 | +#include <linux/netfilter_ipv4/ip_set_malloc.h> | |
3285 | +#include <linux/netfilter_ipv4/ip_set_iphash.h> | |
3286 | +#include <linux/netfilter_ipv4/ip_set_jhash.h> | |
3287 | + | |
3288 | +static inline __u32 | |
3289 | +jhash_ip(const struct ip_set_iphash *map, uint16_t i, ip_set_ip_t ip) | |
3290 | +{ | |
3291 | + return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); | |
3292 | +} | |
3293 | + | |
3294 | +static inline __u32 | |
3295 | +hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3296 | +{ | |
3297 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3298 | + __u32 id; | |
3299 | + u_int16_t i; | |
3300 | + ip_set_ip_t *elem; | |
3301 | + | |
3302 | + *hash_ip = ip & map->netmask; | |
3303 | + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", | |
3304 | + set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); | |
3305 | + | |
3306 | + for (i = 0; i < map->probes; i++) { | |
3307 | + id = jhash_ip(map, i, *hash_ip) % map->hashsize; | |
3308 | + DP("hash key: %u", id); | |
3309 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
3310 | + if (*elem == *hash_ip) | |
3311 | + return id; | |
3312 | + /* No shortcut at testing - there can be deleted | |
3313 | + * entries. */ | |
3314 | + } | |
3315 | + return UINT_MAX; | |
3316 | +} | |
3317 | + | |
3318 | +static inline int | |
3319 | +__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3320 | +{ | |
3321 | + return (hash_id(set, ip, hash_ip) != UINT_MAX); | |
3322 | +} | |
3323 | + | |
3324 | +static int | |
3325 | +testip(struct ip_set *set, const void *data, size_t size, | |
3326 | + ip_set_ip_t *hash_ip) | |
3327 | +{ | |
3328 | + struct ip_set_req_iphash *req = | |
3329 | + (struct ip_set_req_iphash *) data; | |
3330 | + | |
3331 | + if (size != sizeof(struct ip_set_req_iphash)) { | |
3332 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3333 | + sizeof(struct ip_set_req_iphash), | |
3334 | + size); | |
3335 | + return -EINVAL; | |
3336 | + } | |
3337 | + return __testip(set, req->ip, hash_ip); | |
3338 | +} | |
3339 | + | |
3340 | +static int | |
3341 | +testip_kernel(struct ip_set *set, | |
3342 | + const struct sk_buff *skb, | |
3343 | + ip_set_ip_t *hash_ip, | |
3344 | + const u_int32_t *flags, | |
3345 | + unsigned char index) | |
3346 | +{ | |
3347 | + return __testip(set, | |
3348 | + ntohl(flags[index] & IPSET_SRC | |
3349 | + ? skb->nh.iph->saddr | |
3350 | + : skb->nh.iph->daddr), | |
3351 | + hash_ip); | |
3352 | +} | |
3353 | + | |
3354 | +static inline int | |
3355 | +__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3356 | +{ | |
3357 | + __u32 probe; | |
3358 | + u_int16_t i; | |
3359 | + ip_set_ip_t *elem; | |
3360 | + | |
3361 | + *hash_ip = ip & map->netmask; | |
3362 | + | |
3363 | + for (i = 0; i < map->probes; i++) { | |
3364 | + probe = jhash_ip(map, i, *hash_ip) % map->hashsize; | |
3365 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); | |
3366 | + if (*elem == *hash_ip) | |
3367 | + return -EEXIST; | |
3368 | + if (!*elem) { | |
3369 | + *elem = *hash_ip; | |
3370 | + return 0; | |
3371 | + } | |
3372 | + } | |
3373 | + /* Trigger rehashing */ | |
3374 | + return -EAGAIN; | |
3375 | +} | |
3376 | + | |
3377 | +static int | |
3378 | +addip(struct ip_set *set, const void *data, size_t size, | |
3379 | + ip_set_ip_t *hash_ip) | |
3380 | +{ | |
3381 | + struct ip_set_req_iphash *req = | |
3382 | + (struct ip_set_req_iphash *) data; | |
3383 | + | |
3384 | + if (size != sizeof(struct ip_set_req_iphash)) { | |
3385 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3386 | + sizeof(struct ip_set_req_iphash), | |
3387 | + size); | |
3388 | + return -EINVAL; | |
3389 | + } | |
3390 | + return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); | |
3391 | +} | |
3392 | + | |
3393 | +static int | |
3394 | +addip_kernel(struct ip_set *set, | |
3395 | + const struct sk_buff *skb, | |
3396 | + ip_set_ip_t *hash_ip, | |
3397 | + const u_int32_t *flags, | |
3398 | + unsigned char index) | |
3399 | +{ | |
3400 | + return __addip((struct ip_set_iphash *) set->data, | |
3401 | + ntohl(flags[index] & IPSET_SRC | |
3402 | + ? skb->nh.iph->saddr | |
3403 | + : skb->nh.iph->daddr), | |
3404 | + hash_ip); | |
3405 | +} | |
3406 | + | |
3407 | +static int retry(struct ip_set *set) | |
3408 | +{ | |
3409 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3410 | + ip_set_ip_t hash_ip, *elem; | |
3411 | + void *members; | |
3412 | + u_int32_t i, hashsize = map->hashsize; | |
3413 | + int res; | |
3414 | + struct ip_set_iphash *tmp; | |
3415 | + | |
3416 | + if (map->resize == 0) | |
3417 | + return -ERANGE; | |
3418 | + | |
3419 | + again: | |
3420 | + res = 0; | |
3421 | + | |
3422 | + /* Calculate new hash size */ | |
3423 | + hashsize += (hashsize * map->resize)/100; | |
3424 | + if (hashsize == map->hashsize) | |
3425 | + hashsize++; | |
3426 | + | |
3427 | + ip_set_printk("rehashing of set %s triggered: " | |
3428 | + "hashsize grows from %u to %u", | |
3429 | + set->name, map->hashsize, hashsize); | |
3430 | + | |
3431 | + tmp = kmalloc(sizeof(struct ip_set_iphash) | |
3432 | + + map->probes * sizeof(uint32_t), GFP_ATOMIC); | |
3433 | + if (!tmp) { | |
3434 | + DP("out of memory for %d bytes", | |
3435 | + sizeof(struct ip_set_iphash) | |
3436 | + + map->probes * sizeof(uint32_t)); | |
3437 | + return -ENOMEM; | |
3438 | + } | |
3439 | + tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); | |
3440 | + if (!tmp->members) { | |
3441 | + DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); | |
3442 | + kfree(tmp); | |
3443 | + return -ENOMEM; | |
3444 | + } | |
3445 | + tmp->hashsize = hashsize; | |
3446 | + tmp->probes = map->probes; | |
3447 | + tmp->resize = map->resize; | |
3448 | + tmp->netmask = map->netmask; | |
3449 | + memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); | |
3450 | + | |
3451 | + write_lock_bh(&set->lock); | |
3452 | + map = (struct ip_set_iphash *) set->data; /* Play safe */ | |
3453 | + for (i = 0; i < map->hashsize && res == 0; i++) { | |
3454 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
3455 | + if (*elem) | |
3456 | + res = __addip(tmp, *elem, &hash_ip); | |
3457 | + } | |
3458 | + if (res) { | |
3459 | + /* Failure, try again */ | |
3460 | + write_unlock_bh(&set->lock); | |
3461 | + harray_free(tmp->members); | |
3462 | + kfree(tmp); | |
3463 | + goto again; | |
3464 | + } | |
3465 | + | |
3466 | + /* Success at resizing! */ | |
3467 | + members = map->members; | |
3468 | + | |
3469 | + map->hashsize = tmp->hashsize; | |
3470 | + map->members = tmp->members; | |
3471 | + write_unlock_bh(&set->lock); | |
3472 | + | |
3473 | + harray_free(members); | |
3474 | + kfree(tmp); | |
3475 | + | |
3476 | + return 0; | |
3477 | +} | |
3478 | + | |
3479 | +static inline int | |
3480 | +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3481 | +{ | |
3482 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3483 | + ip_set_ip_t id = hash_id(set, ip, hash_ip); | |
3484 | + ip_set_ip_t *elem; | |
3485 | + | |
3486 | + if (id == UINT_MAX) | |
3487 | + return -EEXIST; | |
3488 | + | |
3489 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
3490 | + *elem = 0; | |
3491 | + | |
3492 | + return 0; | |
3493 | +} | |
3494 | + | |
3495 | +static int | |
3496 | +delip(struct ip_set *set, const void *data, size_t size, | |
3497 | + ip_set_ip_t *hash_ip) | |
3498 | +{ | |
3499 | + struct ip_set_req_iphash *req = | |
3500 | + (struct ip_set_req_iphash *) data; | |
3501 | + | |
3502 | + if (size != sizeof(struct ip_set_req_iphash)) { | |
3503 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3504 | + sizeof(struct ip_set_req_iphash), | |
3505 | + size); | |
3506 | + return -EINVAL; | |
3507 | + } | |
3508 | + return __delip(set, req->ip, hash_ip); | |
3509 | +} | |
3510 | + | |
3511 | +static int | |
3512 | +delip_kernel(struct ip_set *set, | |
3513 | + const struct sk_buff *skb, | |
3514 | + ip_set_ip_t *hash_ip, | |
3515 | + const u_int32_t *flags, | |
3516 | + unsigned char index) | |
3517 | +{ | |
3518 | + return __delip(set, | |
3519 | + ntohl(flags[index] & IPSET_SRC | |
3520 | + ? skb->nh.iph->saddr | |
3521 | + : skb->nh.iph->daddr), | |
3522 | + hash_ip); | |
3523 | +} | |
3524 | + | |
3525 | +static int create(struct ip_set *set, const void *data, size_t size) | |
3526 | +{ | |
3527 | + struct ip_set_req_iphash_create *req = | |
3528 | + (struct ip_set_req_iphash_create *) data; | |
3529 | + struct ip_set_iphash *map; | |
3530 | + uint16_t i; | |
3531 | + | |
3532 | + if (size != sizeof(struct ip_set_req_iphash_create)) { | |
3533 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3534 | + sizeof(struct ip_set_req_iphash_create), | |
3535 | + size); | |
3536 | + return -EINVAL; | |
3537 | + } | |
3538 | + | |
3539 | + if (req->hashsize < 1) { | |
3540 | + ip_set_printk("hashsize too small"); | |
3541 | + return -ENOEXEC; | |
3542 | + } | |
3543 | + | |
3544 | + if (req->probes < 1) { | |
3545 | + ip_set_printk("probes too small"); | |
3546 | + return -ENOEXEC; | |
3547 | + } | |
3548 | + | |
3549 | + map = kmalloc(sizeof(struct ip_set_iphash) | |
3550 | + + req->probes * sizeof(uint32_t), GFP_KERNEL); | |
3551 | + if (!map) { | |
3552 | + DP("out of memory for %d bytes", | |
3553 | + sizeof(struct ip_set_iphash) | |
3554 | + + req->probes * sizeof(uint32_t)); | |
3555 | + return -ENOMEM; | |
3556 | + } | |
3557 | + for (i = 0; i < req->probes; i++) | |
3558 | + get_random_bytes(((uint32_t *) map->initval)+i, 4); | |
3559 | + map->hashsize = req->hashsize; | |
3560 | + map->probes = req->probes; | |
3561 | + map->resize = req->resize; | |
3562 | + map->netmask = req->netmask; | |
3563 | + map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); | |
3564 | + if (!map->members) { | |
3565 | + DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); | |
3566 | + kfree(map); | |
3567 | + return -ENOMEM; | |
3568 | + } | |
3569 | + | |
3570 | + set->data = map; | |
3571 | + return 0; | |
3572 | +} | |
3573 | + | |
3574 | +static void destroy(struct ip_set *set) | |
3575 | +{ | |
3576 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3577 | + | |
3578 | + harray_free(map->members); | |
3579 | + kfree(map); | |
3580 | + | |
3581 | + set->data = NULL; | |
3582 | +} | |
3583 | + | |
3584 | +static void flush(struct ip_set *set) | |
3585 | +{ | |
3586 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3587 | + harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); | |
3588 | +} | |
3589 | + | |
3590 | +static void list_header(const struct ip_set *set, void *data) | |
3591 | +{ | |
3592 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3593 | + struct ip_set_req_iphash_create *header = | |
3594 | + (struct ip_set_req_iphash_create *) data; | |
3595 | + | |
3596 | + header->hashsize = map->hashsize; | |
3597 | + header->probes = map->probes; | |
3598 | + header->resize = map->resize; | |
3599 | + header->netmask = map->netmask; | |
3600 | +} | |
3601 | + | |
3602 | +static int list_members_size(const struct ip_set *set) | |
3603 | +{ | |
3604 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3605 | + | |
3606 | + return (map->hashsize * sizeof(ip_set_ip_t)); | |
3607 | +} | |
3608 | + | |
3609 | +static void list_members(const struct ip_set *set, void *data) | |
3610 | +{ | |
3611 | + struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; | |
3612 | + ip_set_ip_t i, *elem; | |
3613 | + | |
3614 | + for (i = 0; i < map->hashsize; i++) { | |
3615 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
3616 | + ((ip_set_ip_t *)data)[i] = *elem; | |
3617 | + } | |
3618 | +} | |
3619 | + | |
3620 | +static struct ip_set_type ip_set_iphash = { | |
3621 | + .typename = SETTYPE_NAME, | |
3622 | + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, | |
3623 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
3624 | + .create = &create, | |
3625 | + .destroy = &destroy, | |
3626 | + .flush = &flush, | |
3627 | + .reqsize = sizeof(struct ip_set_req_iphash), | |
3628 | + .addip = &addip, | |
3629 | + .addip_kernel = &addip_kernel, | |
3630 | + .retry = &retry, | |
3631 | + .delip = &delip, | |
3632 | + .delip_kernel = &delip_kernel, | |
3633 | + .testip = &testip, | |
3634 | + .testip_kernel = &testip_kernel, | |
3635 | + .header_size = sizeof(struct ip_set_req_iphash_create), | |
3636 | + .list_header = &list_header, | |
3637 | + .list_members_size = &list_members_size, | |
3638 | + .list_members = &list_members, | |
3639 | + .me = THIS_MODULE, | |
3640 | +}; | |
3641 | + | |
3642 | +MODULE_LICENSE("GPL"); | |
3643 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
3644 | +MODULE_DESCRIPTION("iphash type of IP sets"); | |
3645 | + | |
3646 | +static int __init init(void) | |
3647 | +{ | |
3648 | + return ip_set_register_set_type(&ip_set_iphash); | |
3649 | +} | |
3650 | + | |
3651 | +static void __exit fini(void) | |
3652 | +{ | |
3653 | + /* FIXME: possible race with ip_set_create() */ | |
3654 | + ip_set_unregister_set_type(&ip_set_iphash); | |
3655 | +} | |
3656 | + | |
3657 | +module_init(init); | |
3658 | +module_exit(fini); | |
3659 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_ipmap.c linux/net/ipv4/netfilter/ip_set_ipmap.c | |
3660 | --- linux.org/net/ipv4/netfilter/ip_set_ipmap.c 1970-01-01 01:00:00.000000000 +0100 | |
3661 | +++ linux/net/ipv4/netfilter/ip_set_ipmap.c 2006-05-04 10:26:33.000000000 +0200 | |
3662 | @@ -0,0 +1,327 @@ | |
3663 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
3664 | + * Patrick Schaaf <bof@bof.de> | |
3665 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
3666 | + * | |
3667 | + * This program is free software; you can redistribute it and/or modify | |
3668 | + * it under the terms of the GNU General Public License version 2 as | |
3669 | + * published by the Free Software Foundation. | |
3670 | + */ | |
3671 | + | |
3672 | +/* Kernel module implementing an IP set type: the single bitmap type */ | |
3673 | + | |
3674 | +#include <linux/module.h> | |
3675 | +#include <linux/ip.h> | |
3676 | +#include <linux/skbuff.h> | |
3677 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
3678 | +#include <linux/netfilter_ipv4/ip_set.h> | |
3679 | +#include <linux/errno.h> | |
3680 | +#include <asm/uaccess.h> | |
3681 | +#include <asm/bitops.h> | |
3682 | +#include <linux/spinlock.h> | |
3683 | + | |
3684 | +#include <linux/netfilter_ipv4/ip_set_ipmap.h> | |
3685 | + | |
3686 | +static inline ip_set_ip_t | |
3687 | +ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) | |
3688 | +{ | |
3689 | + return (ip - map->first_ip)/map->hosts; | |
3690 | +} | |
3691 | + | |
3692 | +static inline int | |
3693 | +__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3694 | +{ | |
3695 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3696 | + | |
3697 | + if (ip < map->first_ip || ip > map->last_ip) | |
3698 | + return -ERANGE; | |
3699 | + | |
3700 | + *hash_ip = ip & map->netmask; | |
3701 | + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", | |
3702 | + set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
3703 | + return !!test_bit(ip_to_id(map, *hash_ip), map->members); | |
3704 | +} | |
3705 | + | |
3706 | +static int | |
3707 | +testip(struct ip_set *set, const void *data, size_t size, | |
3708 | + ip_set_ip_t *hash_ip) | |
3709 | +{ | |
3710 | + struct ip_set_req_ipmap *req = | |
3711 | + (struct ip_set_req_ipmap *) data; | |
3712 | + | |
3713 | + if (size != sizeof(struct ip_set_req_ipmap)) { | |
3714 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3715 | + sizeof(struct ip_set_req_ipmap), | |
3716 | + size); | |
3717 | + return -EINVAL; | |
3718 | + } | |
3719 | + return __testip(set, req->ip, hash_ip); | |
3720 | +} | |
3721 | + | |
3722 | +static int | |
3723 | +testip_kernel(struct ip_set *set, | |
3724 | + const struct sk_buff *skb, | |
3725 | + ip_set_ip_t *hash_ip, | |
3726 | + const u_int32_t *flags, | |
3727 | + unsigned char index) | |
3728 | +{ | |
3729 | + int res; | |
3730 | + | |
3731 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
3732 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
3733 | + NIPQUAD(skb->nh.iph->saddr), | |
3734 | + NIPQUAD(skb->nh.iph->daddr)); | |
3735 | + | |
3736 | + res = __testip(set, | |
3737 | + ntohl(flags[index] & IPSET_SRC | |
3738 | + ? skb->nh.iph->saddr | |
3739 | + : skb->nh.iph->daddr), | |
3740 | + hash_ip); | |
3741 | + return (res < 0 ? 0 : res); | |
3742 | +} | |
3743 | + | |
3744 | +static inline int | |
3745 | +__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3746 | +{ | |
3747 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3748 | + | |
3749 | + if (ip < map->first_ip || ip > map->last_ip) | |
3750 | + return -ERANGE; | |
3751 | + | |
3752 | + *hash_ip = ip & map->netmask; | |
3753 | + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
3754 | + if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) | |
3755 | + return -EEXIST; | |
3756 | + | |
3757 | + return 0; | |
3758 | +} | |
3759 | + | |
3760 | +static int | |
3761 | +addip(struct ip_set *set, const void *data, size_t size, | |
3762 | + ip_set_ip_t *hash_ip) | |
3763 | +{ | |
3764 | + struct ip_set_req_ipmap *req = | |
3765 | + (struct ip_set_req_ipmap *) data; | |
3766 | + | |
3767 | + if (size != sizeof(struct ip_set_req_ipmap)) { | |
3768 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3769 | + sizeof(struct ip_set_req_ipmap), | |
3770 | + size); | |
3771 | + return -EINVAL; | |
3772 | + } | |
3773 | + DP("%u.%u.%u.%u", HIPQUAD(req->ip)); | |
3774 | + return __addip(set, req->ip, hash_ip); | |
3775 | +} | |
3776 | + | |
3777 | +static int | |
3778 | +addip_kernel(struct ip_set *set, | |
3779 | + const struct sk_buff *skb, | |
3780 | + ip_set_ip_t *hash_ip, | |
3781 | + const u_int32_t *flags, | |
3782 | + unsigned char index) | |
3783 | +{ | |
3784 | + return __addip(set, | |
3785 | + ntohl(flags[index] & IPSET_SRC | |
3786 | + ? skb->nh.iph->saddr | |
3787 | + : skb->nh.iph->daddr), | |
3788 | + hash_ip); | |
3789 | +} | |
3790 | + | |
3791 | +static inline int | |
3792 | +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
3793 | +{ | |
3794 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3795 | + | |
3796 | + if (ip < map->first_ip || ip > map->last_ip) | |
3797 | + return -ERANGE; | |
3798 | + | |
3799 | + *hash_ip = ip & map->netmask; | |
3800 | + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
3801 | + if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) | |
3802 | + return -EEXIST; | |
3803 | + | |
3804 | + return 0; | |
3805 | +} | |
3806 | + | |
3807 | +static int | |
3808 | +delip(struct ip_set *set, const void *data, size_t size, | |
3809 | + ip_set_ip_t *hash_ip) | |
3810 | +{ | |
3811 | + struct ip_set_req_ipmap *req = | |
3812 | + (struct ip_set_req_ipmap *) data; | |
3813 | + | |
3814 | + if (size != sizeof(struct ip_set_req_ipmap)) { | |
3815 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3816 | + sizeof(struct ip_set_req_ipmap), | |
3817 | + size); | |
3818 | + return -EINVAL; | |
3819 | + } | |
3820 | + return __delip(set, req->ip, hash_ip); | |
3821 | +} | |
3822 | + | |
3823 | +static int | |
3824 | +delip_kernel(struct ip_set *set, | |
3825 | + const struct sk_buff *skb, | |
3826 | + ip_set_ip_t *hash_ip, | |
3827 | + const u_int32_t *flags, | |
3828 | + unsigned char index) | |
3829 | +{ | |
3830 | + return __delip(set, | |
3831 | + ntohl(flags[index] & IPSET_SRC | |
3832 | + ? skb->nh.iph->saddr | |
3833 | + : skb->nh.iph->daddr), | |
3834 | + hash_ip); | |
3835 | +} | |
3836 | + | |
3837 | +static int create(struct ip_set *set, const void *data, size_t size) | |
3838 | +{ | |
3839 | + int newbytes; | |
3840 | + struct ip_set_req_ipmap_create *req = | |
3841 | + (struct ip_set_req_ipmap_create *) data; | |
3842 | + struct ip_set_ipmap *map; | |
3843 | + | |
3844 | + if (size != sizeof(struct ip_set_req_ipmap_create)) { | |
3845 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
3846 | + sizeof(struct ip_set_req_ipmap_create), | |
3847 | + size); | |
3848 | + return -EINVAL; | |
3849 | + } | |
3850 | + | |
3851 | + DP("from %u.%u.%u.%u to %u.%u.%u.%u", | |
3852 | + HIPQUAD(req->from), HIPQUAD(req->to)); | |
3853 | + | |
3854 | + if (req->from > req->to) { | |
3855 | + DP("bad ip range"); | |
3856 | + return -ENOEXEC; | |
3857 | + } | |
3858 | + | |
3859 | + map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); | |
3860 | + if (!map) { | |
3861 | + DP("out of memory for %d bytes", | |
3862 | + sizeof(struct ip_set_ipmap)); | |
3863 | + return -ENOMEM; | |
3864 | + } | |
3865 | + map->first_ip = req->from; | |
3866 | + map->last_ip = req->to; | |
3867 | + map->netmask = req->netmask; | |
3868 | + | |
3869 | + if (req->netmask == 0xFFFFFFFF) { | |
3870 | + map->hosts = 1; | |
3871 | + map->sizeid = map->last_ip - map->first_ip + 1; | |
3872 | + } else { | |
3873 | + unsigned int mask_bits, netmask_bits; | |
3874 | + ip_set_ip_t mask; | |
3875 | + | |
3876 | + map->first_ip &= map->netmask; /* Should we better bark? */ | |
3877 | + | |
3878 | + mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); | |
3879 | + netmask_bits = mask_to_bits(map->netmask); | |
3880 | + | |
3881 | + if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF)) | |
3882 | + || netmask_bits <= mask_bits) | |
3883 | + return -ENOEXEC; | |
3884 | + | |
3885 | + DP("mask_bits %u, netmask_bits %u", | |
3886 | + mask_bits, netmask_bits); | |
3887 | + map->hosts = 2 << (32 - netmask_bits - 1); | |
3888 | + map->sizeid = 2 << (netmask_bits - mask_bits - 1); | |
3889 | + } | |
3890 | + if (map->sizeid > MAX_RANGE + 1) { | |
3891 | + ip_set_printk("range too big (max %d addresses)", | |
3892 | + MAX_RANGE+1); | |
3893 | + kfree(map); | |
3894 | + return -ENOEXEC; | |
3895 | + } | |
3896 | + DP("hosts %u, sizeid %u", map->hosts, map->sizeid); | |
3897 | + newbytes = bitmap_bytes(0, map->sizeid - 1); | |
3898 | + map->members = kmalloc(newbytes, GFP_KERNEL); | |
3899 | + if (!map->members) { | |
3900 | + DP("out of memory for %d bytes", newbytes); | |
3901 | + kfree(map); | |
3902 | + return -ENOMEM; | |
3903 | + } | |
3904 | + memset(map->members, 0, newbytes); | |
3905 | + | |
3906 | + set->data = map; | |
3907 | + return 0; | |
3908 | +} | |
3909 | + | |
3910 | +static void destroy(struct ip_set *set) | |
3911 | +{ | |
3912 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3913 | + | |
3914 | + kfree(map->members); | |
3915 | + kfree(map); | |
3916 | + | |
3917 | + set->data = NULL; | |
3918 | +} | |
3919 | + | |
3920 | +static void flush(struct ip_set *set) | |
3921 | +{ | |
3922 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3923 | + memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); | |
3924 | +} | |
3925 | + | |
3926 | +static void list_header(const struct ip_set *set, void *data) | |
3927 | +{ | |
3928 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3929 | + struct ip_set_req_ipmap_create *header = | |
3930 | + (struct ip_set_req_ipmap_create *) data; | |
3931 | + | |
3932 | + header->from = map->first_ip; | |
3933 | + header->to = map->last_ip; | |
3934 | + header->netmask = map->netmask; | |
3935 | +} | |
3936 | + | |
3937 | +static int list_members_size(const struct ip_set *set) | |
3938 | +{ | |
3939 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3940 | + | |
3941 | + return bitmap_bytes(0, map->sizeid - 1); | |
3942 | +} | |
3943 | + | |
3944 | +static void list_members(const struct ip_set *set, void *data) | |
3945 | +{ | |
3946 | + struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; | |
3947 | + int bytes = bitmap_bytes(0, map->sizeid - 1); | |
3948 | + | |
3949 | + memcpy(data, map->members, bytes); | |
3950 | +} | |
3951 | + | |
3952 | +static struct ip_set_type ip_set_ipmap = { | |
3953 | + .typename = SETTYPE_NAME, | |
3954 | + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, | |
3955 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
3956 | + .create = &create, | |
3957 | + .destroy = &destroy, | |
3958 | + .flush = &flush, | |
3959 | + .reqsize = sizeof(struct ip_set_req_ipmap), | |
3960 | + .addip = &addip, | |
3961 | + .addip_kernel = &addip_kernel, | |
3962 | + .delip = &delip, | |
3963 | + .delip_kernel = &delip_kernel, | |
3964 | + .testip = &testip, | |
3965 | + .testip_kernel = &testip_kernel, | |
3966 | + .header_size = sizeof(struct ip_set_req_ipmap_create), | |
3967 | + .list_header = &list_header, | |
3968 | + .list_members_size = &list_members_size, | |
3969 | + .list_members = &list_members, | |
3970 | + .me = THIS_MODULE, | |
3971 | +}; | |
3972 | + | |
3973 | +MODULE_LICENSE("GPL"); | |
3974 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
3975 | +MODULE_DESCRIPTION("ipmap type of IP sets"); | |
3976 | + | |
3977 | +static int __init init(void) | |
3978 | +{ | |
3979 | + return ip_set_register_set_type(&ip_set_ipmap); | |
3980 | +} | |
3981 | + | |
3982 | +static void __exit fini(void) | |
3983 | +{ | |
3984 | + /* FIXME: possible race with ip_set_create() */ | |
3985 | + ip_set_unregister_set_type(&ip_set_ipmap); | |
3986 | +} | |
3987 | + | |
3988 | +module_init(init); | |
3989 | +module_exit(fini); | |
3990 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_ipporthash.c linux/net/ipv4/netfilter/ip_set_ipporthash.c | |
3991 | --- linux.org/net/ipv4/netfilter/ip_set_ipporthash.c 1970-01-01 01:00:00.000000000 +0100 | |
3992 | +++ linux/net/ipv4/netfilter/ip_set_ipporthash.c 2006-05-04 10:26:33.000000000 +0200 | |
3993 | @@ -0,0 +1,524 @@ | |
3994 | +/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
3995 | + * | |
3996 | + * This program is free software; you can redistribute it and/or modify | |
3997 | + * it under the terms of the GNU General Public License version 2 as | |
3998 | + * published by the Free Software Foundation. | |
3999 | + */ | |
4000 | + | |
4001 | +/* Kernel module implementing an ip+port hash set */ | |
4002 | + | |
4003 | +#include <linux/module.h> | |
4004 | +#include <linux/ip.h> | |
4005 | +#include <linux/tcp.h> | |
4006 | +#include <linux/udp.h> | |
4007 | +#include <linux/skbuff.h> | |
4008 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
4009 | +#include <linux/netfilter_ipv4/ip_set.h> | |
4010 | +#include <linux/errno.h> | |
4011 | +#include <asm/uaccess.h> | |
4012 | +#include <asm/bitops.h> | |
4013 | +#include <linux/spinlock.h> | |
4014 | +#include <linux/vmalloc.h> | |
4015 | +#include <linux/random.h> | |
4016 | + | |
4017 | +#include <net/ip.h> | |
4018 | + | |
4019 | +#include <linux/netfilter_ipv4/ip_set_malloc.h> | |
4020 | +#include <linux/netfilter_ipv4/ip_set_ipporthash.h> | |
4021 | +#include <linux/netfilter_ipv4/ip_set_jhash.h> | |
4022 | + | |
4023 | +/* We must handle non-linear skbs */ | |
4024 | +static inline ip_set_ip_t | |
4025 | +get_port(const struct sk_buff *skb, u_int32_t flags) | |
4026 | +{ | |
4027 | + struct iphdr *iph = skb->nh.iph; | |
4028 | + u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; | |
4029 | + | |
4030 | + switch (iph->protocol) { | |
4031 | + case IPPROTO_TCP: { | |
4032 | + struct tcphdr tcph; | |
4033 | + | |
4034 | + /* See comments at tcp_match in ip_tables.c */ | |
4035 | + if (offset) | |
4036 | + return INVALID_PORT; | |
4037 | + | |
4038 | + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) | |
4039 | + /* No choice either */ | |
4040 | + return INVALID_PORT; | |
4041 | + | |
4042 | + return ntohs(flags & IPSET_SRC ? | |
4043 | + tcph.source : tcph.dest); | |
4044 | + } | |
4045 | + case IPPROTO_UDP: { | |
4046 | + struct udphdr udph; | |
4047 | + | |
4048 | + if (offset) | |
4049 | + return INVALID_PORT; | |
4050 | + | |
4051 | + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) | |
4052 | + /* No choice either */ | |
4053 | + return INVALID_PORT; | |
4054 | + | |
4055 | + return ntohs(flags & IPSET_SRC ? | |
4056 | + udph.source : udph.dest); | |
4057 | + } | |
4058 | + default: | |
4059 | + return INVALID_PORT; | |
4060 | + } | |
4061 | +} | |
4062 | + | |
4063 | +static inline __u32 | |
4064 | +jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip) | |
4065 | +{ | |
4066 | + return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); | |
4067 | +} | |
4068 | + | |
4069 | +#define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16)) | |
4070 | + | |
4071 | +static inline __u32 | |
4072 | +hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, | |
4073 | + ip_set_ip_t *hash_ip) | |
4074 | +{ | |
4075 | + struct ip_set_ipporthash *map = | |
4076 | + (struct ip_set_ipporthash *) set->data; | |
4077 | + __u32 id; | |
4078 | + u_int16_t i; | |
4079 | + ip_set_ip_t *elem; | |
4080 | + | |
4081 | + *hash_ip = HASH_IP(map, ip, port); | |
4082 | + DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u", | |
4083 | + set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip)); | |
4084 | + | |
4085 | + for (i = 0; i < map->probes; i++) { | |
4086 | + id = jhash_ip(map, i, *hash_ip) % map->hashsize; | |
4087 | + DP("hash key: %u", id); | |
4088 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
4089 | + if (*elem == *hash_ip) | |
4090 | + return id; | |
4091 | + /* No shortcut at testing - there can be deleted | |
4092 | + * entries. */ | |
4093 | + } | |
4094 | + return UINT_MAX; | |
4095 | +} | |
4096 | + | |
4097 | +static inline int | |
4098 | +__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, | |
4099 | + ip_set_ip_t *hash_ip) | |
4100 | +{ | |
4101 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4102 | + | |
4103 | + if (ip < map->first_ip || ip > map->last_ip) | |
4104 | + return -ERANGE; | |
4105 | + | |
4106 | + return (hash_id(set, ip, port, hash_ip) != UINT_MAX); | |
4107 | +} | |
4108 | + | |
4109 | +static int | |
4110 | +testip(struct ip_set *set, const void *data, size_t size, | |
4111 | + ip_set_ip_t *hash_ip) | |
4112 | +{ | |
4113 | + struct ip_set_req_ipporthash *req = | |
4114 | + (struct ip_set_req_ipporthash *) data; | |
4115 | + | |
4116 | + if (size != sizeof(struct ip_set_req_ipporthash)) { | |
4117 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4118 | + sizeof(struct ip_set_req_ipporthash), | |
4119 | + size); | |
4120 | + return -EINVAL; | |
4121 | + } | |
4122 | + return __testip(set, req->ip, req->port, hash_ip); | |
4123 | +} | |
4124 | + | |
4125 | +static int | |
4126 | +testip_kernel(struct ip_set *set, | |
4127 | + const struct sk_buff *skb, | |
4128 | + ip_set_ip_t *hash_ip, | |
4129 | + const u_int32_t *flags, | |
4130 | + unsigned char index) | |
4131 | +{ | |
4132 | + ip_set_ip_t port; | |
4133 | + | |
4134 | + if (flags[index+1] == 0) | |
4135 | + return -EINVAL; | |
4136 | + | |
4137 | + port = get_port(skb, flags[index+1]); | |
4138 | + | |
4139 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
4140 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
4141 | + NIPQUAD(skb->nh.iph->saddr), | |
4142 | + NIPQUAD(skb->nh.iph->daddr)); | |
4143 | + DP("flag %s port %u", | |
4144 | + flags[index+1] & IPSET_SRC ? "SRC" : "DST", | |
4145 | + port); | |
4146 | + if (port == INVALID_PORT) | |
4147 | + return 0; | |
4148 | + | |
4149 | + return __testip(set, | |
4150 | + ntohl(flags[index] & IPSET_SRC | |
4151 | + ? skb->nh.iph->saddr | |
4152 | + : skb->nh.iph->daddr), | |
4153 | + port, | |
4154 | + hash_ip); | |
4155 | +} | |
4156 | + | |
4157 | +static inline int | |
4158 | +__add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip) | |
4159 | +{ | |
4160 | + __u32 probe; | |
4161 | + u_int16_t i; | |
4162 | + ip_set_ip_t *elem; | |
4163 | + | |
4164 | + for (i = 0; i < map->probes; i++) { | |
4165 | + probe = jhash_ip(map, i, hash_ip) % map->hashsize; | |
4166 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); | |
4167 | + if (*elem == hash_ip) | |
4168 | + return -EEXIST; | |
4169 | + if (!*elem) { | |
4170 | + *elem = hash_ip; | |
4171 | + return 0; | |
4172 | + } | |
4173 | + } | |
4174 | + /* Trigger rehashing */ | |
4175 | + return -EAGAIN; | |
4176 | +} | |
4177 | + | |
4178 | +static inline int | |
4179 | +__addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port, | |
4180 | + ip_set_ip_t *hash_ip) | |
4181 | +{ | |
4182 | + if (ip < map->first_ip || ip > map->last_ip) | |
4183 | + return -ERANGE; | |
4184 | + | |
4185 | + *hash_ip = HASH_IP(map, ip, port); | |
4186 | + | |
4187 | + return __add_haship(map, *hash_ip); | |
4188 | +} | |
4189 | + | |
4190 | +static int | |
4191 | +addip(struct ip_set *set, const void *data, size_t size, | |
4192 | + ip_set_ip_t *hash_ip) | |
4193 | +{ | |
4194 | + struct ip_set_req_ipporthash *req = | |
4195 | + (struct ip_set_req_ipporthash *) data; | |
4196 | + | |
4197 | + if (size != sizeof(struct ip_set_req_ipporthash)) { | |
4198 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4199 | + sizeof(struct ip_set_req_ipporthash), | |
4200 | + size); | |
4201 | + return -EINVAL; | |
4202 | + } | |
4203 | + return __addip((struct ip_set_ipporthash *) set->data, | |
4204 | + req->ip, req->port, hash_ip); | |
4205 | +} | |
4206 | + | |
4207 | +static int | |
4208 | +addip_kernel(struct ip_set *set, | |
4209 | + const struct sk_buff *skb, | |
4210 | + ip_set_ip_t *hash_ip, | |
4211 | + const u_int32_t *flags, | |
4212 | + unsigned char index) | |
4213 | +{ | |
4214 | + ip_set_ip_t port; | |
4215 | + | |
4216 | + if (flags[index+1] == 0) | |
4217 | + return -EINVAL; | |
4218 | + | |
4219 | + port = get_port(skb, flags[index+1]); | |
4220 | + | |
4221 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
4222 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
4223 | + NIPQUAD(skb->nh.iph->saddr), | |
4224 | + NIPQUAD(skb->nh.iph->daddr)); | |
4225 | + DP("flag %s port %u", | |
4226 | + flags[index+1] & IPSET_SRC ? "SRC" : "DST", | |
4227 | + port); | |
4228 | + if (port == INVALID_PORT) | |
4229 | + return -EINVAL; | |
4230 | + | |
4231 | + return __addip((struct ip_set_ipporthash *) set->data, | |
4232 | + ntohl(flags[index] & IPSET_SRC | |
4233 | + ? skb->nh.iph->saddr | |
4234 | + : skb->nh.iph->daddr), | |
4235 | + port, | |
4236 | + hash_ip); | |
4237 | +} | |
4238 | + | |
4239 | +static int retry(struct ip_set *set) | |
4240 | +{ | |
4241 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4242 | + ip_set_ip_t *elem; | |
4243 | + void *members; | |
4244 | + u_int32_t i, hashsize = map->hashsize; | |
4245 | + int res; | |
4246 | + struct ip_set_ipporthash *tmp; | |
4247 | + | |
4248 | + if (map->resize == 0) | |
4249 | + return -ERANGE; | |
4250 | + | |
4251 | + again: | |
4252 | + res = 0; | |
4253 | + | |
4254 | + /* Calculate new hash size */ | |
4255 | + hashsize += (hashsize * map->resize)/100; | |
4256 | + if (hashsize == map->hashsize) | |
4257 | + hashsize++; | |
4258 | + | |
4259 | + ip_set_printk("rehashing of set %s triggered: " | |
4260 | + "hashsize grows from %u to %u", | |
4261 | + set->name, map->hashsize, hashsize); | |
4262 | + | |
4263 | + tmp = kmalloc(sizeof(struct ip_set_ipporthash) | |
4264 | + + map->probes * sizeof(uint32_t), GFP_ATOMIC); | |
4265 | + if (!tmp) { | |
4266 | + DP("out of memory for %d bytes", | |
4267 | + sizeof(struct ip_set_ipporthash) | |
4268 | + + map->probes * sizeof(uint32_t)); | |
4269 | + return -ENOMEM; | |
4270 | + } | |
4271 | + tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); | |
4272 | + if (!tmp->members) { | |
4273 | + DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); | |
4274 | + kfree(tmp); | |
4275 | + return -ENOMEM; | |
4276 | + } | |
4277 | + tmp->hashsize = hashsize; | |
4278 | + tmp->probes = map->probes; | |
4279 | + tmp->resize = map->resize; | |
4280 | + tmp->first_ip = map->first_ip; | |
4281 | + tmp->last_ip = map->last_ip; | |
4282 | + memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); | |
4283 | + | |
4284 | + write_lock_bh(&set->lock); | |
4285 | + map = (struct ip_set_ipporthash *) set->data; /* Play safe */ | |
4286 | + for (i = 0; i < map->hashsize && res == 0; i++) { | |
4287 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
4288 | + if (*elem) | |
4289 | + res = __add_haship(tmp, *elem); | |
4290 | + } | |
4291 | + if (res) { | |
4292 | + /* Failure, try again */ | |
4293 | + write_unlock_bh(&set->lock); | |
4294 | + harray_free(tmp->members); | |
4295 | + kfree(tmp); | |
4296 | + goto again; | |
4297 | + } | |
4298 | + | |
4299 | + /* Success at resizing! */ | |
4300 | + members = map->members; | |
4301 | + | |
4302 | + map->hashsize = tmp->hashsize; | |
4303 | + map->members = tmp->members; | |
4304 | + write_unlock_bh(&set->lock); | |
4305 | + | |
4306 | + harray_free(members); | |
4307 | + kfree(tmp); | |
4308 | + | |
4309 | + return 0; | |
4310 | +} | |
4311 | + | |
4312 | +static inline int | |
4313 | +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, | |
4314 | + ip_set_ip_t *hash_ip) | |
4315 | +{ | |
4316 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4317 | + ip_set_ip_t id; | |
4318 | + ip_set_ip_t *elem; | |
4319 | + | |
4320 | + if (ip < map->first_ip || ip > map->last_ip) | |
4321 | + return -ERANGE; | |
4322 | + | |
4323 | + id = hash_id(set, ip, port, hash_ip); | |
4324 | + | |
4325 | + if (id == UINT_MAX) | |
4326 | + return -EEXIST; | |
4327 | + | |
4328 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
4329 | + *elem = 0; | |
4330 | + | |
4331 | + return 0; | |
4332 | +} | |
4333 | + | |
4334 | +static int | |
4335 | +delip(struct ip_set *set, const void *data, size_t size, | |
4336 | + ip_set_ip_t *hash_ip) | |
4337 | +{ | |
4338 | + struct ip_set_req_ipporthash *req = | |
4339 | + (struct ip_set_req_ipporthash *) data; | |
4340 | + | |
4341 | + if (size != sizeof(struct ip_set_req_ipporthash)) { | |
4342 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4343 | + sizeof(struct ip_set_req_ipporthash), | |
4344 | + size); | |
4345 | + return -EINVAL; | |
4346 | + } | |
4347 | + return __delip(set, req->ip, req->port, hash_ip); | |
4348 | +} | |
4349 | + | |
4350 | +static int | |
4351 | +delip_kernel(struct ip_set *set, | |
4352 | + const struct sk_buff *skb, | |
4353 | + ip_set_ip_t *hash_ip, | |
4354 | + const u_int32_t *flags, | |
4355 | + unsigned char index) | |
4356 | +{ | |
4357 | + ip_set_ip_t port; | |
4358 | + | |
4359 | + if (flags[index+1] == 0) | |
4360 | + return -EINVAL; | |
4361 | + | |
4362 | + port = get_port(skb, flags[index+1]); | |
4363 | + | |
4364 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
4365 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
4366 | + NIPQUAD(skb->nh.iph->saddr), | |
4367 | + NIPQUAD(skb->nh.iph->daddr)); | |
4368 | + DP("flag %s port %u", | |
4369 | + flags[index+1] & IPSET_SRC ? "SRC" : "DST", | |
4370 | + port); | |
4371 | + if (port == INVALID_PORT) | |
4372 | + return -EINVAL; | |
4373 | + | |
4374 | + return __delip(set, | |
4375 | + ntohl(flags[index] & IPSET_SRC | |
4376 | + ? skb->nh.iph->saddr | |
4377 | + : skb->nh.iph->daddr), | |
4378 | + port, | |
4379 | + hash_ip); | |
4380 | +} | |
4381 | + | |
4382 | +static int create(struct ip_set *set, const void *data, size_t size) | |
4383 | +{ | |
4384 | + struct ip_set_req_ipporthash_create *req = | |
4385 | + (struct ip_set_req_ipporthash_create *) data; | |
4386 | + struct ip_set_ipporthash *map; | |
4387 | + uint16_t i; | |
4388 | + | |
4389 | + if (size != sizeof(struct ip_set_req_ipporthash_create)) { | |
4390 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4391 | + sizeof(struct ip_set_req_ipporthash_create), | |
4392 | + size); | |
4393 | + return -EINVAL; | |
4394 | + } | |
4395 | + | |
4396 | + if (req->hashsize < 1) { | |
4397 | + ip_set_printk("hashsize too small"); | |
4398 | + return -ENOEXEC; | |
4399 | + } | |
4400 | + | |
4401 | + if (req->probes < 1) { | |
4402 | + ip_set_printk("probes too small"); | |
4403 | + return -ENOEXEC; | |
4404 | + } | |
4405 | + | |
4406 | + map = kmalloc(sizeof(struct ip_set_ipporthash) | |
4407 | + + req->probes * sizeof(uint32_t), GFP_KERNEL); | |
4408 | + if (!map) { | |
4409 | + DP("out of memory for %d bytes", | |
4410 | + sizeof(struct ip_set_ipporthash) | |
4411 | + + req->probes * sizeof(uint32_t)); | |
4412 | + return -ENOMEM; | |
4413 | + } | |
4414 | + for (i = 0; i < req->probes; i++) | |
4415 | + get_random_bytes(((uint32_t *) map->initval)+i, 4); | |
4416 | + map->hashsize = req->hashsize; | |
4417 | + map->probes = req->probes; | |
4418 | + map->resize = req->resize; | |
4419 | + map->first_ip = req->from; | |
4420 | + map->last_ip = req->to; | |
4421 | + map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); | |
4422 | + if (!map->members) { | |
4423 | + DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); | |
4424 | + kfree(map); | |
4425 | + return -ENOMEM; | |
4426 | + } | |
4427 | + | |
4428 | + set->data = map; | |
4429 | + return 0; | |
4430 | +} | |
4431 | + | |
4432 | +static void destroy(struct ip_set *set) | |
4433 | +{ | |
4434 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4435 | + | |
4436 | + harray_free(map->members); | |
4437 | + kfree(map); | |
4438 | + | |
4439 | + set->data = NULL; | |
4440 | +} | |
4441 | + | |
4442 | +static void flush(struct ip_set *set) | |
4443 | +{ | |
4444 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4445 | + harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); | |
4446 | +} | |
4447 | + | |
4448 | +static void list_header(const struct ip_set *set, void *data) | |
4449 | +{ | |
4450 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4451 | + struct ip_set_req_ipporthash_create *header = | |
4452 | + (struct ip_set_req_ipporthash_create *) data; | |
4453 | + | |
4454 | + header->hashsize = map->hashsize; | |
4455 | + header->probes = map->probes; | |
4456 | + header->resize = map->resize; | |
4457 | + header->from = map->first_ip; | |
4458 | + header->to = map->last_ip; | |
4459 | +} | |
4460 | + | |
4461 | +static int list_members_size(const struct ip_set *set) | |
4462 | +{ | |
4463 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4464 | + | |
4465 | + return (map->hashsize * sizeof(ip_set_ip_t)); | |
4466 | +} | |
4467 | + | |
4468 | +static void list_members(const struct ip_set *set, void *data) | |
4469 | +{ | |
4470 | + struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; | |
4471 | + ip_set_ip_t i, *elem; | |
4472 | + | |
4473 | + for (i = 0; i < map->hashsize; i++) { | |
4474 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
4475 | + ((ip_set_ip_t *)data)[i] = *elem; | |
4476 | + } | |
4477 | +} | |
4478 | + | |
4479 | +static struct ip_set_type ip_set_ipporthash = { | |
4480 | + .typename = SETTYPE_NAME, | |
4481 | + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE, | |
4482 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
4483 | + .create = &create, | |
4484 | + .destroy = &destroy, | |
4485 | + .flush = &flush, | |
4486 | + .reqsize = sizeof(struct ip_set_req_ipporthash), | |
4487 | + .addip = &addip, | |
4488 | + .addip_kernel = &addip_kernel, | |
4489 | + .retry = &retry, | |
4490 | + .delip = &delip, | |
4491 | + .delip_kernel = &delip_kernel, | |
4492 | + .testip = &testip, | |
4493 | + .testip_kernel = &testip_kernel, | |
4494 | + .header_size = sizeof(struct ip_set_req_ipporthash_create), | |
4495 | + .list_header = &list_header, | |
4496 | + .list_members_size = &list_members_size, | |
4497 | + .list_members = &list_members, | |
4498 | + .me = THIS_MODULE, | |
4499 | +}; | |
4500 | + | |
4501 | +MODULE_LICENSE("GPL"); | |
4502 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
4503 | +MODULE_DESCRIPTION("ipporthash type of IP sets"); | |
4504 | + | |
4505 | +static int __init init(void) | |
4506 | +{ | |
4507 | + return ip_set_register_set_type(&ip_set_ipporthash); | |
4508 | +} | |
4509 | + | |
4510 | +static void __exit fini(void) | |
4511 | +{ | |
4512 | + /* FIXME: possible race with ip_set_create() */ | |
4513 | + ip_set_unregister_set_type(&ip_set_ipporthash); | |
4514 | +} | |
4515 | + | |
4516 | +module_init(init); | |
4517 | +module_exit(fini); | |
4518 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_iptree.c linux/net/ipv4/netfilter/ip_set_iptree.c | |
4519 | --- linux.org/net/ipv4/netfilter/ip_set_iptree.c 1970-01-01 01:00:00.000000000 +0100 | |
4520 | +++ linux/net/ipv4/netfilter/ip_set_iptree.c 2006-05-04 10:26:33.000000000 +0200 | |
4521 | @@ -0,0 +1,544 @@ | |
4522 | +/* Copyright (C) 2005 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
4523 | + * | |
4524 | + * This program is free software; you can redistribute it and/or modify | |
4525 | + * it under the terms of the GNU General Public License version 2 as | |
4526 | + * published by the Free Software Foundation. | |
4527 | + */ | |
4528 | + | |
4529 | +/* Kernel module implementing an IP set type: the iptree type */ | |
4530 | + | |
4531 | +#include <linux/module.h> | |
4532 | +#include <linux/ip.h> | |
4533 | +#include <linux/skbuff.h> | |
4534 | +#include <linux/slab.h> | |
4535 | +#include <linux/delay.h> | |
4536 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
4537 | +#include <linux/netfilter_ipv4/ip_set.h> | |
4538 | +#include <linux/errno.h> | |
4539 | +#include <asm/uaccess.h> | |
4540 | +#include <asm/bitops.h> | |
4541 | +#include <linux/spinlock.h> | |
4542 | + | |
4543 | +/* Backward compatibility */ | |
4544 | +#ifndef __nocast | |
4545 | +#define __nocast | |
4546 | +#endif | |
4547 | + | |
4548 | +#include <linux/netfilter_ipv4/ip_set_iptree.h> | |
4549 | + | |
4550 | +/* Garbage collection interval in seconds: */ | |
4551 | +#define IPTREE_GC_TIME 5*60 | |
4552 | +/* Sleep so many milliseconds before trying again | |
4553 | + * to delete the gc timer at destroying/flushing a set */ | |
4554 | +#define IPTREE_DESTROY_SLEEP 100 | |
4555 | + | |
4556 | +static kmem_cache_t *branch_cachep; | |
4557 | +static kmem_cache_t *leaf_cachep; | |
4558 | + | |
4559 | +#define ABCD(a,b,c,d,addrp) do { \ | |
4560 | + a = ((unsigned char *)addrp)[3]; \ | |
4561 | + b = ((unsigned char *)addrp)[2]; \ | |
4562 | + c = ((unsigned char *)addrp)[1]; \ | |
4563 | + d = ((unsigned char *)addrp)[0]; \ | |
4564 | +} while (0) | |
4565 | + | |
4566 | +#define TESTIP_WALK(map, elem, branch) do { \ | |
4567 | + if ((map)->tree[elem]) { \ | |
4568 | + branch = (map)->tree[elem]; \ | |
4569 | + } else \ | |
4570 | + return 0; \ | |
4571 | +} while (0) | |
4572 | + | |
4573 | +static inline int | |
4574 | +__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
4575 | +{ | |
4576 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4577 | + struct ip_set_iptreeb *btree; | |
4578 | + struct ip_set_iptreec *ctree; | |
4579 | + struct ip_set_iptreed *dtree; | |
4580 | + unsigned char a,b,c,d; | |
4581 | + | |
4582 | + *hash_ip = ip; | |
4583 | + ABCD(a, b, c, d, hash_ip); | |
4584 | + DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); | |
4585 | + TESTIP_WALK(map, a, btree); | |
4586 | + TESTIP_WALK(btree, b, ctree); | |
4587 | + TESTIP_WALK(ctree, c, dtree); | |
4588 | + DP("%lu %lu", dtree->expires[d], jiffies); | |
4589 | + return !!(map->timeout ? (time_after(dtree->expires[d], jiffies)) | |
4590 | + : dtree->expires[d]); | |
4591 | +} | |
4592 | + | |
4593 | +static int | |
4594 | +testip(struct ip_set *set, const void *data, size_t size, | |
4595 | + ip_set_ip_t *hash_ip) | |
4596 | +{ | |
4597 | + struct ip_set_req_iptree *req = | |
4598 | + (struct ip_set_req_iptree *) data; | |
4599 | + | |
4600 | + if (size != sizeof(struct ip_set_req_iptree)) { | |
4601 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4602 | + sizeof(struct ip_set_req_iptree), | |
4603 | + size); | |
4604 | + return -EINVAL; | |
4605 | + } | |
4606 | + return __testip(set, req->ip, hash_ip); | |
4607 | +} | |
4608 | + | |
4609 | +static int | |
4610 | +testip_kernel(struct ip_set *set, | |
4611 | + const struct sk_buff *skb, | |
4612 | + ip_set_ip_t *hash_ip, | |
4613 | + const u_int32_t *flags, | |
4614 | + unsigned char index) | |
4615 | +{ | |
4616 | + int res; | |
4617 | + | |
4618 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
4619 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
4620 | + NIPQUAD(skb->nh.iph->saddr), | |
4621 | + NIPQUAD(skb->nh.iph->daddr)); | |
4622 | + | |
4623 | + res = __testip(set, | |
4624 | + ntohl(flags[index] & IPSET_SRC | |
4625 | + ? skb->nh.iph->saddr | |
4626 | + : skb->nh.iph->daddr), | |
4627 | + hash_ip); | |
4628 | + return (res < 0 ? 0 : res); | |
4629 | +} | |
4630 | + | |
4631 | +#define ADDIP_WALK(map, elem, branch, type, cachep, flags) do { \ | |
4632 | + if ((map)->tree[elem]) { \ | |
4633 | + DP("found %u", elem); \ | |
4634 | + branch = (map)->tree[elem]; \ | |
4635 | + } else { \ | |
4636 | + branch = (type *) \ | |
4637 | + kmem_cache_alloc(cachep, flags); \ | |
4638 | + if (branch == NULL) \ | |
4639 | + return -ENOMEM; \ | |
4640 | + memset(branch, 0, sizeof(*branch)); \ | |
4641 | + (map)->tree[elem] = branch; \ | |
4642 | + DP("alloc %u", elem); \ | |
4643 | + } \ | |
4644 | +} while (0) | |
4645 | + | |
4646 | +static inline int | |
4647 | +__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, | |
4648 | + ip_set_ip_t *hash_ip, | |
4649 | + unsigned int __nocast flags) | |
4650 | +{ | |
4651 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4652 | + struct ip_set_iptreeb *btree; | |
4653 | + struct ip_set_iptreec *ctree; | |
4654 | + struct ip_set_iptreed *dtree; | |
4655 | + unsigned char a,b,c,d; | |
4656 | + int ret = 0; | |
4657 | + | |
4658 | + *hash_ip = ip; | |
4659 | + ABCD(a, b, c, d, hash_ip); | |
4660 | + DP("%u %u %u %u timeout %u", a, b, c, d, timeout); | |
4661 | + ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep, flags); | |
4662 | + ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep, flags); | |
4663 | + ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep, flags); | |
4664 | + if (dtree->expires[d] | |
4665 | + && (!map->timeout || time_after(dtree->expires[d], jiffies))) | |
4666 | + ret = -EEXIST; | |
4667 | + dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; | |
4668 | + /* Lottery */ | |
4669 | + if (dtree->expires[d] == 0) | |
4670 | + dtree->expires[d] = 1; | |
4671 | + DP("%u %lu", d, dtree->expires[d]); | |
4672 | + return ret; | |
4673 | +} | |
4674 | + | |
4675 | +static int | |
4676 | +addip(struct ip_set *set, const void *data, size_t size, | |
4677 | + ip_set_ip_t *hash_ip) | |
4678 | +{ | |
4679 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4680 | + struct ip_set_req_iptree *req = | |
4681 | + (struct ip_set_req_iptree *) data; | |
4682 | + | |
4683 | + if (size != sizeof(struct ip_set_req_iptree)) { | |
4684 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4685 | + sizeof(struct ip_set_req_iptree), | |
4686 | + size); | |
4687 | + return -EINVAL; | |
4688 | + } | |
4689 | + DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); | |
4690 | + return __addip(set, req->ip, | |
4691 | + req->timeout ? req->timeout : map->timeout, | |
4692 | + hash_ip, | |
4693 | + GFP_ATOMIC); | |
4694 | +} | |
4695 | + | |
4696 | +static int | |
4697 | +addip_kernel(struct ip_set *set, | |
4698 | + const struct sk_buff *skb, | |
4699 | + ip_set_ip_t *hash_ip, | |
4700 | + const u_int32_t *flags, | |
4701 | + unsigned char index) | |
4702 | +{ | |
4703 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4704 | + | |
4705 | + return __addip(set, | |
4706 | + ntohl(flags[index] & IPSET_SRC | |
4707 | + ? skb->nh.iph->saddr | |
4708 | + : skb->nh.iph->daddr), | |
4709 | + map->timeout, | |
4710 | + hash_ip, | |
4711 | + GFP_ATOMIC); | |
4712 | +} | |
4713 | + | |
4714 | +#define DELIP_WALK(map, elem, branch) do { \ | |
4715 | + if ((map)->tree[elem]) { \ | |
4716 | + branch = (map)->tree[elem]; \ | |
4717 | + } else \ | |
4718 | + return -EEXIST; \ | |
4719 | +} while (0) | |
4720 | + | |
4721 | +static inline int | |
4722 | +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
4723 | +{ | |
4724 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4725 | + struct ip_set_iptreeb *btree; | |
4726 | + struct ip_set_iptreec *ctree; | |
4727 | + struct ip_set_iptreed *dtree; | |
4728 | + unsigned char a,b,c,d; | |
4729 | + | |
4730 | + *hash_ip = ip; | |
4731 | + ABCD(a, b, c, d, hash_ip); | |
4732 | + DELIP_WALK(map, a, btree); | |
4733 | + DELIP_WALK(btree, b, ctree); | |
4734 | + DELIP_WALK(ctree, c, dtree); | |
4735 | + | |
4736 | + if (dtree->expires[d]) { | |
4737 | + dtree->expires[d] = 0; | |
4738 | + return 0; | |
4739 | + } | |
4740 | + return -EEXIST; | |
4741 | +} | |
4742 | + | |
4743 | +static int | |
4744 | +delip(struct ip_set *set, const void *data, size_t size, | |
4745 | + ip_set_ip_t *hash_ip) | |
4746 | +{ | |
4747 | + struct ip_set_req_iptree *req = | |
4748 | + (struct ip_set_req_iptree *) data; | |
4749 | + | |
4750 | + if (size != sizeof(struct ip_set_req_iptree)) { | |
4751 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4752 | + sizeof(struct ip_set_req_iptree), | |
4753 | + size); | |
4754 | + return -EINVAL; | |
4755 | + } | |
4756 | + return __delip(set, req->ip, hash_ip); | |
4757 | +} | |
4758 | + | |
4759 | +static int | |
4760 | +delip_kernel(struct ip_set *set, | |
4761 | + const struct sk_buff *skb, | |
4762 | + ip_set_ip_t *hash_ip, | |
4763 | + const u_int32_t *flags, | |
4764 | + unsigned char index) | |
4765 | +{ | |
4766 | + return __delip(set, | |
4767 | + ntohl(flags[index] & IPSET_SRC | |
4768 | + ? skb->nh.iph->saddr | |
4769 | + : skb->nh.iph->daddr), | |
4770 | + hash_ip); | |
4771 | +} | |
4772 | + | |
4773 | +#define LOOP_WALK_BEGIN(map, i, branch) \ | |
4774 | + for (i = 0; i < 256; i++) { \ | |
4775 | + if (!(map)->tree[i]) \ | |
4776 | + continue; \ | |
4777 | + branch = (map)->tree[i] | |
4778 | + | |
4779 | +#define LOOP_WALK_END } | |
4780 | + | |
4781 | +static void ip_tree_gc(unsigned long ul_set) | |
4782 | +{ | |
4783 | + struct ip_set *set = (void *) ul_set; | |
4784 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4785 | + struct ip_set_iptreeb *btree; | |
4786 | + struct ip_set_iptreec *ctree; | |
4787 | + struct ip_set_iptreed *dtree; | |
4788 | + unsigned int a,b,c,d; | |
4789 | + unsigned char i,j,k; | |
4790 | + | |
4791 | + i = j = k = 0; | |
4792 | + DP("gc: %s", set->name); | |
4793 | + write_lock_bh(&set->lock); | |
4794 | + LOOP_WALK_BEGIN(map, a, btree); | |
4795 | + LOOP_WALK_BEGIN(btree, b, ctree); | |
4796 | + LOOP_WALK_BEGIN(ctree, c, dtree); | |
4797 | + for (d = 0; d < 256; d++) { | |
4798 | + if (dtree->expires[d]) { | |
4799 | + DP("gc: %u %u %u %u: expires %lu jiffies %lu", | |
4800 | + a, b, c, d, | |
4801 | + dtree->expires[d], jiffies); | |
4802 | + if (map->timeout | |
4803 | + && time_before(dtree->expires[d], jiffies)) | |
4804 | + dtree->expires[d] = 0; | |
4805 | + else | |
4806 | + k = 1; | |
4807 | + } | |
4808 | + } | |
4809 | + if (k == 0) { | |
4810 | + DP("gc: %s: leaf %u %u %u empty", | |
4811 | + set->name, a, b, c); | |
4812 | + kmem_cache_free(leaf_cachep, dtree); | |
4813 | + ctree->tree[c] = NULL; | |
4814 | + } else { | |
4815 | + DP("gc: %s: leaf %u %u %u not empty", | |
4816 | + set->name, a, b, c); | |
4817 | + j = 1; | |
4818 | + k = 0; | |
4819 | + } | |
4820 | + LOOP_WALK_END; | |
4821 | + if (j == 0) { | |
4822 | + DP("gc: %s: branch %u %u empty", | |
4823 | + set->name, a, b); | |
4824 | + kmem_cache_free(branch_cachep, ctree); | |
4825 | + btree->tree[b] = NULL; | |
4826 | + } else { | |
4827 | + DP("gc: %s: branch %u %u not empty", | |
4828 | + set->name, a, b); | |
4829 | + i = 1; | |
4830 | + j = k = 0; | |
4831 | + } | |
4832 | + LOOP_WALK_END; | |
4833 | + if (i == 0) { | |
4834 | + DP("gc: %s: branch %u empty", | |
4835 | + set->name, a); | |
4836 | + kmem_cache_free(branch_cachep, btree); | |
4837 | + map->tree[a] = NULL; | |
4838 | + } else { | |
4839 | + DP("gc: %s: branch %u not empty", | |
4840 | + set->name, a); | |
4841 | + i = j = k = 0; | |
4842 | + } | |
4843 | + LOOP_WALK_END; | |
4844 | + write_unlock_bh(&set->lock); | |
4845 | + | |
4846 | + map->gc.expires = jiffies + map->gc_interval * HZ; | |
4847 | + add_timer(&map->gc); | |
4848 | +} | |
4849 | + | |
4850 | +static inline void init_gc_timer(struct ip_set *set) | |
4851 | +{ | |
4852 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4853 | + | |
4854 | + /* Even if there is no timeout for the entries, | |
4855 | + * we still have to call gc because delete | |
4856 | + * do not clean up empty branches */ | |
4857 | + map->gc_interval = IPTREE_GC_TIME; | |
4858 | + init_timer(&map->gc); | |
4859 | + map->gc.data = (unsigned long) set; | |
4860 | + map->gc.function = ip_tree_gc; | |
4861 | + map->gc.expires = jiffies + map->gc_interval * HZ; | |
4862 | + add_timer(&map->gc); | |
4863 | +} | |
4864 | + | |
4865 | +static int create(struct ip_set *set, const void *data, size_t size) | |
4866 | +{ | |
4867 | + struct ip_set_req_iptree_create *req = | |
4868 | + (struct ip_set_req_iptree_create *) data; | |
4869 | + struct ip_set_iptree *map; | |
4870 | + | |
4871 | + if (size != sizeof(struct ip_set_req_iptree_create)) { | |
4872 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
4873 | + sizeof(struct ip_set_req_iptree_create), | |
4874 | + size); | |
4875 | + return -EINVAL; | |
4876 | + } | |
4877 | + | |
4878 | + map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); | |
4879 | + if (!map) { | |
4880 | + DP("out of memory for %d bytes", | |
4881 | + sizeof(struct ip_set_iptree)); | |
4882 | + return -ENOMEM; | |
4883 | + } | |
4884 | + memset(map, 0, sizeof(*map)); | |
4885 | + map->timeout = req->timeout; | |
4886 | + set->data = map; | |
4887 | + | |
4888 | + init_gc_timer(set); | |
4889 | + | |
4890 | + return 0; | |
4891 | +} | |
4892 | + | |
4893 | +static void __flush(struct ip_set_iptree *map) | |
4894 | +{ | |
4895 | + struct ip_set_iptreeb *btree; | |
4896 | + struct ip_set_iptreec *ctree; | |
4897 | + struct ip_set_iptreed *dtree; | |
4898 | + unsigned int a,b,c; | |
4899 | + | |
4900 | + LOOP_WALK_BEGIN(map, a, btree); | |
4901 | + LOOP_WALK_BEGIN(btree, b, ctree); | |
4902 | + LOOP_WALK_BEGIN(ctree, c, dtree); | |
4903 | + kmem_cache_free(leaf_cachep, dtree); | |
4904 | + LOOP_WALK_END; | |
4905 | + kmem_cache_free(branch_cachep, ctree); | |
4906 | + LOOP_WALK_END; | |
4907 | + kmem_cache_free(branch_cachep, btree); | |
4908 | + LOOP_WALK_END; | |
4909 | +} | |
4910 | + | |
4911 | +static void destroy(struct ip_set *set) | |
4912 | +{ | |
4913 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4914 | + | |
4915 | + /* gc might be running */ | |
4916 | + while (!del_timer(&map->gc)) | |
4917 | + msleep(IPTREE_DESTROY_SLEEP); | |
4918 | + __flush(map); | |
4919 | + kfree(map); | |
4920 | + set->data = NULL; | |
4921 | +} | |
4922 | + | |
4923 | +static void flush(struct ip_set *set) | |
4924 | +{ | |
4925 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4926 | + unsigned int timeout = map->timeout; | |
4927 | + | |
4928 | + /* gc might be running */ | |
4929 | + while (!del_timer(&map->gc)) | |
4930 | + msleep(IPTREE_DESTROY_SLEEP); | |
4931 | + __flush(map); | |
4932 | + memset(map, 0, sizeof(*map)); | |
4933 | + map->timeout = timeout; | |
4934 | + | |
4935 | + init_gc_timer(set); | |
4936 | +} | |
4937 | + | |
4938 | +static void list_header(const struct ip_set *set, void *data) | |
4939 | +{ | |
4940 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4941 | + struct ip_set_req_iptree_create *header = | |
4942 | + (struct ip_set_req_iptree_create *) data; | |
4943 | + | |
4944 | + header->timeout = map->timeout; | |
4945 | +} | |
4946 | + | |
4947 | +static int list_members_size(const struct ip_set *set) | |
4948 | +{ | |
4949 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4950 | + struct ip_set_iptreeb *btree; | |
4951 | + struct ip_set_iptreec *ctree; | |
4952 | + struct ip_set_iptreed *dtree; | |
4953 | + unsigned int a,b,c,d; | |
4954 | + unsigned int count = 0; | |
4955 | + | |
4956 | + LOOP_WALK_BEGIN(map, a, btree); | |
4957 | + LOOP_WALK_BEGIN(btree, b, ctree); | |
4958 | + LOOP_WALK_BEGIN(ctree, c, dtree); | |
4959 | + for (d = 0; d < 256; d++) { | |
4960 | + if (dtree->expires[d] | |
4961 | + && (!map->timeout || time_after(dtree->expires[d], jiffies))) | |
4962 | + count++; | |
4963 | + } | |
4964 | + LOOP_WALK_END; | |
4965 | + LOOP_WALK_END; | |
4966 | + LOOP_WALK_END; | |
4967 | + | |
4968 | + DP("members %u", count); | |
4969 | + return (count * sizeof(struct ip_set_req_iptree)); | |
4970 | +} | |
4971 | + | |
4972 | +static void list_members(const struct ip_set *set, void *data) | |
4973 | +{ | |
4974 | + struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; | |
4975 | + struct ip_set_iptreeb *btree; | |
4976 | + struct ip_set_iptreec *ctree; | |
4977 | + struct ip_set_iptreed *dtree; | |
4978 | + unsigned int a,b,c,d; | |
4979 | + size_t offset = 0; | |
4980 | + struct ip_set_req_iptree *entry; | |
4981 | + | |
4982 | + LOOP_WALK_BEGIN(map, a, btree); | |
4983 | + LOOP_WALK_BEGIN(btree, b, ctree); | |
4984 | + LOOP_WALK_BEGIN(ctree, c, dtree); | |
4985 | + for (d = 0; d < 256; d++) { | |
4986 | + if (dtree->expires[d] | |
4987 | + && (!map->timeout || time_after(dtree->expires[d], jiffies))) { | |
4988 | + entry = (struct ip_set_req_iptree *)(data + offset); | |
4989 | + entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); | |
4990 | + entry->timeout = !map->timeout ? 0 | |
4991 | + : (dtree->expires[d] - jiffies)/HZ; | |
4992 | + offset += sizeof(struct ip_set_req_iptree); | |
4993 | + } | |
4994 | + } | |
4995 | + LOOP_WALK_END; | |
4996 | + LOOP_WALK_END; | |
4997 | + LOOP_WALK_END; | |
4998 | +} | |
4999 | + | |
5000 | +static struct ip_set_type ip_set_iptree = { | |
5001 | + .typename = SETTYPE_NAME, | |
5002 | + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, | |
5003 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
5004 | + .create = &create, | |
5005 | + .destroy = &destroy, | |
5006 | + .flush = &flush, | |
5007 | + .reqsize = sizeof(struct ip_set_req_iptree), | |
5008 | + .addip = &addip, | |
5009 | + .addip_kernel = &addip_kernel, | |
5010 | + .delip = &delip, | |
5011 | + .delip_kernel = &delip_kernel, | |
5012 | + .testip = &testip, | |
5013 | + .testip_kernel = &testip_kernel, | |
5014 | + .header_size = sizeof(struct ip_set_req_iptree_create), | |
5015 | + .list_header = &list_header, | |
5016 | + .list_members_size = &list_members_size, | |
5017 | + .list_members = &list_members, | |
5018 | + .me = THIS_MODULE, | |
5019 | +}; | |
5020 | + | |
5021 | +MODULE_LICENSE("GPL"); | |
5022 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
5023 | +MODULE_DESCRIPTION("iptree type of IP sets"); | |
5024 | + | |
5025 | +static int __init init(void) | |
5026 | +{ | |
5027 | + int ret; | |
5028 | + | |
5029 | + branch_cachep = kmem_cache_create("ip_set_iptreeb", | |
5030 | + sizeof(struct ip_set_iptreeb), | |
5031 | + 0, 0, NULL, NULL); | |
5032 | + if (!branch_cachep) { | |
5033 | + printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); | |
5034 | + ret = -ENOMEM; | |
5035 | + goto out; | |
5036 | + } | |
5037 | + leaf_cachep = kmem_cache_create("ip_set_iptreed", | |
5038 | + sizeof(struct ip_set_iptreed), | |
5039 | + 0, 0, NULL, NULL); | |
5040 | + if (!leaf_cachep) { | |
5041 | + printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); | |
5042 | + ret = -ENOMEM; | |
5043 | + goto free_branch; | |
5044 | + } | |
5045 | + ret = ip_set_register_set_type(&ip_set_iptree); | |
5046 | + if (ret == 0) | |
5047 | + goto out; | |
5048 | + | |
5049 | + kmem_cache_destroy(leaf_cachep); | |
5050 | + free_branch: | |
5051 | + kmem_cache_destroy(branch_cachep); | |
5052 | + out: | |
5053 | + return ret; | |
5054 | +} | |
5055 | + | |
5056 | +static void __exit fini(void) | |
5057 | +{ | |
5058 | + /* FIXME: possible race with ip_set_create() */ | |
5059 | + ip_set_unregister_set_type(&ip_set_iptree); | |
5060 | + kmem_cache_destroy(leaf_cachep); | |
5061 | + kmem_cache_destroy(branch_cachep); | |
5062 | +} | |
5063 | + | |
5064 | +module_init(init); | |
5065 | +module_exit(fini); | |
5066 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_macipmap.c linux/net/ipv4/netfilter/ip_set_macipmap.c | |
5067 | --- linux.org/net/ipv4/netfilter/ip_set_macipmap.c 1970-01-01 01:00:00.000000000 +0100 | |
5068 | +++ linux/net/ipv4/netfilter/ip_set_macipmap.c 2006-05-04 10:26:33.000000000 +0200 | |
5069 | @@ -0,0 +1,353 @@ | |
5070 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
5071 | + * Patrick Schaaf <bof@bof.de> | |
5072 | + * Martin Josefsson <gandalf@wlug.westbo.se> | |
5073 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
5074 | + * | |
5075 | + * This program is free software; you can redistribute it and/or modify | |
5076 | + * it under the terms of the GNU General Public License version 2 as | |
5077 | + * published by the Free Software Foundation. | |
5078 | + */ | |
5079 | + | |
5080 | +/* Kernel module implementing an IP set type: the macipmap type */ | |
5081 | + | |
5082 | +#include <linux/module.h> | |
5083 | +#include <linux/ip.h> | |
5084 | +#include <linux/skbuff.h> | |
5085 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
5086 | +#include <linux/netfilter_ipv4/ip_set.h> | |
5087 | +#include <linux/errno.h> | |
5088 | +#include <asm/uaccess.h> | |
5089 | +#include <asm/bitops.h> | |
5090 | +#include <linux/spinlock.h> | |
5091 | +#include <linux/if_ether.h> | |
5092 | +#include <linux/vmalloc.h> | |
5093 | + | |
5094 | +#include <linux/netfilter_ipv4/ip_set_malloc.h> | |
5095 | +#include <linux/netfilter_ipv4/ip_set_macipmap.h> | |
5096 | + | |
5097 | +static int | |
5098 | +testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) | |
5099 | +{ | |
5100 | + struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; | |
5101 | + struct ip_set_macip *table = (struct ip_set_macip *) map->members; | |
5102 | + struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; | |
5103 | + | |
5104 | + if (size != sizeof(struct ip_set_req_macipmap)) { | |
5105 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5106 | + sizeof(struct ip_set_req_macipmap), | |
5107 | + size); | |
5108 | + return -EINVAL; | |
5109 | + } | |
5110 | + | |
5111 | + if (req->ip < map->first_ip || req->ip > map->last_ip) | |
5112 | + return -ERANGE; | |
5113 | + | |
5114 | + *hash_ip = req->ip; | |
5115 | + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", | |
5116 | + set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); | |
5117 | + if (test_bit(IPSET_MACIP_ISSET, | |
5118 | + (void *) &table[req->ip - map->first_ip].flags)) { | |
5119 | + return (memcmp(req->ethernet, | |
5120 | + &table[req->ip - map->first_ip].ethernet, | |
5121 | + ETH_ALEN) == 0); | |
5122 | + } else { | |
5123 | + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); | |
5124 | + } | |
5125 | +} | |
5126 | + | |
5127 | +static int | |
5128 | +testip_kernel(struct ip_set *set, | |
5129 | + const struct sk_buff *skb, | |
5130 | + ip_set_ip_t *hash_ip, | |
5131 | + const u_int32_t *flags, | |
5132 | + unsigned char index) | |
5133 | +{ | |
5134 | + struct ip_set_macipmap *map = | |
5135 | + (struct ip_set_macipmap *) set->data; | |
5136 | + struct ip_set_macip *table = | |
5137 | + (struct ip_set_macip *) map->members; | |
5138 | + ip_set_ip_t ip; | |
5139 | + | |
5140 | + ip = ntohl(flags[index] & IPSET_SRC | |
5141 | + ? skb->nh.iph->saddr | |
5142 | + : skb->nh.iph->daddr); | |
5143 | + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", | |
5144 | + flags[index] & IPSET_SRC ? "SRC" : "DST", | |
5145 | + NIPQUAD(skb->nh.iph->saddr), | |
5146 | + NIPQUAD(skb->nh.iph->daddr)); | |
5147 | + | |
5148 | + if (ip < map->first_ip || ip > map->last_ip) | |
5149 | + return 0; | |
5150 | + | |
5151 | + *hash_ip = ip; | |
5152 | + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", | |
5153 | + set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
5154 | + if (test_bit(IPSET_MACIP_ISSET, | |
5155 | + (void *) &table[ip - map->first_ip].flags)) { | |
5156 | + /* Is mac pointer valid? | |
5157 | + * If so, compare... */ | |
5158 | + return (skb->mac.raw >= skb->head | |
5159 | + && (skb->mac.raw + ETH_HLEN) <= skb->data | |
5160 | + && (memcmp(eth_hdr(skb)->h_source, | |
5161 | + &table[ip - map->first_ip].ethernet, | |
5162 | + ETH_ALEN) == 0)); | |
5163 | + } else { | |
5164 | + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); | |
5165 | + } | |
5166 | +} | |
5167 | + | |
5168 | +/* returns 0 on success */ | |
5169 | +static inline int | |
5170 | +__addip(struct ip_set *set, | |
5171 | + ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) | |
5172 | +{ | |
5173 | + struct ip_set_macipmap *map = | |
5174 | + (struct ip_set_macipmap *) set->data; | |
5175 | + struct ip_set_macip *table = | |
5176 | + (struct ip_set_macip *) map->members; | |
5177 | + | |
5178 | + if (ip < map->first_ip || ip > map->last_ip) | |
5179 | + return -ERANGE; | |
5180 | + if (test_and_set_bit(IPSET_MACIP_ISSET, | |
5181 | + (void *) &table[ip - map->first_ip].flags)) | |
5182 | + return -EEXIST; | |
5183 | + | |
5184 | + *hash_ip = ip; | |
5185 | + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
5186 | + memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); | |
5187 | + return 0; | |
5188 | +} | |
5189 | + | |
5190 | +static int | |
5191 | +addip(struct ip_set *set, const void *data, size_t size, | |
5192 | + ip_set_ip_t *hash_ip) | |
5193 | +{ | |
5194 | + struct ip_set_req_macipmap *req = | |
5195 | + (struct ip_set_req_macipmap *) data; | |
5196 | + | |
5197 | + if (size != sizeof(struct ip_set_req_macipmap)) { | |
5198 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5199 | + sizeof(struct ip_set_req_macipmap), | |
5200 | + size); | |
5201 | + return -EINVAL; | |
5202 | + } | |
5203 | + return __addip(set, req->ip, req->ethernet, hash_ip); | |
5204 | +} | |
5205 | + | |
5206 | +static int | |
5207 | +addip_kernel(struct ip_set *set, | |
5208 | + const struct sk_buff *skb, | |
5209 | + ip_set_ip_t *hash_ip, | |
5210 | + const u_int32_t *flags, | |
5211 | + unsigned char index) | |
5212 | +{ | |
5213 | + ip_set_ip_t ip; | |
5214 | + | |
5215 | + ip = ntohl(flags[index] & IPSET_SRC | |
5216 | + ? skb->nh.iph->saddr | |
5217 | + : skb->nh.iph->daddr); | |
5218 | + | |
5219 | + if (!(skb->mac.raw >= skb->head | |
5220 | + && (skb->mac.raw + ETH_HLEN) <= skb->data)) | |
5221 | + return -EINVAL; | |
5222 | + | |
5223 | + return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); | |
5224 | +} | |
5225 | + | |
5226 | +static inline int | |
5227 | +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
5228 | +{ | |
5229 | + struct ip_set_macipmap *map = | |
5230 | + (struct ip_set_macipmap *) set->data; | |
5231 | + struct ip_set_macip *table = | |
5232 | + (struct ip_set_macip *) map->members; | |
5233 | + | |
5234 | + if (ip < map->first_ip || ip > map->last_ip) | |
5235 | + return -ERANGE; | |
5236 | + if (!test_and_clear_bit(IPSET_MACIP_ISSET, | |
5237 | + (void *)&table[ip - map->first_ip].flags)) | |
5238 | + return -EEXIST; | |
5239 | + | |
5240 | + *hash_ip = ip; | |
5241 | + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); | |
5242 | + return 0; | |
5243 | +} | |
5244 | + | |
5245 | +static int | |
5246 | +delip(struct ip_set *set, const void *data, size_t size, | |
5247 | + ip_set_ip_t *hash_ip) | |
5248 | +{ | |
5249 | + struct ip_set_req_macipmap *req = | |
5250 | + (struct ip_set_req_macipmap *) data; | |
5251 | + | |
5252 | + if (size != sizeof(struct ip_set_req_macipmap)) { | |
5253 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5254 | + sizeof(struct ip_set_req_macipmap), | |
5255 | + size); | |
5256 | + return -EINVAL; | |
5257 | + } | |
5258 | + return __delip(set, req->ip, hash_ip); | |
5259 | +} | |
5260 | + | |
5261 | +static int | |
5262 | +delip_kernel(struct ip_set *set, | |
5263 | + const struct sk_buff *skb, | |
5264 | + ip_set_ip_t *hash_ip, | |
5265 | + const u_int32_t *flags, | |
5266 | + unsigned char index) | |
5267 | +{ | |
5268 | + return __delip(set, | |
5269 | + ntohl(flags[index] & IPSET_SRC | |
5270 | + ? skb->nh.iph->saddr | |
5271 | + : skb->nh.iph->daddr), | |
5272 | + hash_ip); | |
5273 | +} | |
5274 | + | |
5275 | +static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) | |
5276 | +{ | |
5277 | + return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); | |
5278 | +} | |
5279 | + | |
5280 | +static int create(struct ip_set *set, const void *data, size_t size) | |
5281 | +{ | |
5282 | + int newbytes; | |
5283 | + struct ip_set_req_macipmap_create *req = | |
5284 | + (struct ip_set_req_macipmap_create *) data; | |
5285 | + struct ip_set_macipmap *map; | |
5286 | + | |
5287 | + if (size != sizeof(struct ip_set_req_macipmap_create)) { | |
5288 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5289 | + sizeof(struct ip_set_req_macipmap_create), | |
5290 | + size); | |
5291 | + return -EINVAL; | |
5292 | + } | |
5293 | + | |
5294 | + DP("from %u.%u.%u.%u to %u.%u.%u.%u", | |
5295 | + HIPQUAD(req->from), HIPQUAD(req->to)); | |
5296 | + | |
5297 | + if (req->from > req->to) { | |
5298 | + DP("bad ip range"); | |
5299 | + return -ENOEXEC; | |
5300 | + } | |
5301 | + | |
5302 | + if (req->to - req->from > MAX_RANGE) { | |
5303 | + ip_set_printk("range too big (max %d addresses)", | |
5304 | + MAX_RANGE+1); | |
5305 | + return -ENOEXEC; | |
5306 | + } | |
5307 | + | |
5308 | + map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); | |
5309 | + if (!map) { | |
5310 | + DP("out of memory for %d bytes", | |
5311 | + sizeof(struct ip_set_macipmap)); | |
5312 | + return -ENOMEM; | |
5313 | + } | |
5314 | + map->flags = req->flags; | |
5315 | + map->first_ip = req->from; | |
5316 | + map->last_ip = req->to; | |
5317 | + newbytes = members_size(map->first_ip, map->last_ip); | |
5318 | + map->members = ip_set_malloc(newbytes); | |
5319 | + DP("members: %u %p", newbytes, map->members); | |
5320 | + if (!map->members) { | |
5321 | + DP("out of memory for %d bytes", newbytes); | |
5322 | + kfree(map); | |
5323 | + return -ENOMEM; | |
5324 | + } | |
5325 | + memset(map->members, 0, newbytes); | |
5326 | + | |
5327 | + set->data = map; | |
5328 | + return 0; | |
5329 | +} | |
5330 | + | |
5331 | +static void destroy(struct ip_set *set) | |
5332 | +{ | |
5333 | + struct ip_set_macipmap *map = | |
5334 | + (struct ip_set_macipmap *) set->data; | |
5335 | + | |
5336 | + ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); | |
5337 | + kfree(map); | |
5338 | + | |
5339 | + set->data = NULL; | |
5340 | +} | |
5341 | + | |
5342 | +static void flush(struct ip_set *set) | |
5343 | +{ | |
5344 | + struct ip_set_macipmap *map = | |
5345 | + (struct ip_set_macipmap *) set->data; | |
5346 | + memset(map->members, 0, members_size(map->first_ip, map->last_ip)); | |
5347 | +} | |
5348 | + | |
5349 | +static void list_header(const struct ip_set *set, void *data) | |
5350 | +{ | |
5351 | + struct ip_set_macipmap *map = | |
5352 | + (struct ip_set_macipmap *) set->data; | |
5353 | + struct ip_set_req_macipmap_create *header = | |
5354 | + (struct ip_set_req_macipmap_create *) data; | |
5355 | + | |
5356 | + DP("list_header %x %x %u", map->first_ip, map->last_ip, | |
5357 | + map->flags); | |
5358 | + | |
5359 | + header->from = map->first_ip; | |
5360 | + header->to = map->last_ip; | |
5361 | + header->flags = map->flags; | |
5362 | +} | |
5363 | + | |
5364 | +static int list_members_size(const struct ip_set *set) | |
5365 | +{ | |
5366 | + struct ip_set_macipmap *map = | |
5367 | + (struct ip_set_macipmap *) set->data; | |
5368 | + | |
5369 | + DP("%u", members_size(map->first_ip, map->last_ip)); | |
5370 | + return members_size(map->first_ip, map->last_ip); | |
5371 | +} | |
5372 | + | |
5373 | +static void list_members(const struct ip_set *set, void *data) | |
5374 | +{ | |
5375 | + struct ip_set_macipmap *map = | |
5376 | + (struct ip_set_macipmap *) set->data; | |
5377 | + | |
5378 | + int bytes = members_size(map->first_ip, map->last_ip); | |
5379 | + | |
5380 | + DP("members: %u %p", bytes, map->members); | |
5381 | + memcpy(data, map->members, bytes); | |
5382 | +} | |
5383 | + | |
5384 | +static struct ip_set_type ip_set_macipmap = { | |
5385 | + .typename = SETTYPE_NAME, | |
5386 | + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, | |
5387 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
5388 | + .create = &create, | |
5389 | + .destroy = &destroy, | |
5390 | + .flush = &flush, | |
5391 | + .reqsize = sizeof(struct ip_set_req_macipmap), | |
5392 | + .addip = &addip, | |
5393 | + .addip_kernel = &addip_kernel, | |
5394 | + .delip = &delip, | |
5395 | + .delip_kernel = &delip_kernel, | |
5396 | + .testip = &testip, | |
5397 | + .testip_kernel = &testip_kernel, | |
5398 | + .header_size = sizeof(struct ip_set_req_macipmap_create), | |
5399 | + .list_header = &list_header, | |
5400 | + .list_members_size = &list_members_size, | |
5401 | + .list_members = &list_members, | |
5402 | + .me = THIS_MODULE, | |
5403 | +}; | |
5404 | + | |
5405 | +MODULE_LICENSE("GPL"); | |
5406 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
5407 | +MODULE_DESCRIPTION("macipmap type of IP sets"); | |
5408 | + | |
5409 | +static int __init init(void) | |
5410 | +{ | |
5411 | + init_max_malloc_size(); | |
5412 | + return ip_set_register_set_type(&ip_set_macipmap); | |
5413 | +} | |
5414 | + | |
5415 | +static void __exit fini(void) | |
5416 | +{ | |
5417 | + /* FIXME: possible race with ip_set_create() */ | |
5418 | + ip_set_unregister_set_type(&ip_set_macipmap); | |
5419 | +} | |
5420 | + | |
5421 | +module_init(init); | |
5422 | +module_exit(fini); | |
5423 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_nethash.c linux/net/ipv4/netfilter/ip_set_nethash.c | |
5424 | --- linux.org/net/ipv4/netfilter/ip_set_nethash.c 1970-01-01 01:00:00.000000000 +0100 | |
5425 | +++ linux/net/ipv4/netfilter/ip_set_nethash.c 2006-05-04 10:26:33.000000000 +0200 | |
5426 | @@ -0,0 +1,466 @@ | |
5427 | +/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
5428 | + * | |
5429 | + * This program is free software; you can redistribute it and/or modify | |
5430 | + * it under the terms of the GNU General Public License version 2 as | |
5431 | + * published by the Free Software Foundation. | |
5432 | + */ | |
5433 | + | |
5434 | +/* Kernel module implementing a cidr nethash set */ | |
5435 | + | |
5436 | +#include <linux/module.h> | |
5437 | +#include <linux/ip.h> | |
5438 | +#include <linux/skbuff.h> | |
5439 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
5440 | +#include <linux/netfilter_ipv4/ip_set.h> | |
5441 | +#include <linux/errno.h> | |
5442 | +#include <asm/uaccess.h> | |
5443 | +#include <asm/bitops.h> | |
5444 | +#include <linux/spinlock.h> | |
5445 | +#include <linux/vmalloc.h> | |
5446 | +#include <linux/random.h> | |
5447 | + | |
5448 | +#include <net/ip.h> | |
5449 | + | |
5450 | +#include <linux/netfilter_ipv4/ip_set_malloc.h> | |
5451 | +#include <linux/netfilter_ipv4/ip_set_nethash.h> | |
5452 | +#include <linux/netfilter_ipv4/ip_set_jhash.h> | |
5453 | + | |
5454 | +static inline __u32 | |
5455 | +jhash_ip(const struct ip_set_nethash *map, uint16_t i, ip_set_ip_t ip) | |
5456 | +{ | |
5457 | + return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); | |
5458 | +} | |
5459 | + | |
5460 | +static inline __u32 | |
5461 | +hash_id_cidr(struct ip_set_nethash *map, | |
5462 | + ip_set_ip_t ip, | |
5463 | + unsigned char cidr, | |
5464 | + ip_set_ip_t *hash_ip) | |
5465 | +{ | |
5466 | + __u32 id; | |
5467 | + u_int16_t i; | |
5468 | + ip_set_ip_t *elem; | |
5469 | + | |
5470 | + *hash_ip = pack(ip, cidr); | |
5471 | + | |
5472 | + for (i = 0; i < map->probes; i++) { | |
5473 | + id = jhash_ip(map, i, *hash_ip) % map->hashsize; | |
5474 | + DP("hash key: %u", id); | |
5475 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
5476 | + if (*elem == *hash_ip) | |
5477 | + return id; | |
5478 | + } | |
5479 | + return UINT_MAX; | |
5480 | +} | |
5481 | + | |
5482 | +static inline __u32 | |
5483 | +hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
5484 | +{ | |
5485 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5486 | + __u32 id = UINT_MAX; | |
5487 | + int i; | |
5488 | + | |
5489 | + for (i = 0; i < 30 && map->cidr[i]; i++) { | |
5490 | + id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); | |
5491 | + if (id != UINT_MAX) | |
5492 | + break; | |
5493 | + } | |
5494 | + return id; | |
5495 | +} | |
5496 | + | |
5497 | +static inline int | |
5498 | +__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, | |
5499 | + ip_set_ip_t *hash_ip) | |
5500 | +{ | |
5501 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5502 | + | |
5503 | + return (hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); | |
5504 | +} | |
5505 | + | |
5506 | +static inline int | |
5507 | +__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) | |
5508 | +{ | |
5509 | + return (hash_id(set, ip, hash_ip) != UINT_MAX); | |
5510 | +} | |
5511 | + | |
5512 | +static int | |
5513 | +testip(struct ip_set *set, const void *data, size_t size, | |
5514 | + ip_set_ip_t *hash_ip) | |
5515 | +{ | |
5516 | + struct ip_set_req_nethash *req = | |
5517 | + (struct ip_set_req_nethash *) data; | |
5518 | + | |
5519 | + if (size != sizeof(struct ip_set_req_nethash)) { | |
5520 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5521 | + sizeof(struct ip_set_req_nethash), | |
5522 | + size); | |
5523 | + return -EINVAL; | |
5524 | + } | |
5525 | + return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) | |
5526 | + : __testip_cidr(set, req->ip, req->cidr, hash_ip)); | |
5527 | +} | |
5528 | + | |
5529 | +static int | |
5530 | +testip_kernel(struct ip_set *set, | |
5531 | + const struct sk_buff *skb, | |
5532 | + ip_set_ip_t *hash_ip, | |
5533 | + const u_int32_t *flags, | |
5534 | + unsigned char index) | |
5535 | +{ | |
5536 | + return __testip(set, | |
5537 | + ntohl(flags[index] & IPSET_SRC | |
5538 | + ? skb->nh.iph->saddr | |
5539 | + : skb->nh.iph->daddr), | |
5540 | + hash_ip); | |
5541 | +} | |
5542 | + | |
5543 | +static inline int | |
5544 | +__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) | |
5545 | +{ | |
5546 | + __u32 probe; | |
5547 | + u_int16_t i; | |
5548 | + ip_set_ip_t *elem; | |
5549 | + | |
5550 | + for (i = 0; i < map->probes; i++) { | |
5551 | + probe = jhash_ip(map, i, ip) % map->hashsize; | |
5552 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); | |
5553 | + if (*elem == ip) | |
5554 | + return -EEXIST; | |
5555 | + if (!*elem) { | |
5556 | + *elem = ip; | |
5557 | + return 0; | |
5558 | + } | |
5559 | + } | |
5560 | + /* Trigger rehashing */ | |
5561 | + return -EAGAIN; | |
5562 | +} | |
5563 | + | |
5564 | +static inline int | |
5565 | +__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, | |
5566 | + ip_set_ip_t *hash_ip) | |
5567 | +{ | |
5568 | + *hash_ip = pack(ip, cidr); | |
5569 | + DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); | |
5570 | + | |
5571 | + return __addip_base(map, *hash_ip); | |
5572 | +} | |
5573 | + | |
5574 | +static void | |
5575 | +update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) | |
5576 | +{ | |
5577 | + unsigned char next; | |
5578 | + int i; | |
5579 | + | |
5580 | + for (i = 0; i < 30 && map->cidr[i]; i++) { | |
5581 | + if (map->cidr[i] == cidr) { | |
5582 | + return; | |
5583 | + } else if (map->cidr[i] < cidr) { | |
5584 | + next = map->cidr[i]; | |
5585 | + map->cidr[i] = cidr; | |
5586 | + cidr = next; | |
5587 | + } | |
5588 | + } | |
5589 | + if (i < 30) | |
5590 | + map->cidr[i] = cidr; | |
5591 | +} | |
5592 | + | |
5593 | +static int | |
5594 | +addip(struct ip_set *set, const void *data, size_t size, | |
5595 | + ip_set_ip_t *hash_ip) | |
5596 | +{ | |
5597 | + struct ip_set_req_nethash *req = | |
5598 | + (struct ip_set_req_nethash *) data; | |
5599 | + int ret; | |
5600 | + | |
5601 | + if (size != sizeof(struct ip_set_req_nethash)) { | |
5602 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5603 | + sizeof(struct ip_set_req_nethash), | |
5604 | + size); | |
5605 | + return -EINVAL; | |
5606 | + } | |
5607 | + ret = __addip((struct ip_set_nethash *) set->data, | |
5608 | + req->ip, req->cidr, hash_ip); | |
5609 | + | |
5610 | + if (ret == 0) | |
5611 | + update_cidr_sizes((struct ip_set_nethash *) set->data, | |
5612 | + req->cidr); | |
5613 | + | |
5614 | + return ret; | |
5615 | +} | |
5616 | + | |
5617 | +static int | |
5618 | +addip_kernel(struct ip_set *set, | |
5619 | + const struct sk_buff *skb, | |
5620 | + ip_set_ip_t *hash_ip, | |
5621 | + const u_int32_t *flags, | |
5622 | + unsigned char index) | |
5623 | +{ | |
5624 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5625 | + int ret = -ERANGE; | |
5626 | + ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC | |
5627 | + ? skb->nh.iph->saddr | |
5628 | + : skb->nh.iph->daddr); | |
5629 | + | |
5630 | + if (map->cidr[0]) | |
5631 | + ret = __addip(map, ip, map->cidr[0], hash_ip); | |
5632 | + | |
5633 | + return ret; | |
5634 | +} | |
5635 | + | |
5636 | +static int retry(struct ip_set *set) | |
5637 | +{ | |
5638 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5639 | + ip_set_ip_t *elem; | |
5640 | + void *members; | |
5641 | + u_int32_t i, hashsize = map->hashsize; | |
5642 | + int res; | |
5643 | + struct ip_set_nethash *tmp; | |
5644 | + | |
5645 | + if (map->resize == 0) | |
5646 | + return -ERANGE; | |
5647 | + | |
5648 | + again: | |
5649 | + res = 0; | |
5650 | + | |
5651 | + /* Calculate new parameters */ | |
5652 | + hashsize += (hashsize * map->resize)/100; | |
5653 | + if (hashsize == map->hashsize) | |
5654 | + hashsize++; | |
5655 | + | |
5656 | + ip_set_printk("rehashing of set %s triggered: " | |
5657 | + "hashsize grows from %u to %u", | |
5658 | + set->name, map->hashsize, hashsize); | |
5659 | + | |
5660 | + tmp = kmalloc(sizeof(struct ip_set_nethash) | |
5661 | + + map->probes * sizeof(uint32_t), GFP_ATOMIC); | |
5662 | + if (!tmp) { | |
5663 | + DP("out of memory for %d bytes", | |
5664 | + sizeof(struct ip_set_nethash) | |
5665 | + + map->probes * sizeof(uint32_t)); | |
5666 | + return -ENOMEM; | |
5667 | + } | |
5668 | + tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); | |
5669 | + if (!tmp->members) { | |
5670 | + DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); | |
5671 | + kfree(tmp); | |
5672 | + return -ENOMEM; | |
5673 | + } | |
5674 | + tmp->hashsize = hashsize; | |
5675 | + tmp->probes = map->probes; | |
5676 | + tmp->resize = map->resize; | |
5677 | + memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); | |
5678 | + memcpy(tmp->cidr, map->cidr, 30 * sizeof(unsigned char)); | |
5679 | + | |
5680 | + write_lock_bh(&set->lock); | |
5681 | + map = (struct ip_set_nethash *) set->data; /* Play safe */ | |
5682 | + for (i = 0; i < map->hashsize && res == 0; i++) { | |
5683 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
5684 | + if (*elem) | |
5685 | + res = __addip_base(tmp, *elem); | |
5686 | + } | |
5687 | + if (res) { | |
5688 | + /* Failure, try again */ | |
5689 | + write_unlock_bh(&set->lock); | |
5690 | + harray_free(tmp->members); | |
5691 | + kfree(tmp); | |
5692 | + goto again; | |
5693 | + } | |
5694 | + | |
5695 | + /* Success at resizing! */ | |
5696 | + members = map->members; | |
5697 | + | |
5698 | + map->hashsize = tmp->hashsize; | |
5699 | + map->members = tmp->members; | |
5700 | + write_unlock_bh(&set->lock); | |
5701 | + | |
5702 | + harray_free(members); | |
5703 | + kfree(tmp); | |
5704 | + | |
5705 | + return 0; | |
5706 | +} | |
5707 | + | |
5708 | +static inline int | |
5709 | +__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, | |
5710 | + ip_set_ip_t *hash_ip) | |
5711 | +{ | |
5712 | + ip_set_ip_t id = hash_id_cidr(map, ip, cidr, hash_ip); | |
5713 | + ip_set_ip_t *elem; | |
5714 | + | |
5715 | + if (id == UINT_MAX) | |
5716 | + return -EEXIST; | |
5717 | + | |
5718 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); | |
5719 | + *elem = 0; | |
5720 | + return 0; | |
5721 | +} | |
5722 | + | |
5723 | +static int | |
5724 | +delip(struct ip_set *set, const void *data, size_t size, | |
5725 | + ip_set_ip_t *hash_ip) | |
5726 | +{ | |
5727 | + struct ip_set_req_nethash *req = | |
5728 | + (struct ip_set_req_nethash *) data; | |
5729 | + | |
5730 | + if (size != sizeof(struct ip_set_req_nethash)) { | |
5731 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5732 | + sizeof(struct ip_set_req_nethash), | |
5733 | + size); | |
5734 | + return -EINVAL; | |
5735 | + } | |
5736 | + /* TODO: no garbage collection in map->cidr */ | |
5737 | + return __delip((struct ip_set_nethash *) set->data, | |
5738 | + req->ip, req->cidr, hash_ip); | |
5739 | +} | |
5740 | + | |
5741 | +static int | |
5742 | +delip_kernel(struct ip_set *set, | |
5743 | + const struct sk_buff *skb, | |
5744 | + ip_set_ip_t *hash_ip, | |
5745 | + const u_int32_t *flags, | |
5746 | + unsigned char index) | |
5747 | +{ | |
5748 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5749 | + int ret = -ERANGE; | |
5750 | + ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC | |
5751 | + ? skb->nh.iph->saddr | |
5752 | + : skb->nh.iph->daddr); | |
5753 | + | |
5754 | + if (map->cidr[0]) | |
5755 | + ret = __delip(map, ip, map->cidr[0], hash_ip); | |
5756 | + | |
5757 | + return ret; | |
5758 | +} | |
5759 | + | |
5760 | +static int create(struct ip_set *set, const void *data, size_t size) | |
5761 | +{ | |
5762 | + struct ip_set_req_nethash_create *req = | |
5763 | + (struct ip_set_req_nethash_create *) data; | |
5764 | + struct ip_set_nethash *map; | |
5765 | + uint16_t i; | |
5766 | + | |
5767 | + if (size != sizeof(struct ip_set_req_nethash_create)) { | |
5768 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5769 | + sizeof(struct ip_set_req_nethash_create), | |
5770 | + size); | |
5771 | + return -EINVAL; | |
5772 | + } | |
5773 | + | |
5774 | + if (req->hashsize < 1) { | |
5775 | + ip_set_printk("hashsize too small"); | |
5776 | + return -ENOEXEC; | |
5777 | + } | |
5778 | + if (req->probes < 1) { | |
5779 | + ip_set_printk("probes too small"); | |
5780 | + return -ENOEXEC; | |
5781 | + } | |
5782 | + | |
5783 | + map = kmalloc(sizeof(struct ip_set_nethash) | |
5784 | + + req->probes * sizeof(uint32_t), GFP_KERNEL); | |
5785 | + if (!map) { | |
5786 | + DP("out of memory for %d bytes", | |
5787 | + sizeof(struct ip_set_nethash) | |
5788 | + + req->probes * sizeof(uint32_t)); | |
5789 | + return -ENOMEM; | |
5790 | + } | |
5791 | + for (i = 0; i < req->probes; i++) | |
5792 | + get_random_bytes(((uint32_t *) map->initval)+i, 4); | |
5793 | + map->hashsize = req->hashsize; | |
5794 | + map->probes = req->probes; | |
5795 | + map->resize = req->resize; | |
5796 | + memset(map->cidr, 0, 30 * sizeof(unsigned char)); | |
5797 | + map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); | |
5798 | + if (!map->members) { | |
5799 | + DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); | |
5800 | + kfree(map); | |
5801 | + return -ENOMEM; | |
5802 | + } | |
5803 | + | |
5804 | + set->data = map; | |
5805 | + return 0; | |
5806 | +} | |
5807 | + | |
5808 | +static void destroy(struct ip_set *set) | |
5809 | +{ | |
5810 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5811 | + | |
5812 | + harray_free(map->members); | |
5813 | + kfree(map); | |
5814 | + | |
5815 | + set->data = NULL; | |
5816 | +} | |
5817 | + | |
5818 | +static void flush(struct ip_set *set) | |
5819 | +{ | |
5820 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5821 | + harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); | |
5822 | + memset(map->cidr, 0, 30 * sizeof(unsigned char)); | |
5823 | +} | |
5824 | + | |
5825 | +static void list_header(const struct ip_set *set, void *data) | |
5826 | +{ | |
5827 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5828 | + struct ip_set_req_nethash_create *header = | |
5829 | + (struct ip_set_req_nethash_create *) data; | |
5830 | + | |
5831 | + header->hashsize = map->hashsize; | |
5832 | + header->probes = map->probes; | |
5833 | + header->resize = map->resize; | |
5834 | +} | |
5835 | + | |
5836 | +static int list_members_size(const struct ip_set *set) | |
5837 | +{ | |
5838 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5839 | + | |
5840 | + return (map->hashsize * sizeof(ip_set_ip_t)); | |
5841 | +} | |
5842 | + | |
5843 | +static void list_members(const struct ip_set *set, void *data) | |
5844 | +{ | |
5845 | + struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; | |
5846 | + ip_set_ip_t i, *elem; | |
5847 | + | |
5848 | + for (i = 0; i < map->hashsize; i++) { | |
5849 | + elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); | |
5850 | + ((ip_set_ip_t *)data)[i] = *elem; | |
5851 | + } | |
5852 | +} | |
5853 | + | |
5854 | +static struct ip_set_type ip_set_nethash = { | |
5855 | + .typename = SETTYPE_NAME, | |
5856 | + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, | |
5857 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
5858 | + .create = &create, | |
5859 | + .destroy = &destroy, | |
5860 | + .flush = &flush, | |
5861 | + .reqsize = sizeof(struct ip_set_req_nethash), | |
5862 | + .addip = &addip, | |
5863 | + .addip_kernel = &addip_kernel, | |
5864 | + .retry = &retry, | |
5865 | + .delip = &delip, | |
5866 | + .delip_kernel = &delip_kernel, | |
5867 | + .testip = &testip, | |
5868 | + .testip_kernel = &testip_kernel, | |
5869 | + .header_size = sizeof(struct ip_set_req_nethash_create), | |
5870 | + .list_header = &list_header, | |
5871 | + .list_members_size = &list_members_size, | |
5872 | + .list_members = &list_members, | |
5873 | + .me = THIS_MODULE, | |
5874 | +}; | |
5875 | + | |
5876 | +MODULE_LICENSE("GPL"); | |
5877 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
5878 | +MODULE_DESCRIPTION("nethash type of IP sets"); | |
5879 | + | |
5880 | +static int __init init(void) | |
5881 | +{ | |
5882 | + return ip_set_register_set_type(&ip_set_nethash); | |
5883 | +} | |
5884 | + | |
5885 | +static void __exit fini(void) | |
5886 | +{ | |
5887 | + /* FIXME: possible race with ip_set_create() */ | |
5888 | + ip_set_unregister_set_type(&ip_set_nethash); | |
5889 | +} | |
5890 | + | |
5891 | +module_init(init); | |
5892 | +module_exit(fini); | |
5893 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ip_set_portmap.c linux/net/ipv4/netfilter/ip_set_portmap.c | |
5894 | --- linux.org/net/ipv4/netfilter/ip_set_portmap.c 1970-01-01 01:00:00.000000000 +0100 | |
5895 | +++ linux/net/ipv4/netfilter/ip_set_portmap.c 2006-05-04 10:26:33.000000000 +0200 | |
5896 | @@ -0,0 +1,334 @@ | |
5897 | +/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
5898 | + * | |
5899 | + * This program is free software; you can redistribute it and/or modify | |
5900 | + * it under the terms of the GNU General Public License version 2 as | |
5901 | + * published by the Free Software Foundation. | |
5902 | + */ | |
5903 | + | |
5904 | +/* Kernel module implementing a port set type as a bitmap */ | |
5905 | + | |
5906 | +#include <linux/module.h> | |
5907 | +#include <linux/ip.h> | |
5908 | +#include <linux/tcp.h> | |
5909 | +#include <linux/udp.h> | |
5910 | +#include <linux/skbuff.h> | |
5911 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
5912 | +#include <linux/netfilter_ipv4/ip_set.h> | |
5913 | +#include <linux/errno.h> | |
5914 | +#include <asm/uaccess.h> | |
5915 | +#include <asm/bitops.h> | |
5916 | +#include <linux/spinlock.h> | |
5917 | + | |
5918 | +#include <net/ip.h> | |
5919 | + | |
5920 | +#include <linux/netfilter_ipv4/ip_set_portmap.h> | |
5921 | + | |
5922 | +/* We must handle non-linear skbs */ | |
5923 | +static inline ip_set_ip_t | |
5924 | +get_port(const struct sk_buff *skb, u_int32_t flags) | |
5925 | +{ | |
5926 | + struct iphdr *iph = skb->nh.iph; | |
5927 | + u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; | |
5928 | + | |
5929 | + switch (iph->protocol) { | |
5930 | + case IPPROTO_TCP: { | |
5931 | + struct tcphdr tcph; | |
5932 | + | |
5933 | + /* See comments at tcp_match in ip_tables.c */ | |
5934 | + if (offset) | |
5935 | + return INVALID_PORT; | |
5936 | + | |
5937 | + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) | |
5938 | + /* No choice either */ | |
5939 | + return INVALID_PORT; | |
5940 | + | |
5941 | + return ntohs(flags & IPSET_SRC ? | |
5942 | + tcph.source : tcph.dest); | |
5943 | + } | |
5944 | + case IPPROTO_UDP: { | |
5945 | + struct udphdr udph; | |
5946 | + | |
5947 | + if (offset) | |
5948 | + return INVALID_PORT; | |
5949 | + | |
5950 | + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) | |
5951 | + /* No choice either */ | |
5952 | + return INVALID_PORT; | |
5953 | + | |
5954 | + return ntohs(flags & IPSET_SRC ? | |
5955 | + udph.source : udph.dest); | |
5956 | + } | |
5957 | + default: | |
5958 | + return INVALID_PORT; | |
5959 | + } | |
5960 | +} | |
5961 | + | |
5962 | +static inline int | |
5963 | +__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) | |
5964 | +{ | |
5965 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
5966 | + | |
5967 | + if (port < map->first_port || port > map->last_port) | |
5968 | + return -ERANGE; | |
5969 | + | |
5970 | + *hash_port = port; | |
5971 | + DP("set: %s, port:%u, %u", set->name, port, *hash_port); | |
5972 | + return !!test_bit(port - map->first_port, map->members); | |
5973 | +} | |
5974 | + | |
5975 | +static int | |
5976 | +testport(struct ip_set *set, const void *data, size_t size, | |
5977 | + ip_set_ip_t *hash_port) | |
5978 | +{ | |
5979 | + struct ip_set_req_portmap *req = | |
5980 | + (struct ip_set_req_portmap *) data; | |
5981 | + | |
5982 | + if (size != sizeof(struct ip_set_req_portmap)) { | |
5983 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
5984 | + sizeof(struct ip_set_req_portmap), | |
5985 | + size); | |
5986 | + return -EINVAL; | |
5987 | + } | |
5988 | + return __testport(set, req->port, hash_port); | |
5989 | +} | |
5990 | + | |
5991 | +static int | |
5992 | +testport_kernel(struct ip_set *set, | |
5993 | + const struct sk_buff *skb, | |
5994 | + ip_set_ip_t *hash_port, | |
5995 | + const u_int32_t *flags, | |
5996 | + unsigned char index) | |
5997 | +{ | |
5998 | + int res; | |
5999 | + ip_set_ip_t port = get_port(skb, flags[index]); | |
6000 | + | |
6001 | + DP("flag %s port %u", flags[index] & IPSET_SRC ? "SRC" : "DST", port); | |
6002 | + if (port == INVALID_PORT) | |
6003 | + return 0; | |
6004 | + | |
6005 | + res = __testport(set, port, hash_port); | |
6006 | + | |
6007 | + return (res < 0 ? 0 : res); | |
6008 | +} | |
6009 | + | |
6010 | +static inline int | |
6011 | +__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) | |
6012 | +{ | |
6013 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6014 | + | |
6015 | + if (port < map->first_port || port > map->last_port) | |
6016 | + return -ERANGE; | |
6017 | + if (test_and_set_bit(port - map->first_port, map->members)) | |
6018 | + return -EEXIST; | |
6019 | + | |
6020 | + *hash_port = port; | |
6021 | + DP("port %u", port); | |
6022 | + return 0; | |
6023 | +} | |
6024 | + | |
6025 | +static int | |
6026 | +addport(struct ip_set *set, const void *data, size_t size, | |
6027 | + ip_set_ip_t *hash_port) | |
6028 | +{ | |
6029 | + struct ip_set_req_portmap *req = | |
6030 | + (struct ip_set_req_portmap *) data; | |
6031 | + | |
6032 | + if (size != sizeof(struct ip_set_req_portmap)) { | |
6033 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
6034 | + sizeof(struct ip_set_req_portmap), | |
6035 | + size); | |
6036 | + return -EINVAL; | |
6037 | + } | |
6038 | + return __addport(set, req->port, hash_port); | |
6039 | +} | |
6040 | + | |
6041 | +static int | |
6042 | +addport_kernel(struct ip_set *set, | |
6043 | + const struct sk_buff *skb, | |
6044 | + ip_set_ip_t *hash_port, | |
6045 | + const u_int32_t *flags, | |
6046 | + unsigned char index) | |
6047 | +{ | |
6048 | + ip_set_ip_t port = get_port(skb, flags[index]); | |
6049 | + | |
6050 | + if (port == INVALID_PORT) | |
6051 | + return -EINVAL; | |
6052 | + | |
6053 | + return __addport(set, port, hash_port); | |
6054 | +} | |
6055 | + | |
6056 | +static inline int | |
6057 | +__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) | |
6058 | +{ | |
6059 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6060 | + | |
6061 | + if (port < map->first_port || port > map->last_port) | |
6062 | + return -ERANGE; | |
6063 | + if (!test_and_clear_bit(port - map->first_port, map->members)) | |
6064 | + return -EEXIST; | |
6065 | + | |
6066 | + *hash_port = port; | |
6067 | + DP("port %u", port); | |
6068 | + return 0; | |
6069 | +} | |
6070 | + | |
6071 | +static int | |
6072 | +delport(struct ip_set *set, const void *data, size_t size, | |
6073 | + ip_set_ip_t *hash_port) | |
6074 | +{ | |
6075 | + struct ip_set_req_portmap *req = | |
6076 | + (struct ip_set_req_portmap *) data; | |
6077 | + | |
6078 | + if (size != sizeof(struct ip_set_req_portmap)) { | |
6079 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
6080 | + sizeof(struct ip_set_req_portmap), | |
6081 | + size); | |
6082 | + return -EINVAL; | |
6083 | + } | |
6084 | + return __delport(set, req->port, hash_port); | |
6085 | +} | |
6086 | + | |
6087 | +static int | |
6088 | +delport_kernel(struct ip_set *set, | |
6089 | + const struct sk_buff *skb, | |
6090 | + ip_set_ip_t *hash_port, | |
6091 | + const u_int32_t *flags, | |
6092 | + unsigned char index) | |
6093 | +{ | |
6094 | + ip_set_ip_t port = get_port(skb, flags[index]); | |
6095 | + | |
6096 | + if (port == INVALID_PORT) | |
6097 | + return -EINVAL; | |
6098 | + | |
6099 | + return __delport(set, port, hash_port); | |
6100 | +} | |
6101 | + | |
6102 | +static int create(struct ip_set *set, const void *data, size_t size) | |
6103 | +{ | |
6104 | + int newbytes; | |
6105 | + struct ip_set_req_portmap_create *req = | |
6106 | + (struct ip_set_req_portmap_create *) data; | |
6107 | + struct ip_set_portmap *map; | |
6108 | + | |
6109 | + if (size != sizeof(struct ip_set_req_portmap_create)) { | |
6110 | + ip_set_printk("data length wrong (want %zu, have %zu)", | |
6111 | + sizeof(struct ip_set_req_portmap_create), | |
6112 | + size); | |
6113 | + return -EINVAL; | |
6114 | + } | |
6115 | + | |
6116 | + DP("from %u to %u", req->from, req->to); | |
6117 | + | |
6118 | + if (req->from > req->to) { | |
6119 | + DP("bad port range"); | |
6120 | + return -ENOEXEC; | |
6121 | + } | |
6122 | + | |
6123 | + if (req->to - req->from > MAX_RANGE) { | |
6124 | + ip_set_printk("range too big (max %d ports)", | |
6125 | + MAX_RANGE+1); | |
6126 | + return -ENOEXEC; | |
6127 | + } | |
6128 | + | |
6129 | + map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); | |
6130 | + if (!map) { | |
6131 | + DP("out of memory for %d bytes", | |
6132 | + sizeof(struct ip_set_portmap)); | |
6133 | + return -ENOMEM; | |
6134 | + } | |
6135 | + map->first_port = req->from; | |
6136 | + map->last_port = req->to; | |
6137 | + newbytes = bitmap_bytes(req->from, req->to); | |
6138 | + map->members = kmalloc(newbytes, GFP_KERNEL); | |
6139 | + if (!map->members) { | |
6140 | + DP("out of memory for %d bytes", newbytes); | |
6141 | + kfree(map); | |
6142 | + return -ENOMEM; | |
6143 | + } | |
6144 | + memset(map->members, 0, newbytes); | |
6145 | + | |
6146 | + set->data = map; | |
6147 | + return 0; | |
6148 | +} | |
6149 | + | |
6150 | +static void destroy(struct ip_set *set) | |
6151 | +{ | |
6152 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6153 | + | |
6154 | + kfree(map->members); | |
6155 | + kfree(map); | |
6156 | + | |
6157 | + set->data = NULL; | |
6158 | +} | |
6159 | + | |
6160 | +static void flush(struct ip_set *set) | |
6161 | +{ | |
6162 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6163 | + memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); | |
6164 | +} | |
6165 | + | |
6166 | +static void list_header(const struct ip_set *set, void *data) | |
6167 | +{ | |
6168 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6169 | + struct ip_set_req_portmap_create *header = | |
6170 | + (struct ip_set_req_portmap_create *) data; | |
6171 | + | |
6172 | + DP("list_header %u %u", map->first_port, map->last_port); | |
6173 | + | |
6174 | + header->from = map->first_port; | |
6175 | + header->to = map->last_port; | |
6176 | +} | |
6177 | + | |
6178 | +static int list_members_size(const struct ip_set *set) | |
6179 | +{ | |
6180 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6181 | + | |
6182 | + return bitmap_bytes(map->first_port, map->last_port); | |
6183 | +} | |
6184 | + | |
6185 | +static void list_members(const struct ip_set *set, void *data) | |
6186 | +{ | |
6187 | + struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; | |
6188 | + int bytes = bitmap_bytes(map->first_port, map->last_port); | |
6189 | + | |
6190 | + memcpy(data, map->members, bytes); | |
6191 | +} | |
6192 | + | |
6193 | +static struct ip_set_type ip_set_portmap = { | |
6194 | + .typename = SETTYPE_NAME, | |
6195 | + .features = IPSET_TYPE_PORT | IPSET_DATA_SINGLE, | |
6196 | + .protocol_version = IP_SET_PROTOCOL_VERSION, | |
6197 | + .create = &create, | |
6198 | + .destroy = &destroy, | |
6199 | + .flush = &flush, | |
6200 | + .reqsize = sizeof(struct ip_set_req_portmap), | |
6201 | + .addip = &addport, | |
6202 | + .addip_kernel = &addport_kernel, | |
6203 | + .delip = &delport, | |
6204 | + .delip_kernel = &delport_kernel, | |
6205 | + .testip = &testport, | |
6206 | + .testip_kernel = &testport_kernel, | |
6207 | + .header_size = sizeof(struct ip_set_req_portmap_create), | |
6208 | + .list_header = &list_header, | |
6209 | + .list_members_size = &list_members_size, | |
6210 | + .list_members = &list_members, | |
6211 | + .me = THIS_MODULE, | |
6212 | +}; | |
6213 | + | |
6214 | +MODULE_LICENSE("GPL"); | |
6215 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
6216 | +MODULE_DESCRIPTION("portmap type of IP sets"); | |
6217 | + | |
6218 | +static int __init init(void) | |
6219 | +{ | |
6220 | + return ip_set_register_set_type(&ip_set_portmap); | |
6221 | +} | |
6222 | + | |
6223 | +static void __exit fini(void) | |
6224 | +{ | |
6225 | + /* FIXME: possible race with ip_set_create() */ | |
6226 | + ip_set_unregister_set_type(&ip_set_portmap); | |
6227 | +} | |
6228 | + | |
6229 | +module_init(init); | |
6230 | +module_exit(fini); | |
6231 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_SET.c linux/net/ipv4/netfilter/ipt_SET.c | |
6232 | --- linux.org/net/ipv4/netfilter/ipt_SET.c 1970-01-01 01:00:00.000000000 +0100 | |
6233 | +++ linux/net/ipv4/netfilter/ipt_SET.c 2006-05-04 10:26:33.000000000 +0200 | |
6234 | @@ -0,0 +1,128 @@ | |
6235 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
6236 | + * Patrick Schaaf <bof@bof.de> | |
6237 | + * Martin Josefsson <gandalf@wlug.westbo.se> | |
6238 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
6239 | + * | |
6240 | + * This program is free software; you can redistribute it and/or modify | |
6241 | + * it under the terms of the GNU General Public License version 2 as | |
6242 | + * published by the Free Software Foundation. | |
6243 | + */ | |
6244 | + | |
6245 | +/* ipt_SET.c - netfilter target to manipulate IP sets */ | |
6246 | + | |
6247 | +#include <linux/types.h> | |
6248 | +#include <linux/ip.h> | |
6249 | +#include <linux/timer.h> | |
6250 | +#include <linux/module.h> | |
6251 | +#include <linux/netfilter.h> | |
6252 | +#include <linux/netdevice.h> | |
6253 | +#include <linux/if.h> | |
6254 | +#include <linux/inetdevice.h> | |
6255 | +#include <net/protocol.h> | |
6256 | +#include <net/checksum.h> | |
6257 | +#include <linux/netfilter_ipv4.h> | |
6258 | +#include <linux/netfilter_ipv4/ip_nat_rule.h> | |
6259 | +#include <linux/netfilter_ipv4/ipt_set.h> | |
6260 | + | |
6261 | +static unsigned int | |
6262 | +target(struct sk_buff **pskb, | |
6263 | + const struct net_device *in, | |
6264 | + const struct net_device *out, | |
6265 | + unsigned int hooknum, | |
6266 | + const void *targinfo, | |
6267 | + void *userinfo) | |
6268 | +{ | |
6269 | + const struct ipt_set_info_target *info = targinfo; | |
6270 | + | |
6271 | + if (info->add_set.index != IP_SET_INVALID_ID) | |
6272 | + ip_set_addip_kernel(info->add_set.index, | |
6273 | + *pskb, | |
6274 | + info->add_set.flags); | |
6275 | + if (info->del_set.index != IP_SET_INVALID_ID) | |
6276 | + ip_set_delip_kernel(info->del_set.index, | |
6277 | + *pskb, | |
6278 | + info->del_set.flags); | |
6279 | + | |
6280 | + return IPT_CONTINUE; | |
6281 | +} | |
6282 | + | |
6283 | +static int | |
6284 | +checkentry(const char *tablename, | |
6285 | + const struct ipt_entry *e, | |
6286 | + void *targinfo, | |
6287 | + unsigned int targinfosize, unsigned int hook_mask) | |
6288 | +{ | |
6289 | + struct ipt_set_info_target *info = | |
6290 | + (struct ipt_set_info_target *) targinfo; | |
6291 | + ip_set_id_t index; | |
6292 | + | |
6293 | + if (targinfosize != IPT_ALIGN(sizeof(*info))) { | |
6294 | + DP("bad target info size %u", targinfosize); | |
6295 | + return 0; | |
6296 | + } | |
6297 | + | |
6298 | + if (info->add_set.index != IP_SET_INVALID_ID) { | |
6299 | + index = ip_set_get_byindex(info->add_set.index); | |
6300 | + if (index == IP_SET_INVALID_ID) { | |
6301 | + ip_set_printk("cannot find add_set index %u as target", | |
6302 | + info->add_set.index); | |
6303 | + return 0; /* error */ | |
6304 | + } | |
6305 | + } | |
6306 | + | |
6307 | + if (info->del_set.index != IP_SET_INVALID_ID) { | |
6308 | + index = ip_set_get_byindex(info->del_set.index); | |
6309 | + if (index == IP_SET_INVALID_ID) { | |
6310 | + ip_set_printk("cannot find del_set index %u as target", | |
6311 | + info->del_set.index); | |
6312 | + return 0; /* error */ | |
6313 | + } | |
6314 | + } | |
6315 | + if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 | |
6316 | + || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { | |
6317 | + ip_set_printk("That's nasty!"); | |
6318 | + return 0; /* error */ | |
6319 | + } | |
6320 | + | |
6321 | + return 1; | |
6322 | +} | |
6323 | + | |
6324 | +static void destroy(void *targetinfo, unsigned int targetsize) | |
6325 | +{ | |
6326 | + struct ipt_set_info_target *info = targetinfo; | |
6327 | + | |
6328 | + if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { | |
6329 | + ip_set_printk("invalid targetsize %d", targetsize); | |
6330 | + return; | |
6331 | + } | |
6332 | + | |
6333 | + if (info->add_set.index != IP_SET_INVALID_ID) | |
6334 | + ip_set_put(info->add_set.index); | |
6335 | + if (info->del_set.index != IP_SET_INVALID_ID) | |
6336 | + ip_set_put(info->del_set.index); | |
6337 | +} | |
6338 | + | |
6339 | +static struct ipt_target SET_target = { | |
6340 | + .name = "SET", | |
6341 | + .target = target, | |
6342 | + .checkentry = checkentry, | |
6343 | + .destroy = destroy, | |
6344 | + .me = THIS_MODULE | |
6345 | +}; | |
6346 | + | |
6347 | +MODULE_LICENSE("GPL"); | |
6348 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
6349 | +MODULE_DESCRIPTION("iptables IP set target module"); | |
6350 | + | |
6351 | +static int __init init(void) | |
6352 | +{ | |
6353 | + return ipt_register_target(&SET_target); | |
6354 | +} | |
6355 | + | |
6356 | +static void __exit fini(void) | |
6357 | +{ | |
6358 | + ipt_unregister_target(&SET_target); | |
6359 | +} | |
6360 | + | |
6361 | +module_init(init); | |
6362 | +module_exit(fini); | |
6363 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_set.c linux/net/ipv4/netfilter/ipt_set.c | |
6364 | --- linux.org/net/ipv4/netfilter/ipt_set.c 1970-01-01 01:00:00.000000000 +0100 | |
6365 | +++ linux/net/ipv4/netfilter/ipt_set.c 2006-05-04 10:26:33.000000000 +0200 | |
6366 | @@ -0,0 +1,112 @@ | |
6367 | +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
6368 | + * Patrick Schaaf <bof@bof.de> | |
6369 | + * Martin Josefsson <gandalf@wlug.westbo.se> | |
6370 | + * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
6371 | + * | |
6372 | + * This program is free software; you can redistribute it and/or modify | |
6373 | + * it under the terms of the GNU General Public License version 2 as | |
6374 | + * published by the Free Software Foundation. | |
6375 | + */ | |
6376 | + | |
6377 | +/* Kernel module to match an IP set. */ | |
6378 | + | |
6379 | +#include <linux/module.h> | |
6380 | +#include <linux/ip.h> | |
6381 | +#include <linux/skbuff.h> | |
6382 | + | |
6383 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
6384 | +#include <linux/netfilter_ipv4/ip_set.h> | |
6385 | +#include <linux/netfilter_ipv4/ipt_set.h> | |
6386 | + | |
6387 | +static inline int | |
6388 | +match_set(const struct ipt_set_info *info, | |
6389 | + const struct sk_buff *skb, | |
6390 | + int inv) | |
6391 | +{ | |
6392 | + if (ip_set_testip_kernel(info->index, skb, info->flags)) | |
6393 | + inv = !inv; | |
6394 | + return inv; | |
6395 | +} | |
6396 | + | |
6397 | +static int | |
6398 | +match(const struct sk_buff *skb, | |
6399 | + const struct net_device *in, | |
6400 | + const struct net_device *out, | |
6401 | + const void *matchinfo, | |
6402 | + int offset, | |
6403 | + int *hotdrop) | |
6404 | +{ | |
6405 | + const struct ipt_set_info_match *info = matchinfo; | |
6406 | + | |
6407 | + return match_set(&info->match_set, | |
6408 | + skb, | |
6409 | + info->match_set.flags[0] & IPSET_MATCH_INV); | |
6410 | +} | |
6411 | + | |
6412 | +static int | |
6413 | +checkentry(const char *tablename, | |
6414 | + const struct ipt_ip *ip, | |
6415 | + void *matchinfo, | |
6416 | + unsigned int matchsize, | |
6417 | + unsigned int hook_mask) | |
6418 | +{ | |
6419 | + struct ipt_set_info_match *info = | |
6420 | + (struct ipt_set_info_match *) matchinfo; | |
6421 | + ip_set_id_t index; | |
6422 | + | |
6423 | + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { | |
6424 | + ip_set_printk("invalid matchsize %d", matchsize); | |
6425 | + return 0; | |
6426 | + } | |
6427 | + | |
6428 | + index = ip_set_get_byindex(info->match_set.index); | |
6429 | + | |
6430 | + if (index == IP_SET_INVALID_ID) { | |
6431 | + ip_set_printk("Cannot find set indentified by id %u to match", | |
6432 | + info->match_set.index); | |
6433 | + return 0; /* error */ | |
6434 | + } | |
6435 | + if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { | |
6436 | + ip_set_printk("That's nasty!"); | |
6437 | + return 0; /* error */ | |
6438 | + } | |
6439 | + | |
6440 | + return 1; | |
6441 | +} | |
6442 | + | |
6443 | +static void destroy(void *matchinfo, unsigned int matchsize) | |
6444 | +{ | |
6445 | + struct ipt_set_info_match *info = matchinfo; | |
6446 | + | |
6447 | + if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { | |
6448 | + ip_set_printk("invalid matchsize %d", matchsize); | |
6449 | + return; | |
6450 | + } | |
6451 | + | |
6452 | + ip_set_put(info->match_set.index); | |
6453 | +} | |
6454 | + | |
6455 | +static struct ipt_match set_match = { | |
6456 | + .name = "set", | |
6457 | + .match = &match, | |
6458 | + .checkentry = &checkentry, | |
6459 | + .destroy = &destroy, | |
6460 | + .me = THIS_MODULE | |
6461 | +}; | |
6462 | + | |
6463 | +MODULE_LICENSE("GPL"); | |
6464 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
6465 | +MODULE_DESCRIPTION("iptables IP set match module"); | |
6466 | + | |
6467 | +static int __init init(void) | |
6468 | +{ | |
6469 | + return ipt_register_match(&set_match); | |
6470 | +} | |
6471 | + | |
6472 | +static void __exit fini(void) | |
6473 | +{ | |
6474 | + ipt_unregister_match(&set_match); | |
6475 | +} | |
6476 | + | |
6477 | +module_init(init); | |
6478 | +module_exit(fini); |