This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / net / ipv4 / netfilter / ip_set_ipmap.c
1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
2  *                         Patrick Schaaf <bof@bof.de>
3  * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.  
8  */
9
10 /* Kernel module implementing an IP set type: the single bitmap type */
11
12 #include <linux/module.h>
13 #include <linux/ip.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
22 #include <linux/netfilter_ipv4/ip_set_ipmap.h>
23
24 static inline ip_set_ip_t
25 ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip)
26 {
27         return (ip - map->first_ip)/map->hosts;
28 }
29
30 static inline int
31 __testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
32 {
33         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
34         
35         if (ip < map->first_ip || ip > map->last_ip)
36                 return -ERANGE;
37
38         *hash_ip = ip & map->netmask;
39         DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u",
40            set->name, HIPQUAD(ip), HIPQUAD(*hash_ip));
41         return !!test_bit(ip_to_id(map, *hash_ip), map->members);
42 }
43
44 static int
45 testip(struct ip_set *set, const void *data, size_t size,
46        ip_set_ip_t *hash_ip)
47 {
48         struct ip_set_req_ipmap *req = 
49             (struct ip_set_req_ipmap *) data;
50
51         if (size != sizeof(struct ip_set_req_ipmap)) {
52                 ip_set_printk("data length wrong (want %zu, have %zu)",
53                               sizeof(struct ip_set_req_ipmap),
54                               size);
55                 return -EINVAL;
56         }
57         return __testip(set, req->ip, hash_ip);
58 }
59
60 static int
61 testip_kernel(struct ip_set *set, 
62               const struct sk_buff *skb,
63               ip_set_ip_t *hash_ip,
64               const u_int32_t *flags,
65               unsigned char index)
66 {
67         int res;
68         
69         DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u",
70            flags[index] & IPSET_SRC ? "SRC" : "DST",
71            NIPQUAD(skb->nh.iph->saddr),
72            NIPQUAD(skb->nh.iph->daddr));
73
74         res =  __testip(set,
75                         ntohl(flags[index] & IPSET_SRC 
76                                 ? skb->nh.iph->saddr 
77                                 : skb->nh.iph->daddr),
78                         hash_ip);
79         return (res < 0 ? 0 : res);
80 }
81
82 static inline int
83 __addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
84 {
85         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
86
87         if (ip < map->first_ip || ip > map->last_ip)
88                 return -ERANGE;
89
90         *hash_ip = ip & map->netmask;
91         DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
92         if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members))
93                 return -EEXIST;
94
95         return 0;
96 }
97
98 static int
99 addip(struct ip_set *set, const void *data, size_t size,
100       ip_set_ip_t *hash_ip)
101 {
102         struct ip_set_req_ipmap *req = 
103             (struct ip_set_req_ipmap *) data;
104
105         if (size != sizeof(struct ip_set_req_ipmap)) {
106                 ip_set_printk("data length wrong (want %zu, have %zu)",
107                               sizeof(struct ip_set_req_ipmap),
108                               size);
109                 return -EINVAL;
110         }
111         DP("%u.%u.%u.%u", HIPQUAD(req->ip));
112         return __addip(set, req->ip, hash_ip);
113 }
114
115 static int
116 addip_kernel(struct ip_set *set, 
117              const struct sk_buff *skb,
118              ip_set_ip_t *hash_ip,
119              const u_int32_t *flags,
120              unsigned char index)
121 {
122         return __addip(set,
123                        ntohl(flags[index] & IPSET_SRC 
124                                 ? skb->nh.iph->saddr 
125                                 : skb->nh.iph->daddr),
126                        hash_ip);
127 }
128
129 static inline int 
130 __delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip)
131 {
132         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
133
134         if (ip < map->first_ip || ip > map->last_ip)
135                 return -ERANGE;
136
137         *hash_ip = ip & map->netmask;
138         DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip));
139         if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members))
140                 return -EEXIST;
141         
142         return 0;
143 }
144
145 static int
146 delip(struct ip_set *set, const void *data, size_t size,
147       ip_set_ip_t *hash_ip)
148 {
149         struct ip_set_req_ipmap *req =
150             (struct ip_set_req_ipmap *) data;
151
152         if (size != sizeof(struct ip_set_req_ipmap)) {
153                 ip_set_printk("data length wrong (want %zu, have %zu)",
154                               sizeof(struct ip_set_req_ipmap),
155                               size);
156                 return -EINVAL;
157         }
158         return __delip(set, req->ip, hash_ip);
159 }
160
161 static int
162 delip_kernel(struct ip_set *set,
163              const struct sk_buff *skb,
164              ip_set_ip_t *hash_ip,
165              const u_int32_t *flags,
166              unsigned char index)
167 {
168         return __delip(set,
169                        ntohl(flags[index] & IPSET_SRC 
170                                 ? skb->nh.iph->saddr 
171                                 : skb->nh.iph->daddr),
172                        hash_ip);
173 }
174
175 static int create(struct ip_set *set, const void *data, size_t size)
176 {
177         int newbytes;
178         struct ip_set_req_ipmap_create *req =
179             (struct ip_set_req_ipmap_create *) data;
180         struct ip_set_ipmap *map;
181
182         if (size != sizeof(struct ip_set_req_ipmap_create)) {
183                 ip_set_printk("data length wrong (want %zu, have %zu)",
184                               sizeof(struct ip_set_req_ipmap_create),
185                               size);
186                 return -EINVAL;
187         }
188
189         DP("from %u.%u.%u.%u to %u.%u.%u.%u",
190            HIPQUAD(req->from), HIPQUAD(req->to));
191
192         if (req->from > req->to) {
193                 DP("bad ip range");
194                 return -ENOEXEC;
195         }
196
197         map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL);
198         if (!map) {
199                 DP("out of memory for %d bytes",
200                    sizeof(struct ip_set_ipmap));
201                 return -ENOMEM;
202         }
203         map->first_ip = req->from;
204         map->last_ip = req->to;
205         map->netmask = req->netmask;
206
207         if (req->netmask == 0xFFFFFFFF) {
208                 map->hosts = 1;
209                 map->sizeid = map->last_ip - map->first_ip + 1;
210         } else {
211                 unsigned int mask_bits, netmask_bits;
212                 ip_set_ip_t mask;
213                 
214                 map->first_ip &= map->netmask;  /* Should we better bark? */
215                 
216                 mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits);
217                 netmask_bits = mask_to_bits(map->netmask);
218                 
219                 if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF))
220                     || netmask_bits <= mask_bits)
221                         return -ENOEXEC;
222
223                 DP("mask_bits %u, netmask_bits %u",
224                    mask_bits, netmask_bits);
225                 map->hosts = 2 << (32 - netmask_bits - 1);
226                 map->sizeid = 2 << (netmask_bits - mask_bits - 1);
227         }
228         if (map->sizeid > MAX_RANGE + 1) {
229                 ip_set_printk("range too big (max %d addresses)",
230                                MAX_RANGE+1);
231                 kfree(map);
232                 return -ENOEXEC;
233         }
234         DP("hosts %u, sizeid %u", map->hosts, map->sizeid);
235         newbytes = bitmap_bytes(0, map->sizeid - 1);
236         map->members = kmalloc(newbytes, GFP_KERNEL);
237         if (!map->members) {
238                 DP("out of memory for %d bytes", newbytes);
239                 kfree(map);
240                 return -ENOMEM;
241         }
242         memset(map->members, 0, newbytes);
243         
244         set->data = map;
245         return 0;
246 }
247
248 static void destroy(struct ip_set *set)
249 {
250         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
251         
252         kfree(map->members);
253         kfree(map);
254         
255         set->data = NULL;
256 }
257
258 static void flush(struct ip_set *set)
259 {
260         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
261         memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1));
262 }
263
264 static void list_header(const struct ip_set *set, void *data)
265 {
266         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
267         struct ip_set_req_ipmap_create *header =
268             (struct ip_set_req_ipmap_create *) data;
269
270         header->from = map->first_ip;
271         header->to = map->last_ip;
272         header->netmask = map->netmask;
273 }
274
275 static int list_members_size(const struct ip_set *set)
276 {
277         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
278
279         return bitmap_bytes(0, map->sizeid - 1);
280 }
281
282 static void list_members(const struct ip_set *set, void *data)
283 {
284         struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data;
285         int bytes = bitmap_bytes(0, map->sizeid - 1);
286
287         memcpy(data, map->members, bytes);
288 }
289
290 static struct ip_set_type ip_set_ipmap = {
291         .typename               = SETTYPE_NAME,
292         .features               = IPSET_TYPE_IP | IPSET_DATA_SINGLE,
293         .protocol_version       = IP_SET_PROTOCOL_VERSION,
294         .create                 = &create,
295         .destroy                = &destroy,
296         .flush                  = &flush,
297         .reqsize                = sizeof(struct ip_set_req_ipmap),
298         .addip                  = &addip,
299         .addip_kernel           = &addip_kernel,
300         .delip                  = &delip,
301         .delip_kernel           = &delip_kernel,
302         .testip                 = &testip,
303         .testip_kernel          = &testip_kernel,
304         .header_size            = sizeof(struct ip_set_req_ipmap_create),
305         .list_header            = &list_header,
306         .list_members_size      = &list_members_size,
307         .list_members           = &list_members,
308         .me                     = THIS_MODULE,
309 };
310
311 MODULE_LICENSE("GPL");
312 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
313 MODULE_DESCRIPTION("ipmap type of IP sets");
314
315 static int __init init(void)
316 {
317         return ip_set_register_set_type(&ip_set_ipmap);
318 }
319
320 static void __exit fini(void)
321 {
322         /* FIXME: possible race with ip_set_create() */
323         ip_set_unregister_set_type(&ip_set_ipmap);
324 }
325
326 module_init(init);
327 module_exit(fini);