~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to net/netfilter/ipset/ip_set_hash_net.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
 
2
 *
 
3
 * This program is free software; you can redistribute it and/or modify
 
4
 * it under the terms of the GNU General Public License version 2 as
 
5
 * published by the Free Software Foundation.
 
6
 */
 
7
 
 
8
/* Kernel module implementing an IP set type: the hash:net type */
 
9
 
 
10
#include <linux/jhash.h>
 
11
#include <linux/module.h>
 
12
#include <linux/ip.h>
 
13
#include <linux/skbuff.h>
 
14
#include <linux/errno.h>
 
15
#include <linux/random.h>
 
16
#include <net/ip.h>
 
17
#include <net/ipv6.h>
 
18
#include <net/netlink.h>
 
19
 
 
20
#include <linux/netfilter.h>
 
21
#include <linux/netfilter/ipset/pfxlen.h>
 
22
#include <linux/netfilter/ipset/ip_set.h>
 
23
#include <linux/netfilter/ipset/ip_set_timeout.h>
 
24
#include <linux/netfilter/ipset/ip_set_hash.h>
 
25
 
 
26
MODULE_LICENSE("GPL");
 
27
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 
28
MODULE_DESCRIPTION("hash:net type of IP sets");
 
29
MODULE_ALIAS("ip_set_hash:net");
 
30
 
 
31
/* Type specific function prefix */
 
32
#define TYPE            hash_net
 
33
 
 
34
static bool
 
35
hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
 
36
 
 
37
#define hash_net4_same_set      hash_net_same_set
 
38
#define hash_net6_same_set      hash_net_same_set
 
39
 
 
40
/* The type variant functions: IPv4 */
 
41
 
 
42
/* Member elements without timeout */
 
43
struct hash_net4_elem {
 
44
        __be32 ip;
 
45
        u16 padding0;
 
46
        u8 padding1;
 
47
        u8 cidr;
 
48
};
 
49
 
 
50
/* Member elements with timeout support */
 
51
struct hash_net4_telem {
 
52
        __be32 ip;
 
53
        u16 padding0;
 
54
        u8 padding1;
 
55
        u8 cidr;
 
56
        unsigned long timeout;
 
57
};
 
58
 
 
59
static inline bool
 
60
hash_net4_data_equal(const struct hash_net4_elem *ip1,
 
61
                     const struct hash_net4_elem *ip2,
 
62
                     u32 *multi)
 
63
{
 
64
        return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
 
65
}
 
66
 
 
67
static inline bool
 
68
hash_net4_data_isnull(const struct hash_net4_elem *elem)
 
69
{
 
70
        return elem->cidr == 0;
 
71
}
 
72
 
 
73
static inline void
 
74
hash_net4_data_copy(struct hash_net4_elem *dst,
 
75
                    const struct hash_net4_elem *src)
 
76
{
 
77
        dst->ip = src->ip;
 
78
        dst->cidr = src->cidr;
 
79
}
 
80
 
 
81
static inline void
 
82
hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
 
83
{
 
84
        elem->ip &= ip_set_netmask(cidr);
 
85
        elem->cidr = cidr;
 
86
}
 
87
 
 
88
/* Zero CIDR values cannot be stored */
 
89
static inline void
 
90
hash_net4_data_zero_out(struct hash_net4_elem *elem)
 
91
{
 
92
        elem->cidr = 0;
 
93
}
 
94
 
 
95
static bool
 
96
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
 
97
{
 
98
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
 
99
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
 
100
        return 0;
 
101
 
 
102
nla_put_failure:
 
103
        return 1;
 
104
}
 
105
 
 
106
static bool
 
107
hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
 
108
{
 
109
        const struct hash_net4_telem *tdata =
 
110
                (const struct hash_net4_telem *)data;
 
111
 
 
112
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
 
113
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
 
114
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
 
115
                      htonl(ip_set_timeout_get(tdata->timeout)));
 
116
 
 
117
        return 0;
 
118
 
 
119
nla_put_failure:
 
120
        return 1;
 
121
}
 
122
 
 
123
#define IP_SET_HASH_WITH_NETS
 
124
 
 
125
#define PF              4
 
126
#define HOST_MASK       32
 
127
#include <linux/netfilter/ipset/ip_set_ahash.h>
 
128
 
 
129
static inline void
 
