~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.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:ip type */
 
9
 
 
10
#include <linux/jhash.h>
 
11
#include <linux/module.h>
 
12
#include <linux/ip.h>
 
13
#include <linux/skbuff.h>
 
14
#include <linux/errno.h>
 
15
#include <linux/random.h>
 
16
#include <net/ip.h>
 
17
#include <net/ipv6.h>
 
18
#include <net/netlink.h>
 
19
#include <net/tcp.h>
 
20
 
 
21
#include <linux/netfilter.h>
 
22
#include <linux/netfilter/ipset/pfxlen.h>
 
23
#include <linux/netfilter/ipset/ip_set.h>
 
24
#include <linux/netfilter/ipset/ip_set_timeout.h>
 
25
#include <linux/netfilter/ipset/ip_set_hash.h>
 
26
 
 
27
MODULE_LICENSE("GPL");
 
28
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 
29
MODULE_DESCRIPTION("hash:ip type of IP sets");
 
30
MODULE_ALIAS("ip_set_hash:ip");
 
31
 
 
32
/* Type specific function prefix */
 
33
#define TYPE            hash_ip
 
34
 
 
35
static bool
 
36
hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
 
37
 
 
38
#define hash_ip4_same_set       hash_ip_same_set
 
39
#define hash_ip6_same_set       hash_ip_same_set
 
40
 
 
41
/* The type variant functions: IPv4 */
 
42
 
 
43
/* Member elements without timeout */
 
44
struct hash_ip4_elem {
 
45
        __be32 ip;
 
46
};
 
47
 
 
48
/* Member elements with timeout support */
 
49
struct hash_ip4_telem {
 
50
        __be32 ip;
 
51
        unsigned long timeout;
 
52
};
 
53
 
 
54
static inline bool
 
55
hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
 
56
                    const struct hash_ip4_elem *ip2)
 
57
{
 
58
        return ip1->ip == ip2->ip;
 
59
}
 
60
 
 
61
static inline bool
 
62
hash_ip4_data_isnull(const struct hash_ip4_elem *elem)
 
63
{
 
64
        return elem->ip == 0;
 
65
}
 
66
 
 
67
static inline void
 
68
hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
 
69
{
 
70
        dst->ip = src->ip;
 
71
}
 
72
 
 
73
/* Zero valued IP addresses cannot be stored */
 
74
static inline void
 
75
hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
 
76
{
 
77
        elem->ip = 0;
 
78
}
 
79
 
 
80
static inline bool
 
81
hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
 
82
{
 
83
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
 
84
        return 0;
 
85
 
 
86
nla_put_failure:
 
87
        return 1;
 
88
}
 
89
 
 
90
static bool
 
91
hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
 
92
{
 
93
        const struct hash_ip4_telem *tdata =
 
94
                (const struct hash_ip4_telem *)data;
 
95
 
 
96
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
 
97
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
 
98
                      htonl(ip_set_timeout_get(tdata->timeout)));
 
99
 
 
100
        return 0;
 
101
 
 
102
nla_put_failure:
 
103
        return 1;
 
104
}
 
105
 
 
106
#define IP_SET_HASH_WITH_NETMASK
 
107
#define PF              4
 
108
#define HOST_MASK       32
 
109
#include <linux/netfilter/ipset/ip_set_ahash.h>
 
110
 
 
111
static int
 
112
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
 
113
              enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
 
114
{
 
115
        const struct ip_set_hash *h = set->data;
 
116
        ipset_adtfn adtfn = set->variant->adt[adt];
 
117
        __be32 ip;
 
118
 
 
119
        ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip);
 
120
        ip &= ip_set_netmask(h->netmask);
 
121
        if (ip == 0)
 
122
                return -EINVAL;
 
123
 
 
124
        return adtfn(set, &ip, h->timeout);
 
125
}
 
126
 
 
127
static int
 
128
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 
129
              enum ipset_adt adt, u32 *lineno, u32 flags)
 
