1 /* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
8 /* Kernel module implementing an ip+port hash set */
10 #include <linux/module.h>
12 #include <linux/tcp.h>
13 #include <linux/udp.h>
14 #include <linux/skbuff.h>
15 #include <linux/netfilter_ipv4/ip_tables.h>
16 #include <linux/netfilter_ipv4/ip_set.h>
17 #include <linux/errno.h>
18 #include <asm/uaccess.h>
19 #include <asm/bitops.h>
20 #include <linux/spinlock.h>
21 #include <linux/vmalloc.h>
22 #include <linux/random.h>
26 #include <linux/netfilter_ipv4/ip_set_malloc.h>
27 #include <linux/netfilter_ipv4/ip_set_ipporthash.h>
28 #include <linux/netfilter_ipv4/ip_set_jhash.h>
30 /* We must handle non-linear skbs */
31 static inline ip_set_ip_t
32 get_port(const struct sk_buff *skb, u_int32_t flags)
34 struct iphdr *iph = skb->nh.iph;
35 u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET;
37 switch (iph->protocol) {
41 /* See comments at tcp_match in ip_tables.c */
45 if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0)
46 /* No choice either */
49 return ntohs(flags & IPSET_SRC ?
50 tcph.source : tcph.dest);
58 if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0)
59 /* No choice either */
62 return ntohs(flags & IPSET_SRC ?
63 udph.source : udph.dest);
71 jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip)
73 return jhash_1word(ip, *(((uint32_t *) map->initval) + i));
76 #define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16))
79 hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port,
82 struct ip_set_ipporthash *map =
83 (struct ip_set_ipporthash *) set->data;
88 *hash_ip = HASH_IP(map, ip, port);
89 DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u",
90 set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip));
92 for (i = 0; i < map->probes; i++) {
93 id = jhash_ip(map, i, *hash_ip) % map->hashsize;
94 DP("hash key: %u", id);
95 elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
96 if (*elem == *hash_ip)
98 /* No shortcut at testing - there can be deleted
105 __testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port,
106 ip_set_ip_t *hash_ip)
108 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
110 if (ip < map->first_ip || ip > map->last_ip)
113 return (hash_id(set, ip, port, hash_ip) != UINT_MAX);
117 testip(struct ip_set *set, const void *data, size_t size,
118 ip_set_ip_t *hash_ip)
120 struct ip_set_req_ipporthash *req =
121 (struct ip_set_req_ipporthash *) data;
123 if (size != sizeof(struct ip_set_req_ipporthash)) {
124 ip_set_printk("data length wrong (want %zu, have %zu)",
125 sizeof(struct ip_set_req_ipporthash),
129 return __testip(set, req->ip, req->port, hash_ip);
133 testip_kernel(struct ip_set *set,
134 const struct sk_buff *skb,
135 ip_set_ip_t *hash_ip,
136 const u_int32_t *flags,
141 if (flags[index+1] == 0)
144 port = get_port(skb, flags[index+1]);
146 DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
147 flags[index] & IPSET_SRC ? "SRC" : "DST",
148 NIPQUAD(skb->nh.iph->saddr),
149 NIPQUAD(skb->nh.iph->daddr));
150 DP("flag %s port %u",
151 flags[index+1] & IPSET_SRC ? "SRC" : "DST",
153 if (port == INVALID_PORT)
157 ntohl(flags[index] & IPSET_SRC
159 : skb->nh.iph->daddr),
165 __add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip)
171 for (i = 0; i < map->probes; i++) {
172 probe = jhash_ip(map, i, hash_ip) % map->hashsize;
173 elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe);
174 if (*elem == hash_ip)
181 /* Trigger rehashing */
186 __addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port,
187 ip_set_ip_t *hash_ip)
189 if (ip < map->first_ip || ip > map->last_ip)
192 *hash_ip = HASH_IP(map, ip, port);
194 return __add_haship(map, *hash_ip);
198 addip(struct ip_set *set, const void *data, size_t size,
199 ip_set_ip_t *hash_ip)
201 struct ip_set_req_ipporthash *req =
202 (struct ip_set_req_ipporthash *) data;
204 if (size != sizeof(struct ip_set_req_ipporthash)) {
205 ip_set_printk("data length wrong (want %zu, have %zu)",
206 sizeof(struct ip_set_req_ipporthash),
210 return __addip((struct ip_set_ipporthash *) set->data,
211 req->ip, req->port, hash_ip);
215 addip_kernel(struct ip_set *set,
216 const struct sk_buff *skb,
217 ip_set_ip_t *hash_ip,
218 const u_int32_t *flags,
223 if (flags[index+1] == 0)
226 port = get_port(skb, flags[index+1]);
228 DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
229 flags[index] & IPSET_SRC ? "SRC" : "DST",
230 NIPQUAD(skb->nh.iph->saddr),
231 NIPQUAD(skb->nh.iph->daddr));
232 DP("flag %s port %u",
233 flags[index+1] & IPSET_SRC ? "SRC" : "DST",
235 if (port == INVALID_PORT)
238 return __addip((struct ip_set_ipporthash *) set->data,
239 ntohl(flags[index] & IPSET_SRC
241 : skb->nh.iph->daddr),
246 static int retry(struct ip_set *set)
248 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
251 u_int32_t i, hashsize = map->hashsize;
253 struct ip_set_ipporthash *tmp;
255 if (map->resize == 0)
261 /* Calculate new hash size */
262 hashsize += (hashsize * map->resize)/100;
263 if (hashsize == map->hashsize)
266 ip_set_printk("rehashing of set %s triggered: "
267 "hashsize grows from %u to %u",
268 set->name, map->hashsize, hashsize);
270 tmp = kmalloc(sizeof(struct ip_set_ipporthash)
271 + map->probes * sizeof(uint32_t), GFP_ATOMIC);
273 DP("out of memory for %d bytes",
274 sizeof(struct ip_set_ipporthash)
275 + map->probes * sizeof(uint32_t));
278 tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC);
280 DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t));
284 tmp->hashsize = hashsize;
285 tmp->probes = map->probes;
286 tmp->resize = map->resize;
287 tmp->first_ip = map->first_ip;
288 tmp->last_ip = map->last_ip;
289 memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t));
291 write_lock_bh(&set->lock);
292 map = (struct ip_set_ipporthash *) set->data; /* Play safe */
293 for (i = 0; i < map->hashsize && res == 0; i++) {
294 elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i);
296 res = __add_haship(tmp, *elem);
299 /* Failure, try again */
300 write_unlock_bh(&set->lock);
301 harray_free(tmp->members);
306 /* Success at resizing! */
307 members = map->members;
309 map->hashsize = tmp->hashsize;
310 map->members = tmp->members;
311 write_unlock_bh(&set->lock);
313 harray_free(members);
320 __delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port,
321 ip_set_ip_t *hash_ip)
323 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
327 if (ip < map->first_ip || ip > map->last_ip)
330 id = hash_id(set, ip, port, hash_ip);
335 elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id);
342 delip(struct ip_set *set, const void *data, size_t size,
343 ip_set_ip_t *hash_ip)
345 struct ip_set_req_ipporthash *req =
346 (struct ip_set_req_ipporthash *) data;
348 if (size != sizeof(struct ip_set_req_ipporthash)) {
349 ip_set_printk("data length wrong (want %zu, have %zu)",
350 sizeof(struct ip_set_req_ipporthash),
354 return __delip(set, req->ip, req->port, hash_ip);
358 delip_kernel(struct ip_set *set,
359 const struct sk_buff *skb,
360 ip_set_ip_t *hash_ip,
361 const u_int32_t *flags,
366 if (flags[index+1] == 0)
369 port = get_port(skb, flags[index+1]);
371 DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
372 flags[index] & IPSET_SRC ? "SRC" : "DST",
373 NIPQUAD(skb->nh.iph->saddr),
374 NIPQUAD(skb->nh.iph->daddr));
375 DP("flag %s port %u",
376 flags[index+1] & IPSET_SRC ? "SRC" : "DST",
378 if (port == INVALID_PORT)
382 ntohl(flags[index] & IPSET_SRC
384 : skb->nh.iph->daddr),
389 static int create(struct ip_set *set, const void *data, size_t size)
391 struct ip_set_req_ipporthash_create *req =
392 (struct ip_set_req_ipporthash_create *) data;
393 struct ip_set_ipporthash *map;
396 if (size != sizeof(struct ip_set_req_ipporthash_create)) {
397 ip_set_printk("data length wrong (want %zu, have %zu)",
398 sizeof(struct ip_set_req_ipporthash_create),
403 if (req->hashsize < 1) {
404 ip_set_printk("hashsize too small");
408 if (req->probes < 1) {
409 ip_set_printk("probes too small");
413 map = kmalloc(sizeof(struct ip_set_ipporthash)
414 + req->probes * sizeof(uint32_t), GFP_KERNEL);
416 DP("out of memory for %d bytes",
417 sizeof(struct ip_set_ipporthash)
418 + req->probes * sizeof(uint32_t));
421 for (i = 0; i < req->probes; i++)
422 get_random_bytes(((uint32_t *) map->initval)+i, 4);
423 map->hashsize = req->hashsize;
424 map->probes = req->probes;
425 map->resize = req->resize;
426 map->first_ip = req->from;
427 map->last_ip = req->to;
428 map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL);
430 DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t));
439 static void destroy(struct ip_set *set)
441 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
443 harray_free(map->members);
449 static void flush(struct ip_set *set)
451 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
452 harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t));
455 static void list_header(const struct ip_set *set, void *data)
457 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
458 struct ip_set_req_ipporthash_create *header =
459 (struct ip_set_req_ipporthash_create *) data;
461 header->hashsize = map->hashsize;
462 header->probes = map->probes;
463 header->resize = map->resize;
464 header->from = map->first_ip;
465 header->to = map->last_ip;
468 static int list_members_size(const struct ip_set *set)
470 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
472 return (map->hashsize * sizeof(ip_set_ip_t));
475 static void list_members(const struct ip_set *set, void *data)
477 struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data;
478 ip_set_ip_t i, *elem;
480 for (i = 0; i < map->hashsize; i++) {
481 elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i);
482 ((ip_set_ip_t *)data)[i] = *elem;
486 static struct ip_set_type ip_set_ipporthash = {
487 .typename = SETTYPE_NAME,
488 .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE,
489 .protocol_version = IP_SET_PROTOCOL_VERSION,
493 .reqsize = sizeof(struct ip_set_req_ipporthash),
495 .addip_kernel = &addip_kernel,
498 .delip_kernel = &delip_kernel,
500 .testip_kernel = &testip_kernel,
501 .header_size = sizeof(struct ip_set_req_ipporthash_create),
502 .list_header = &list_header,
503 .list_members_size = &list_members_size,
504 .list_members = &list_members,
508 MODULE_LICENSE("GPL");
509 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
510 MODULE_DESCRIPTION("ipporthash type of IP sets");
512 static int __init init(void)
514 return ip_set_register_set_type(&ip_set_ipporthash);
517 static void __exit fini(void)
519 /* FIXME: possible race with ip_set_create() */
520 ip_set_unregister_set_type(&ip_set_ipporthash);