RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / net / ipv4 / netfilter / ip_set_setlist.c
blob3cfdae87f3c577831beb403920ba03decf1ce09c
1 /* Copyright (C) 2008 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.
6 */
8 /* Kernel module implementing an IP set type: the setlist type */
10 #include <linux/module.h>
11 #include <linux/ip.h>
12 #include <linux/skbuff.h>
13 #include <linux/errno.h>
15 #include <linux/netfilter_ipv4/ip_set.h>
16 #include <linux/netfilter_ipv4/ip_set_bitmaps.h>
17 #include <linux/netfilter_ipv4/ip_set_setlist.h>
20 * before ==> index, ref
21 * after ==> ref, index
24 static inline int
25 next_index_eq(const struct ip_set_setlist *map, int i, ip_set_id_t index)
27 return i < map->size && map->index[i] == index;
30 static int
31 setlist_utest(struct ip_set *set, const void *data, u_int32_t size)
33 const struct ip_set_setlist *map = set->data;
34 const struct ip_set_req_setlist *req = data;
35 ip_set_id_t index, ref = IP_SET_INVALID_ID;
36 int i, res = 0;
37 struct ip_set *s;
39 if (req->before && req->ref[0] == '\0')
40 return 0;
42 index = __ip_set_get_byname(req->name, &s);
43 if (index == IP_SET_INVALID_ID)
44 return 0;
45 if (req->ref[0] != '\0') {
46 ref = __ip_set_get_byname(req->ref, &s);
47 if (ref == IP_SET_INVALID_ID)
48 goto finish;
50 for (i = 0; i < map->size
51 && map->index[i] != IP_SET_INVALID_ID; i++) {
52 if (req->before && map->index[i] == index) {
53 res = next_index_eq(map, i + 1, ref);
54 break;
55 } else if (!req->before) {
56 if ((ref == IP_SET_INVALID_ID
57 && map->index[i] == index)
58 || (map->index[i] == ref
59 && next_index_eq(map, i + 1, index))) {
60 res = 1;
61 break;
65 if (ref != IP_SET_INVALID_ID)
66 __ip_set_put_byindex(ref);
67 finish:
68 __ip_set_put_byindex(index);
69 return res;
72 static int
73 setlist_ktest(struct ip_set *set,
74 const struct sk_buff *skb,
75 const u_int32_t *flags)
77 struct ip_set_setlist *map = set->data;
78 int i, res = 0;
80 for (i = 0; i < map->size
81 && map->index[i] != IP_SET_INVALID_ID
82 && res == 0; i++)
83 res = ip_set_testip_kernel(map->index[i], skb, flags);
84 return res;
87 static inline int
88 insert_setlist(struct ip_set_setlist *map, int i, ip_set_id_t index)
90 ip_set_id_t tmp;
91 int j;
93 DP("i: %u, last %u\n", i, map->index[map->size - 1]);
94 if (i >= map->size || map->index[map->size - 1] != IP_SET_INVALID_ID)
95 return -ERANGE;
97 for (j = i; j < map->size
98 && index != IP_SET_INVALID_ID; j++) {
99 tmp = map->index[j];
100 map->index[j] = index;
101 index = tmp;
103 return 0;
106 static int
107 setlist_uadd(struct ip_set *set, const void *data, u_int32_t size)
109 struct ip_set_setlist *map = set->data;
110 const struct ip_set_req_setlist *req = data;
111 ip_set_id_t index, ref = IP_SET_INVALID_ID;
112 int i, res = -ERANGE;
113 struct ip_set *s;
115 if (req->before && req->ref[0] == '\0')
116 return -EINVAL;
118 index = __ip_set_get_byname(req->name, &s);
119 if (index == IP_SET_INVALID_ID)
120 return -EEXIST;
121 /* "Loop detection" */
122 if (strcmp(s->type->typename, "setlist") == 0)
123 goto finish;
125 if (req->ref[0] != '\0') {
126 ref = __ip_set_get_byname(req->ref, &s);
127 if (ref == IP_SET_INVALID_ID) {
128 res = -EEXIST;
129 goto finish;
132 for (i = 0; i < map->size; i++) {
133 if (map->index[i] != ref)
134 continue;
135 if (req->before)
136 res = insert_setlist(map, i, index);
137 else
138 res = insert_setlist(map,
139 ref == IP_SET_INVALID_ID ? i : i + 1,
140 index);
141 break;
143 if (ref != IP_SET_INVALID_ID)
144 __ip_set_put_byindex(ref);
145 /* In case of success, we keep the reference to the set */
146 finish:
147 if (res != 0)
148 __ip_set_put_byindex(index);
149 return res;
152 static int
153 setlist_kadd(struct ip_set *set,
154 const struct sk_buff *skb,
155 const u_int32_t *flags)
157 struct ip_set_setlist *map = set->data;
158 int i, res = -EINVAL;
160 for (i = 0; i < map->size
161 && map->index[i] != IP_SET_INVALID_ID
162 && res != 0; i++)
163 res = ip_set_addip_kernel(map->index[i], skb, flags);
164 return res;
167 static inline int
168 unshift_setlist(struct ip_set_setlist *map, int i)
170 int j;
172 for (j = i; j < map->size - 1; j++)
173 map->index[j] = map->index[j+1];
174 map->index[map->size-1] = IP_SET_INVALID_ID;
175 return 0;
178 static int
179 setlist_udel(struct ip_set *set, const void *data, u_int32_t size)
181 struct ip_set_setlist *map = set->data;
182 const struct ip_set_req_setlist *req = data;
183 ip_set_id_t index, ref = IP_SET_INVALID_ID;
184 int i, res = -EEXIST;
185 struct ip_set *s;
187 if (req->before && req->ref[0] == '\0')
188 return -EINVAL;
190 index = __ip_set_get_byname(req->name, &s);
191 if (index == IP_SET_INVALID_ID)
192 return -EEXIST;
193 if (req->ref[0] != '\0') {
194 ref = __ip_set_get_byname(req->ref, &s);
195 if (ref == IP_SET_INVALID_ID)
196 goto finish;
198 for (i = 0; i < map->size
199 && map->index[i] != IP_SET_INVALID_ID; i++) {
200 if (req->before) {
201 if (map->index[i] == index
202 && next_index_eq(map, i + 1, ref)) {
203 res = unshift_setlist(map, i);
204 break;
206 } else if (ref == IP_SET_INVALID_ID) {
207 if (map->index[i] == index) {
208 res = unshift_setlist(map, i);
209 break;
211 } else if (map->index[i] == ref
212 && next_index_eq(map, i + 1, index)) {
213 res = unshift_setlist(map, i + 1);
214 break;
217 if (ref != IP_SET_INVALID_ID)
218 __ip_set_put_byindex(ref);
219 finish:
220 __ip_set_put_byindex(index);
221 /* In case of success, release the reference to the set */
222 if (res == 0)
223 __ip_set_put_byindex(index);
224 return res;
227 static int
228 setlist_kdel(struct ip_set *set,
229 const struct sk_buff *skb,
230 const u_int32_t *flags)
232 struct ip_set_setlist *map = set->data;
233 int i, res = -EINVAL;
235 for (i = 0; i < map->size
236 && map->index[i] != IP_SET_INVALID_ID
237 && res != 0; i++)
238 res = ip_set_delip_kernel(map->index[i], skb, flags);
239 return res;
242 static int
243 setlist_create(struct ip_set *set, const void *data, u_int32_t size)
245 struct ip_set_setlist *map;
246 const struct ip_set_req_setlist_create *req = data;
247 int i;
249 map = kmalloc(sizeof(struct ip_set_setlist) +
250 req->size * sizeof(ip_set_id_t), GFP_KERNEL);
251 if (!map)
252 return -ENOMEM;
253 map->size = req->size;
254 for (i = 0; i < map->size; i++)
255 map->index[i] = IP_SET_INVALID_ID;
257 set->data = map;
258 return 0;
261 static void
262 setlist_destroy(struct ip_set *set)
264 struct ip_set_setlist *map = set->data;
265 int i;
267 for (i = 0; i < map->size
268 && map->index[i] != IP_SET_INVALID_ID; i++)
269 __ip_set_put_byindex(map->index[i]);
271 kfree(map);
272 set->data = NULL;
275 static void
276 setlist_flush(struct ip_set *set)
278 struct ip_set_setlist *map = set->data;
279 int i;
281 for (i = 0; i < map->size
282 && map->index[i] != IP_SET_INVALID_ID; i++) {
283 __ip_set_put_byindex(map->index[i]);
284 map->index[i] = IP_SET_INVALID_ID;
288 static void
289 setlist_list_header(const struct ip_set *set, void *data)
291 const struct ip_set_setlist *map = set->data;
292 struct ip_set_req_setlist_create *header = data;
294 header->size = map->size;
297 static int
298 setlist_list_members_size(const struct ip_set *set, char dont_align)
300 const struct ip_set_setlist *map = set->data;
302 return map->size * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
305 static void
306 setlist_list_members(const struct ip_set *set, void *data, char dont_align)
308 struct ip_set_setlist *map = set->data;
309 ip_set_id_t *d;
310 int i;
312 for (i = 0; i < map->size; i++) {
313 d = data + i * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align);
314 *d = ip_set_id(map->index[i]);
318 IP_SET_TYPE(setlist, IPSET_TYPE_SETNAME | IPSET_DATA_SINGLE)
320 MODULE_LICENSE("GPL");
321 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
322 MODULE_DESCRIPTION("setlist type of IP sets");
324 REGISTER_MODULE(setlist)