130
{
 
131
        const struct ip_set_hash *h = set->data;
 
132
        ipset_adtfn adtfn = set->variant->adt[adt];
 
133
        u32 ip, ip_to, hosts, timeout = h->timeout;
 
134
        __be32 nip;
 
135
        int ret = 0;
 
136
 
 
137
        if (unlikely(!tb[IPSET_ATTR_IP] ||
 
138
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 
139
                return -IPSET_ERR_PROTOCOL;
 
140
 
 
141
        if (tb[IPSET_ATTR_LINENO])
 
142
                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
143
 
 
144
        ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
 
145
        if (ret)
 
146
                return ret;
 
147
 
 
148
        ip &= ip_set_hostmask(h->netmask);
 
149
 
 
150
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
151
                if (!with_timeout(h->timeout))
 
152
                        return -IPSET_ERR_TIMEOUT;
 
153
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
154
        }
 
155
 
 
156
        if (adt == IPSET_TEST) {
 
157
                nip = htonl(ip);
 
158
                if (nip == 0)
 
159
                        return -IPSET_ERR_HASH_ELEM;
 
160
                return adtfn(set, &nip, timeout);
 
161
        }
 
162
 
 
163
        if (tb[IPSET_ATTR_IP_TO]) {
 
164
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
 
165
                if (ret)
 
166
                        return ret;
 
167
                if (ip > ip_to)
 
168
                        swap(ip, ip_to);
 
169
        } else if (tb[IPSET_ATTR_CIDR]) {
 
170
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
171
 
 
172
                if (cidr > 32)
 
173
                        return -IPSET_ERR_INVALID_CIDR;
 
174
                ip &= ip_set_hostmask(cidr);
 
175
                ip_to = ip | ~ip_set_hostmask(cidr);
 
176
        } else
 
177
                ip_to = ip;
 
178
 
 
179
        hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
 
180
 
 
181
        for (; !before(ip_to, ip); ip += hosts) {
 
182
                nip = htonl(ip);
 
183
                if (nip == 0)
 
184
                        return -IPSET_ERR_HASH_ELEM;
 
185
                ret = adtfn(set, &nip, timeout);
 
186
 
 
187
                if (ret && !ip_set_eexist(ret, flags))
 
188
                        return ret;
 
189
                else
 
190
                        ret = 0;
 
191
        }
 
192
        return ret;
 
193
}
 
194
 
 
195
static bool
 
196
hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
 
197
{
 
198
        const struct ip_set_hash *x = a->data;
 
199
        const struct ip_set_hash *y = b->data;
 
200
 
 
201
        /* Resizing changes htable_bits, so we ignore it */
 
202
        return x->maxelem == y->maxelem &&
 
203
               x->timeout == y->timeout &&
 
204
               x->netmask == y->netmask;
 
205
}
 
206
 
 
207
/* The type variant functions: IPv6 */
 
208
 
 
209
struct hash_ip6_elem {
 
210
        union nf_inet_addr ip;
 
211
};
 
212
 
 
213
struct hash_ip6_telem {
 
214
        union nf_inet_addr ip;
 
215
        unsigned long timeout;
 
216
};
 
217
 
 
218
static inline bool
 
219
hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
 
220
                    const struct hash_ip6_elem *ip2)
 
221
{
 
222
        return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
 
223
}
 
224
 
 
225
static inline bool
 
226
hash_ip6_data_isnull(const struct hash_ip6_elem *elem)
 
227
{
 
228
        return ipv6_addr_any(&elem->ip.in6);
 
229
}
 
230
 
 
231
static inline void
 
232
hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
 
233
{
 
234
        ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
 
235
}
 
236
 
 
237
static inline void
 
238
hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
 
239
{
 
240
        ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
 
241
}
 
242
 
 
243
static inline void
 
244
ip6_netmask(union nf_inet_addr *ip, u8 prefix)
 