130
hash_net4_data_next(struct ip_set_hash *h,
 
131
                    const struct hash_net4_elem *d)
 
132
{
 
133
        h->next.ip = ntohl(d->ip);
 
134
}
 
135
 
 
136
static int
 
137
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
 
138
               const struct xt_action_param *par,
 
139
               enum ipset_adt adt, const struct ip_set_adt_opt *opt)
 
140
{
 
141
        const struct ip_set_hash *h = set->data;
 
142
        ipset_adtfn adtfn = set->variant->adt[adt];
 
143
        struct hash_net4_elem data = {
 
144
                .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
 
145
        };
 
146
 
 
147
        if (data.cidr == 0)
 
148
                return -EINVAL;
 
149
        if (adt == IPSET_TEST)
 
150
                data.cidr = HOST_MASK;
 
151
 
 
152
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
 
153
        data.ip &= ip_set_netmask(data.cidr);
 
154
 
 
155
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 
156
}
 
157
 
 
158
static int
 
159
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 
160
               enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 
161
{
 
162
        const struct ip_set_hash *h = set->data;
 
163
        ipset_adtfn adtfn = set->variant->adt[adt];
 
164
        struct hash_net4_elem data = { .cidr = HOST_MASK };
 
165
        u32 timeout = h->timeout;
 
166
        u32 ip = 0, ip_to, last;
 
167
        int ret;
 
168
 
 
169
        if (unlikely(!tb[IPSET_ATTR_IP] ||
 
170
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 
171
                return -IPSET_ERR_PROTOCOL;
 
172
 
 
173
        if (tb[IPSET_ATTR_LINENO])
 
174
                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
175
 
 
176
        ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
 
177
        if (ret)
 
178
                return ret;
 
179
 
 
180
        if (tb[IPSET_ATTR_CIDR]) {
 
181
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
182
                if (!data.cidr)
 
183
                        return -IPSET_ERR_INVALID_CIDR;
 
184
        }
 
185
 
 
186
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
187
                if (!with_timeout(h->timeout))
 
188
                        return -IPSET_ERR_TIMEOUT;
 
189
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
190
        }
 
191
 
 
192
        if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
 
193
                data.ip = htonl(ip & ip_set_hostmask(data.cidr));
 
194
                ret = adtfn(set, &data, timeout, flags);
 
195
                return ip_set_eexist(ret, flags) ? 0 : ret;
 
196
        }
 
197
 
 
198
        ip_to = ip;
 
199
        if (tb[IPSET_ATTR_IP_TO]) {
 
200
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
 
201
                if (ret)
 
202
                        return ret;
 
203
                if (ip_to < ip)
 
204
                        swap(ip, ip_to);
 
205
                if (ip + UINT_MAX == ip_to)
 
206
                        return -IPSET_ERR_HASH_RANGE;
 
207
        }
 
208
        if (retried)
 
209
                ip = h->next.ip;
 
210
        while (!after(ip, ip_to)) {
 
211
                data.ip = htonl(ip);
 
212
                last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
 
213
                ret = adtfn(set, &data, timeout, flags);
 
214
                if (ret && !ip_set_eexist(ret, flags))
 
215
                        return ret;
 
216
                else
 
217
                        ret = 0;
 
218
                ip = last + 1;
 
219
        }
 
220
        return ret;
 
221
}
 
222
 
 
223
static bool
 
224
hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
 
225
{
 
226
        const struct ip_set_hash *x = a->data;
 
227
        const struct ip_set_hash *y = b->data;
 
228
 
 
229
        /* Resizing changes htable_bits, so we ignore it */
 
230
        return x->maxelem == y->maxelem &&
 
231
               x->timeout == y->timeout;
 
232
}
 
233
 
 
234
/* The type variant functions: IPv6 */
 
235
 
 
236
struct hash_net6_elem {
 
237
        union nf_inet_addr ip;
 
238
        u16 padding0;
 
239
        u8 padding1;
 
240
        u8 cidr;
 
241
};
 
242
 
 
243
struct hash_net6_telem {
 
244
        union nf_inet_addr ip;
 
245
        u16 padding0;
 
246
        u8 padding1;
 
247
        u8 cidr;
 
248
        unsigned long timeout;
 
249
};
 
250
 
 
251
static inline bool
 
252
hash_net6_data_equal(const struct hash_net6_elem *ip1,
 
253
                     const struct hash_net6_elem *ip2,
 
254
                     u32 *multi)
 
255
{
 
256
        return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
 
257
               ip1->cidr == ip2->cidr;
 
258
}
 
259
 
 
260
static inline bool
 
261
hash_net6_data_isnull(const struct hash_net6_elem *elem)
 
262
{
 
263
        return elem->cidr == 0;
 
264
}
 
265
 
 
266
static inline void
 
267
hash_net6_data_copy(struct hash_net6_elem *dst,
 
268
                    const struct hash_net6_elem *src)
 
269
{
 
270
        ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
 
271
        dst->cidr = src->cidr;
 
272
}
 
273
 
 
274
static inline void
 
275
hash_net6_data_zero_out(struct hash_net6_elem *elem)
 
276
{
 
277
        elem->cidr = 0;
 
278
}
 
279
 
 
280
static inline void
 
281
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
 
282
{
 
283
        ip->ip6[0] &= ip_set_netmask6(prefix)[0];
 
284
        ip->ip6[1] &= ip_set_netmask6(prefix)[1];
 
285
        ip->ip6[2] &= ip_set_netmask6(prefix)[2];
 
286
        ip->ip6[3] &= ip_set_netmask6(prefix)[3];
 
287
}
 
288
 
 
289
static inline void
 
290
hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
 
291
{
 
292
        ip6_netmask(&elem->ip, cidr);
 
293
        elem->cidr = cidr;
 
294
}
 
295
 
 
296
static bool
 
297
hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
 
298
{
 
299
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
 
300
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
 
301
        return 0;
 
302
 
 
303
nla_put_failure:
 
304
        return 1;
 
305
}
 
306
 
 
307
static bool
 
308
hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
 
309
{
 
310
        const struct hash_net6_telem *e =
 
311
                (const struct hash_net6_telem *)data;
 
312
 
 
313
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
 
314
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
 
315
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
 
316
                      htonl(ip_set_timeout_get(e->timeout)));
 
317
        return 0;
 
318
 
 
319
nla_put_failure:
 
320
        return 1;
 
321
}
 
322
 
 
323
#undef PF
 
324
#undef HOST_MASK
 
325
 
 
326
#define PF              6
 
327
#define HOST_MASK       128
 
328
#include <linux/netfilter/ipset/ip_set_ahash.h>
 
329
 
 
330
static inline void
 
331
hash_net6_data_next(struct ip_set_hash *h,
 
332
                    const struct hash_net6_elem *d)
 
333
{
 
334
}
 
335
 
 
336
static int
 
337
hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
 
338
               const struct xt_action_param *par,
 
339
               enum ipset_adt adt, const struct ip_set_adt_opt *opt)
 
340
{
 
341
        const struct ip_set_hash *h = set->data;
 
342
        ipset_adtfn adtfn = set->variant->adt[adt];
 
343
        struct hash_net6_elem data = {
 
344
                .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
 
345
        };
 
346
 
 
347
        if (data.cidr == 0)
 
348
                return -EINVAL;
 
349
        if (adt == IPSET_TEST)
 
350
                data.cidr = HOST_MASK;
 
351
 
 
352
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
 
353
        ip6_netmask(&data.ip, data.cidr);
 
354
 
 
355
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 
356
}
 
357
 
 
358
static int
 
359
hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 
360
               enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 