245
{
 
246
        ip->ip6[0] &= ip_set_netmask6(prefix)[0];
 
247
        ip->ip6[1] &= ip_set_netmask6(prefix)[1];
 
248
        ip->ip6[2] &= ip_set_netmask6(prefix)[2];
 
249
        ip->ip6[3] &= ip_set_netmask6(prefix)[3];
 
250
}
 
251
 
 
252
static bool
 
253
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
 
254
{
 
255
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
 
256
        return 0;
 
257
 
 
258
nla_put_failure:
 
259
        return 1;
 
260
}
 
261
 
 
262
static bool
 
263
hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
 
264
{
 
265
        const struct hash_ip6_telem *e =
 
266
                (const struct hash_ip6_telem *)data;
 
267
 
 
268
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
 
269
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
 
270
                      htonl(ip_set_timeout_get(e->timeout)));
 
271
        return 0;
 
272
 
 
273
nla_put_failure:
 
274
        return 1;
 
275
}
 
276
 
 
277
#undef PF
 
278
#undef HOST_MASK
 
279
 
 
280
#define PF              6
 
281
#define HOST_MASK       128
 
282
#include <linux/netfilter/ipset/ip_set_ahash.h>
 
283
 
 
284
static int
 
285
hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
 
286
              enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
 
287
{
 
288
        const struct ip_set_hash *h = set->data;
 
289
        ipset_adtfn adtfn = set->variant->adt[adt];
 
290
        union nf_inet_addr ip;
 
291
 
 
292
        ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6);
 
293
        ip6_netmask(&ip, h->netmask);
 
294
        if (ipv6_addr_any(&ip.in6))
 
295
                return -EINVAL;
 
296
 
 
297
        return adtfn(set, &ip, h->timeout);
 
298
}
 
299
 
 
300
static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
 
301
        [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
 
302
        [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 
303
        [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
 
304
};
 
305
 
 
306
static int
 
307
hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
 
308
              enum ipset_adt adt, u32 *lineno, u32 flags)
 
309
{
 
310
        const struct ip_set_hash *h = set->data;
 
311
        ipset_adtfn adtfn = set->variant->adt[adt];
 
312
        union nf_inet_addr ip;
 
313
        u32 timeout = h->timeout;
 
314
        int ret;
 
315
 
 
316
        if (unlikely(!tb[IPSET_ATTR_IP] ||
 
317
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
 
318
                     tb[IPSET_ATTR_IP_TO] ||
 
319
                     tb[IPSET_ATTR_CIDR]))
 
320
                return -IPSET_ERR_PROTOCOL;
 
321
 
 
322
        if (tb[IPSET_ATTR_LINENO])
 
323
                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
324
 
 
325
        ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &ip);
 
326
        if (ret)
 
327
                return ret;
 
328
 
 
329
        ip6_netmask(&ip, h->netmask);
 
330
        if (ipv6_addr_any(&ip.in6))
 
331
                return -IPSET_ERR_HASH_ELEM;
 
332
 
 
333
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
334
                if (!with_timeout(h->timeout))
 
335
                        return -IPSET_ERR_TIMEOUT;
 
336
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
337
        }
 
338
 
 
339
        ret = adtfn(set, &ip, timeout);
 
340
 
 
341
        return ip_set_eexist(ret, flags) ? 0 : ret;
 
342
}
 
343
 
 
344
/* Create hash:ip type of sets */
 
345
 
 
346
static int
 
347
hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 
348
{
 
349
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
 
350
        u8 netmask, hbits;
 
351
        struct ip_set_hash *h;
 
352
 
 
353
        if (!(set->family == AF_INET || set->family == AF_INET6))
 
354
                return -IPSET_ERR_INVALID_FAMILY;
 
355
        netmask = set->family == AF_INET ? 32 : 128;
 
356
        pr_debug("Create set %s with family %s\n",
 
357
                 set->name, set->family == AF_INET ? "inet" : "inet6");
 
358
 
 
359
        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
 
360
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
 
361
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 
362
                return -IPSET_ERR_PROTOCOL;
 
363
 
 
364
        if (tb[IPSET_ATTR_HASHSIZE]) {
 
365
                hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
 
366
                if (hashsize < IPSET_MIMINAL_HASHSIZE)
 
367
                        hashsize = IPSET_MIMINAL_HASHSIZE;
 
368
        }
 
369
 
 
370
        if (tb[IPSET_ATTR_MAXELEM])
 
371
                maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
 
372
 
 
373
        if (tb[IPSET_ATTR_NETMASK]) {
 
374
                netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
 
375
 
 
376
                if ((set->family == AF_INET && netmask > 32) ||
 
377
                    (set->family == AF_INET6 && netmask > 128) ||
 
378
                    netmask == 0)
 
379
                        return -IPSET_ERR_INVALID_NETMASK;
 
380
        }
 
381
 
 
382
        h = kzalloc(sizeof(*h), GFP_KERNEL);
 
383
        if (!h)
 
384
                return -ENOMEM;
 
385
 
 
386
        h->maxelem = maxelem;
 
387
        h->netmask = netmask;
 
388
        get_random_bytes(&h->initval, sizeof(h->initval));
 
389
        h->timeout = IPSET_NO_TIMEOUT;
 
390
 
 
391
        hbits = htable_bits(hashsize);
 
392
        h->table = ip_set_alloc(
 
393
                        sizeof(struct htable)
 
394
                        + jhash_size(hbits) * sizeof(struct hbucket));
 
395
        if (!h->table) {
 
396
                kfree(h);
 
397
                return -ENOMEM;
 
398
        }
 
399
        h->table->htable_bits = hbits;
 
400
 
 
401
        set->data = h;
 
402
 
 
403
        if (tb[IPSET_ATTR_TIMEOUT]) {
 
404
                h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
405
 
 
406
                set->variant = set->family == AF_INET
 
407
                        ? &hash_ip4_tvariant : &hash_ip6_tvariant;
 
408
 
 
409
                if (set->family == AF_INET)
 
410
                        hash_ip4_gc_init(set);
 
411
                else
 
412
                        hash_ip6_gc_init(set);
 
413
        } else {
 
414
                set->variant = set->family == AF_INET
 
415
                        ? &hash_ip4_variant : &hash_ip6_variant;
 
416
        }
 
417
 
 
418
        pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
 
419
                 set->name, jhash_size(h->table->htable_bits),
 
420
                 h->table->htable_bits, h->maxelem, set->data, h->table);
 
421
 
 
422
        return 0;
 
423
}
 
424
 
 
425
static struct ip_set_type hash_ip_type __read_mostly = {
 
426
        .name           = "hash:ip",
 
427
        .protocol       = IPSET_PROTOCOL,
 
428
        .features       = IPSET_TYPE_IP,
 
429
        .dimension      = IPSET_DIM_ONE,
 
430
        .family         = AF_UNSPEC,
 
431
        .revision       = 0,
 
432
        .create         = hash_ip_create,
 
433
        .create_policy  = {
 
434
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
 
435
                [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
 
436
                [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
 
437
                [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
 
438
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 
439
                [IPSET_ATTR_NETMASK]    = { .type = NLA_U8  },
 
440
        },
 
441
        .adt_policy     = {
 
442
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
 
443
                [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
 
444
                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
 
445
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 
446
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
 
447
        },
 
448
        .me             = THIS_MODULE,
 
449
};
 
450
 
 
451
static int __init
 
452
hash_ip_init(void)
 
453
{
 
454
        return ip_set_type_register(&hash_ip_type);
 
455
}
 
456
 
 
457
static void __exit
 
458
hash_ip_fini(void)
 
459
{
 
460
        ip_set_type_unregister(&hash_ip_type);
 
461
}
 
462
 
 
463
module_init(hash_ip_init);
 
464
module_exit(hash_ip_fini);