361
{
 
362
        const struct ip_set_hash *h = set->data;
 
363
        ipset_adtfn adtfn = set->variant->adt[adt];
 
364
        struct hash_net6_elem data = { .cidr = HOST_MASK };
 
365
        u32 timeout = h->timeout;
 
366
        int ret;
 
367
 
 
368
        if (unlikely(!tb[IPSET_ATTR_IP] ||
 
369
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 
370
                return -IPSET_ERR_PROTOCOL;
 
371
        if (unlikely(tb[IPSET_ATTR_IP_TO]))
 
372
                return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
 
373
 
 
374
        if (tb[IPSET_ATTR_LINENO])
 
375
                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
376
 
 
377
        ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
 
378
        if (ret)
 
379
                return ret;
 
380
 
 
381
        if (tb[IPSET_ATTR_CIDR])
 
382
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
383
 
 
384
        if (!data.cidr)
 
385
                return -IPSET_ERR_INVALID_CIDR;
 
386
 
 
387
        ip6_netmask(&data.ip, data.cidr);
 
388
 
 
389
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
390
                if (!with_timeout(h->timeout))
 
391
                        return -IPSET_ERR_TIMEOUT;
 
392
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
393
        }
 
394
 
 
395
        ret = adtfn(set, &data, timeout, flags);
 
396
 
 
397
        return ip_set_eexist(ret, flags) ? 0 : ret;
 
398
}
 
399
 
 
400
/* Create hash:ip type of sets */
 
401
 
 
402
static int
 
403
hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 
404
{
 
405
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
 
406
        struct ip_set_hash *h;
 
407
        u8 hbits;
 
408
 
 
409
        if (!(set->family == AF_INET || set->family == AF_INET6))
 
410
                return -IPSET_ERR_INVALID_FAMILY;
 
411
 
 
412
        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
 
413
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
 
414
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 
415
                return -IPSET_ERR_PROTOCOL;
 
416
 
 
417
        if (tb[IPSET_ATTR_HASHSIZE]) {
 
418
                hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
 
419
                if (hashsize < IPSET_MIMINAL_HASHSIZE)
 
420
                        hashsize = IPSET_MIMINAL_HASHSIZE;
 
421
        }
 
422
 
 
423
        if (tb[IPSET_ATTR_MAXELEM])
 
424
                maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
 
425
 
 
426
        h = kzalloc(sizeof(*h)
 
427
                    + sizeof(struct ip_set_hash_nets)
 
428
                      * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
 
429
        if (!h)
 
430
                return -ENOMEM;
 
431
 
 
432
        h->maxelem = maxelem;
 
433
        get_random_bytes(&h->initval, sizeof(h->initval));
 
434
        h->timeout = IPSET_NO_TIMEOUT;
 
435
 
 
436
        hbits = htable_bits(hashsize);
 
437
        h->table = ip_set_alloc(
 
438
                        sizeof(struct htable)
 
439
                        + jhash_size(hbits) * sizeof(struct hbucket));
 
440
        if (!h->table) {
 
441
                kfree(h);
 
442
                return -ENOMEM;
 
443
        }
 
444
        h->table->htable_bits = hbits;
 
445
 
 
446
        set->data = h;
 
447
 
 
448
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
449
                h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
450
 
 
451
                set->variant = set->family == AF_INET
 
452
                        ? &hash_net4_tvariant : &hash_net6_tvariant;
 
453
 
 
454
                if (set->family == AF_INET)
 
455
                        hash_net4_gc_init(set);
 
456
                else
 
457
                        hash_net6_gc_init(set);
 
458
        } else {
 
459
                set->variant = set->family == AF_INET
 
460
                        ? &hash_net4_variant : &hash_net6_variant;
 
461
        }
 
462
 
 
463
        pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
 
464
                 set->name, jhash_size(h->table->htable_bits),
 
465
                 h->table->htable_bits, h->maxelem, set->data, h->table);
 
466
 
 
467
        return 0;
 
468
}
 
469
 
 
470
static struct ip_set_type hash_net_type __read_mostly = {
 
471
        .name           = "hash:net",
 
472
        .protocol       = IPSET_PROTOCOL,
 
473
        .features       = IPSET_TYPE_IP,
 
474
        .dimension      = IPSET_DIM_ONE,
 
475
        .family         = AF_UNSPEC,
 
476
        .revision_min   = 0,
 
477
        .revision_max   = 1,    /* Range as input support for IPv4 added */
 
478
        .create         = hash_net_create,
 
479
        .create_policy  = {
 
480
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
 
481
                [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
 
482
                [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
 
483
                [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
 
484
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 
485
        },
 
486
        .adt_policy     = {
 
487
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
 
488
                [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
 
489
                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
 
490
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 
491
        },
 
492
        .me             = THIS_MODULE,
 
493
};
 
494
 
 
495
static int __init
 
496
hash_net_init(void)
 
497
{
 
498
        return ip_set_type_register(&hash_net_type);
 
499
}
 
500
 
 
501
static void __exit
 
502
hash_net_fini(void)
 
503
{
 
504
        ip_set_type_unregister(&hash_net_type);
 
505
}
 
506
 
 
507
module_init(hash_net_init);
 
508
module_exit(hash_net_fini);