blob: e82157285d34a85006b9833af78293a84f92b391 [file] [log] [blame]
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +02001/* Copyright (C) 2008-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +01002 *
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 list:set type */
9
10#include <linux/module.h>
11#include <linux/ip.h>
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020012#include <linux/rculist.h>
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010013#include <linux/skbuff.h>
14#include <linux/errno.h>
15
16#include <linux/netfilter/ipset/ip_set.h>
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010017#include <linux/netfilter/ipset/ip_set_list.h>
18
Jozsef Kadlecsik35b8dcf2013-04-30 23:02:43 +020019#define IPSET_TYPE_REV_MIN 0
Oliver Smith81b10bb2013-09-22 20:56:33 +020020/* 1 Counters support added */
Anton Danilovcbee93d2014-08-28 10:11:30 +040021/* 2 Comments support added */
22#define IPSET_TYPE_REV_MAX 3 /* skbinfo support added */
Jozsef Kadlecsik10111a62012-09-21 21:59:32 +020023
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010024MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
Jozsef Kadlecsik35b8dcf2013-04-30 23:02:43 +020026IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010027MODULE_ALIAS("ip_set_list:set");
28
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020029/* Member elements */
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010030struct set_elem {
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020031 struct rcu_head rcu;
32 struct list_head list;
Jozsef Kadlecsik45040972016-02-24 20:32:21 +010033 struct ip_set *set; /* Sigh, in order to cleanup reference */
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010034 ip_set_id_t id;
Jozsef Kadlecsik95ad1f42015-11-07 11:21:47 +010035} __aligned(__alignof__(u64));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010036
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020037struct set_adt_elem {
38 ip_set_id_t id;
39 ip_set_id_t refid;
40 int before;
41};
42
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010043/* Type structure */
44struct list_set {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010045 u32 size; /* size of set list array */
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010046 struct timer_list gc; /* garbage collection */
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +020047 struct net *net; /* namespace */
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020048 struct list_head members; /* the set members */
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010049};
50
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010051static int
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020052list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
53 const struct xt_action_param *par,
54 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010055{
56 struct list_set *map = set->data;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020057 struct set_elem *e;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020058 u32 cmdflags = opt->cmdflags;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010059 int ret;
60
Jozsef Kadlecsik6e017812013-04-27 14:40:50 +020061 /* Don't lookup sub-counters at all */
62 opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
63 if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
64 opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020065 list_for_each_entry_rcu(e, &map->members, list) {
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020066 if (SET_WITH_TIMEOUT(set) &&
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +020067 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010068 continue;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020069 ret = ip_set_test(e->id, skb, par, opt);
Jozsef Kadlecsikde763032013-04-08 23:11:32 +020070 if (ret > 0) {
71 if (SET_WITH_COUNTER(set))
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +020072 ip_set_update_counter(ext_counter(e, set),
Jozsef Kadlecsikde763032013-04-08 23:11:32 +020073 ext, &opt->ext,
Jozsef Kadlecsik6e017812013-04-27 14:40:50 +020074 cmdflags);
Anton Danilovcbee93d2014-08-28 10:11:30 +040075 if (SET_WITH_SKBINFO(set))
76 ip_set_get_skbinfo(ext_skbinfo(e, set),
77 ext, &opt->ext,
78 cmdflags);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020079 return ret;
Jozsef Kadlecsikde763032013-04-08 23:11:32 +020080 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020081 }
82 return 0;
83}
84
85static int
86list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
87 const struct xt_action_param *par,
88 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
89{
90 struct list_set *map = set->data;
91 struct set_elem *e;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020092 int ret;
93
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +020094 list_for_each_entry(e, &map->members, list) {
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020095 if (SET_WITH_TIMEOUT(set) &&
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +020096 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +020097 continue;
98 ret = ip_set_add(e->id, skb, par, opt);
99 if (ret == 0)
100 return ret;
101 }
102 return 0;
103}
104
105static int
106list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
107 const struct xt_action_param *par,
108 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
109{
110 struct list_set *map = set->data;
111 struct set_elem *e;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200112 int ret;
113
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200114 list_for_each_entry(e, &map->members, list) {
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200115 if (SET_WITH_TIMEOUT(set) &&
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200116 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200117 continue;
118 ret = ip_set_del(e->id, skb, par, opt);
119 if (ret == 0)
120 return ret;
121 }
122 return 0;
123}
124
125static int
126list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
127 const struct xt_action_param *par,
128 enum ipset_adt adt, struct ip_set_adt_opt *opt)
129{
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200130 struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200131 int ret = -EINVAL;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200132
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200133 rcu_read_lock();
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200134 switch (adt) {
135 case IPSET_TEST:
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200136 ret = list_set_ktest(set, skb, par, opt, &ext);
137 break;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200138 case IPSET_ADD:
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200139 ret = list_set_kadd(set, skb, par, opt, &ext);
140 break;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200141 case IPSET_DEL:
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200142 ret = list_set_kdel(set, skb, par, opt, &ext);
143 break;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200144 default:
145 break;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100146 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200147 rcu_read_unlock();
148
149 return ret;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100150}
151
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200152/* Userspace interfaces: we are protected by the nfnl mutex */
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100153
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200154static void
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100155__list_set_del_rcu(struct rcu_head * rcu)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100156{
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100157 struct set_elem *e = container_of(rcu, struct set_elem, rcu);
158 struct ip_set *set = e->set;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200159 struct list_set *map = set->data;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100160
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200161 ip_set_put_byindex(map->net, e->id);
Jozsef Kadlecsik40cd63b2013-09-09 14:44:29 +0200162 ip_set_ext_destroy(set, e);
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100163 kfree(e);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200164}
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100165
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200166static inline void
167list_set_del(struct ip_set *set, struct set_elem *e)
168{
169 list_del_rcu(&e->list);
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100170 call_rcu(&e->rcu, __list_set_del_rcu);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200171}
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200172
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200173static inline void
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100174list_set_replace(struct set_elem *e, struct set_elem *old)
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200175{
176 list_replace_rcu(&old->list, &e->list);
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100177 call_rcu(&old->rcu, __list_set_del_rcu);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100178}
179
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200180static void
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200181set_cleanup_entries(struct ip_set *set)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200182{
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200183 struct list_set *map = set->data;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200184 struct set_elem *e, *n;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200185
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200186 list_for_each_entry_safe(e, n, &map->members, list)
187 if (ip_set_timeout_expired(ext_timeout(e, set)))
188 list_set_del(set, e);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200189}
190
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100191static int
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200192list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
193 struct ip_set_ext *mext, u32 flags)
194{
195 struct list_set *map = set->data;
196 struct set_adt_elem *d = value;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200197 struct set_elem *e, *next, *prev = NULL;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200198 int ret;
199
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200200 list_for_each_entry(e, &map->members, list) {
201 if (SET_WITH_TIMEOUT(set) &&
202 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200203 continue;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200204 else if (e->id != d->id) {
205 prev = e;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200206 continue;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200207 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200208
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200209 if (d->before == 0) {
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200210 ret = 1;
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200211 } else if (d->before > 0) {
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200212 next = list_next_entry(e, list);
213 ret = !list_is_last(&e->list, &map->members) &&
214 next->id == d->refid;
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200215 } else {
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200216 ret = prev && prev->id == d->refid;
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200217 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200218 return ret;
219 }
220 return 0;
221}
222
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200223static void
224list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext,
225 struct set_elem *e)
226{
227 if (SET_WITH_COUNTER(set))
228 ip_set_init_counter(ext_counter(e, set), ext);
229 if (SET_WITH_COMMENT(set))
230 ip_set_init_comment(ext_comment(e, set), ext);
231 if (SET_WITH_SKBINFO(set))
232 ip_set_init_skbinfo(ext_skbinfo(e, set), ext);
233 /* Update timeout last */
234 if (SET_WITH_TIMEOUT(set))
235 ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
236}
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200237
238static int
239list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
240 struct ip_set_ext *mext, u32 flags)
241{
242 struct list_set *map = set->data;
243 struct set_adt_elem *d = value;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200244 struct set_elem *e, *n, *prev, *next;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200245 bool flag_exist = flags & IPSET_FLAG_EXIST;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200246
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200247 /* Find where to add the new entry */
248 n = prev = next = NULL;
249 list_for_each_entry(e, &map->members, list) {
250 if (SET_WITH_TIMEOUT(set) &&
251 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200252 continue;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200253 else if (d->id == e->id)
254 n = e;
255 else if (d->before == 0 || e->id != d->refid)
256 continue;
257 else if (d->before > 0)
258 next = e;
259 else
260 prev = e;
261 }
262 /* Re-add already existing element */
263 if (n) {
264 if ((d->before > 0 && !next) ||
265 (d->before < 0 && !prev))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200266 return -IPSET_ERR_REF_EXIST;
267 if (!flag_exist)
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200268 return -IPSET_ERR_EXIST;
269 /* Update extensions */
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200270 ip_set_ext_destroy(set, n);
271 list_set_init_extensions(set, ext, n);
Jozsef Kadlecsik40cd63b2013-09-09 14:44:29 +0200272
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200273 /* Set is already added to the list */
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200274 ip_set_put_byindex(map->net, d->id);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200275 return 0;
276 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200277 /* Add new entry */
278 if (d->before == 0) {
279 /* Append */
280 n = list_empty(&map->members) ? NULL :
281 list_last_entry(&map->members, struct set_elem, list);
282 } else if (d->before > 0) {
283 /* Insert after next element */
284 if (!list_is_last(&next->list, &map->members))
285 n = list_next_entry(next, list);
286 } else {
287 /* Insert before prev element */
288 if (prev->list.prev != &map->members)
289 n = list_prev_entry(prev, list);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200290 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200291 /* Can we replace a timed out entry? */
292 if (n &&
293 !(SET_WITH_TIMEOUT(set) &&
294 ip_set_timeout_expired(ext_timeout(n, set))))
295 n = NULL;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200296
Nikolay Borisov00db6742015-10-16 09:40:28 +0300297 e = kzalloc(set->dsize, GFP_ATOMIC);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200298 if (!e)
299 return -ENOMEM;
300 e->id = d->id;
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100301 e->set = set;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200302 INIT_LIST_HEAD(&e->list);
303 list_set_init_extensions(set, ext, e);
304 if (n)
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100305 list_set_replace(e, n);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200306 else if (next)
307 list_add_tail_rcu(&e->list, &next->list);
308 else if (prev)
309 list_add_rcu(&e->list, &prev->list);
310 else
311 list_add_tail_rcu(&e->list, &map->members);
312
313 return 0;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200314}
315
316static int
317list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
318 struct ip_set_ext *mext, u32 flags)
319{
320 struct list_set *map = set->data;
321 struct set_adt_elem *d = value;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200322 struct set_elem *e, *next, *prev = NULL;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200323
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200324 list_for_each_entry(e, &map->members, list) {
325 if (SET_WITH_TIMEOUT(set) &&
326 ip_set_timeout_expired(ext_timeout(e, set)))
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200327 continue;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200328 else if (e->id != d->id) {
329 prev = e;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200330 continue;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200331 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200332
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200333 if (d->before > 0) {
334 next = list_next_entry(e, list);
335 if (list_is_last(&e->list, &map->members) ||
336 next->id != d->refid)
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200337 return -IPSET_ERR_REF_EXIST;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200338 } else if (d->before < 0) {
339 if (!prev || prev->id != d->refid)
340 return -IPSET_ERR_REF_EXIST;
341 }
342 list_set_del(set, e);
343 return 0;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200344 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200345 return d->before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200346}
347
348static int
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100349list_set_uadt(struct ip_set *set, struct nlattr *tb[],
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200350 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100351{
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200352 struct list_set *map = set->data;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200353 ipset_adtfn adtfn = set->variant->adt[adt];
354 struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200355 struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100356 struct ip_set *s;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100357 int ret = 0;
358
Sergey Popovicha212e082015-06-12 21:26:43 +0200359 if (tb[IPSET_ATTR_LINENO])
360 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
361
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100362 if (unlikely(!tb[IPSET_ATTR_NAME] ||
Sergey Popovich7dd37bc2015-06-12 21:14:09 +0200363 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100364 return -IPSET_ERR_PROTOCOL;
365
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200366 ret = ip_set_get_extensions(set, tb, &ext);
367 if (ret)
368 return ret;
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200369 e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200370 if (e.id == IPSET_INVALID_ID)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100371 return -IPSET_ERR_NAME;
372 /* "Loop detection" */
373 if (s->type->features & IPSET_TYPE_NAME) {
374 ret = -IPSET_ERR_LOOP;
375 goto finish;
376 }
377
378 if (tb[IPSET_ATTR_CADT_FLAGS]) {
379 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200380
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200381 e.before = f & IPSET_FLAG_BEFORE;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100382 }
383
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200384 if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100385 ret = -IPSET_ERR_BEFORE;
386 goto finish;
387 }
388
389 if (tb[IPSET_ATTR_NAMEREF]) {
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200390 e.refid = ip_set_get_byname(map->net,
391 nla_data(tb[IPSET_ATTR_NAMEREF]),
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200392 &s);
393 if (e.refid == IPSET_INVALID_ID) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100394 ret = -IPSET_ERR_NAMEREF;
395 goto finish;
396 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200397 if (!e.before)
398 e.before = -1;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100399 }
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200400 if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
401 set_cleanup_entries(set);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100402
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200403 ret = adtfn(set, &e, &ext, &ext, flags);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100404
405finish:
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200406 if (e.refid != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200407 ip_set_put_byindex(map->net, e.refid);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100408 if (adt != IPSET_ADD || ret)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200409 ip_set_put_byindex(map->net, e.id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100410
411 return ip_set_eexist(ret, flags) ? 0 : ret;
412}
413
414static void
415list_set_flush(struct ip_set *set)
416{
417 struct list_set *map = set->data;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200418 struct set_elem *e, *n;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100419
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200420 list_for_each_entry_safe(e, n, &map->members, list)
421 list_set_del(set, e);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100422}
423
424static void
425list_set_destroy(struct ip_set *set)
426{
427 struct list_set *map = set->data;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200428 struct set_elem *e, *n;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100429
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200430 if (SET_WITH_TIMEOUT(set))
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100431 del_timer_sync(&map->gc);
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100432
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200433 list_for_each_entry_safe(e, n, &map->members, list) {
434 list_del(&e->list);
435 ip_set_put_byindex(map->net, e->id);
436 ip_set_ext_destroy(set, e);
437 kfree(e);
438 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100439 kfree(map);
440
441 set->data = NULL;
442}
443
444static int
445list_set_head(struct ip_set *set, struct sk_buff *skb)
446{
447 const struct list_set *map = set->data;
448 struct nlattr *nested;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200449 struct set_elem *e;
450 u32 n = 0;
451
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100452 rcu_read_lock();
453 list_for_each_entry_rcu(e, &map->members, list)
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200454 n++;
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100455 rcu_read_unlock();
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100456
457 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
458 if (!nested)
459 goto nla_put_failure;
David S. Miller7cf78992012-04-01 19:54:46 -0400460 if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
Vishwanath Pai596cf3f2016-03-16 21:49:00 +0100461 nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) ||
David S. Miller7cf78992012-04-01 19:54:46 -0400462 nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200463 htonl(sizeof(*map) + n * set->dsize)))
David S. Miller7cf78992012-04-01 19:54:46 -0400464 goto nla_put_failure;
Oliver Smith81b10bb2013-09-22 20:56:33 +0200465 if (unlikely(ip_set_put_flags(skb, set)))
466 goto nla_put_failure;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100467 ipset_nest_end(skb, nested);
468
469 return 0;
470nla_put_failure:
471 return -EMSGSIZE;
472}
473
474static int
475list_set_list(const struct ip_set *set,
476 struct sk_buff *skb, struct netlink_callback *cb)
477{
478 const struct list_set *map = set->data;
479 struct nlattr *atd, *nested;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200480 u32 i = 0, first = cb->args[IPSET_CB_ARG0];
481 struct set_elem *e;
482 int ret = 0;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100483
484 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
485 if (!atd)
486 return -EMSGSIZE;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200487
488 rcu_read_lock();
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100489 list_for_each_entry_rcu(e, &map->members, list) {
490 if (i < first ||
491 (SET_WITH_TIMEOUT(set) &&
492 ip_set_timeout_expired(ext_timeout(e, set)))) {
493 i++;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100494 continue;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100495 }
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100496 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
497 if (!nested)
498 goto nla_put_failure;
David S. Miller7cf78992012-04-01 19:54:46 -0400499 if (nla_put_string(skb, IPSET_ATTR_NAME,
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200500 ip_set_name_byindex(map->net, e->id)))
David S. Miller7cf78992012-04-01 19:54:46 -0400501 goto nla_put_failure;
Jozsef Kadlecsik3fd986b2013-09-25 17:44:35 +0200502 if (ip_set_put_extensions(skb, set, e, true))
Oliver Smith81b10bb2013-09-22 20:56:33 +0200503 goto nla_put_failure;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100504 ipset_nest_end(skb, nested);
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100505 i++;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100506 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200507
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100508 ipset_nest_end(skb, atd);
509 /* Set listing finished */
Jozsef Kadlecsik93302882013-10-18 11:41:55 +0200510 cb->args[IPSET_CB_ARG0] = 0;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200511 goto out;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100512
513nla_put_failure:
514 nla_nest_cancel(skb, nested);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100515 if (unlikely(i == first)) {
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100516 nla_nest_cancel(skb, atd);
Jozsef Kadlecsik93302882013-10-18 11:41:55 +0200517 cb->args[IPSET_CB_ARG0] = 0;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200518 ret = -EMSGSIZE;
Jozsef Kadlecsik45040972016-02-24 20:32:21 +0100519 } else {
520 cb->args[IPSET_CB_ARG0] = i;
Pan Bian1231e252018-12-10 14:39:37 +0100521 ipset_nest_end(skb, atd);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100522 }
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200523out:
524 rcu_read_unlock();
525 return ret;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100526}
527
528static bool
529list_set_same_set(const struct ip_set *a, const struct ip_set *b)
530{
531 const struct list_set *x = a->data;
532 const struct list_set *y = b->data;
533
534 return x->size == y->size &&
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200535 a->timeout == b->timeout &&
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200536 a->extensions == b->extensions;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100537}
538
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200539static const struct ip_set_type_variant set_variant = {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100540 .kadt = list_set_kadt,
541 .uadt = list_set_uadt,
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200542 .adt = {
543 [IPSET_ADD] = list_set_uadd,
544 [IPSET_DEL] = list_set_udel,
545 [IPSET_TEST] = list_set_utest,
546 },
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100547 .destroy = list_set_destroy,
548 .flush = list_set_flush,
549 .head = list_set_head,
550 .list = list_set_list,
551 .same_set = list_set_same_set,
552};
553
554static void
555list_set_gc(unsigned long ul_set)
556{
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200557 struct ip_set *set = (struct ip_set *)ul_set;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100558 struct list_set *map = set->data;
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200559
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200560 spin_lock_bh(&set->lock);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200561 set_cleanup_entries(set);
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200562 spin_unlock_bh(&set->lock);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100563
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200564 map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100565 add_timer(&map->gc);
566}
567
568static void
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200569list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100570{
571 struct list_set *map = set->data;
572
573 init_timer(&map->gc);
Jozsef Kadlecsikca0f6a52015-06-13 19:45:33 +0200574 map->gc.data = (unsigned long)set;
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200575 map->gc.function = gc;
Jozsef Kadlecsikca134ce2013-09-07 00:10:07 +0200576 map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100577 add_timer(&map->gc);
578}
579
580/* Create list:set type of sets */
581
Jozsef Kadlecsik03c8b232013-09-07 00:43:52 +0200582static bool
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200583init_list_set(struct net *net, struct ip_set *set, u32 size)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100584{
585 struct list_set *map;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100586
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200587 map = kzalloc(sizeof(*map), GFP_KERNEL);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100588 if (!map)
Jozsef Kadlecsik03c8b232013-09-07 00:43:52 +0200589 return false;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100590
591 map->size = size;
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200592 map->net = net;
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200593 INIT_LIST_HEAD(&map->members);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100594 set->data = map;
595
Jozsef Kadlecsik03c8b232013-09-07 00:43:52 +0200596 return true;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100597}
598
599static int
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200600list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
601 u32 flags)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100602{
Jozsef Kadlecsik03c8b232013-09-07 00:43:52 +0200603 u32 size = IP_SET_LIST_DEFAULT_SIZE;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100604
605 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
Jozsef Kadlecsikde763032013-04-08 23:11:32 +0200606 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
607 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100608 return -IPSET_ERR_PROTOCOL;
609
610 if (tb[IPSET_ATTR_SIZE])
611 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
612 if (size < IP_SET_LIST_MIN_SIZE)
613 size = IP_SET_LIST_MIN_SIZE;
614
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200615 set->variant = &set_variant;
Jozsef Kadlecsik95ad1f42015-11-07 11:21:47 +0100616 set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem),
617 __alignof__(struct set_elem));
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200618 if (!init_list_set(net, set, size))
Jozsef Kadlecsik03c8b232013-09-07 00:43:52 +0200619 return -ENOMEM;
620 if (tb[IPSET_ATTR_TIMEOUT]) {
621 set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
Jozsef Kadlecsik7d47d972013-04-04 12:21:02 +0200622 list_set_gc_init(set, list_set_gc);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100623 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100624 return 0;
625}
626
627static struct ip_set_type list_set_type __read_mostly = {
628 .name = "list:set",
629 .protocol = IPSET_PROTOCOL,
630 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
631 .dimension = IPSET_DIM_ONE,
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100632 .family = NFPROTO_UNSPEC,
Jozsef Kadlecsik35b8dcf2013-04-30 23:02:43 +0200633 .revision_min = IPSET_TYPE_REV_MIN,
634 .revision_max = IPSET_TYPE_REV_MAX,
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100635 .create = list_set_create,
636 .create_policy = {
637 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
638 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
Jozsef Kadlecsikde763032013-04-08 23:11:32 +0200639 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100640 },
641 .adt_policy = {
642 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
643 .len = IPSET_MAXNAMELEN },
644 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
645 .len = IPSET_MAXNAMELEN },
646 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
647 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
648 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
Jozsef Kadlecsikde763032013-04-08 23:11:32 +0200649 [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
650 [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
Sergey Popovich03726182015-05-02 19:28:16 +0200651 [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING,
652 .len = IPSET_MAX_COMMENT_SIZE },
Anton Danilovcbee93d2014-08-28 10:11:30 +0400653 [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 },
654 [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 },
655 [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 },
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100656 },
657 .me = THIS_MODULE,
658};
659
660static int __init
661list_set_init(void)
662{
663 return ip_set_type_register(&list_set_type);
664}
665
666static void __exit
667list_set_fini(void)
668{
Jozsef Kadlecsik00590fd2015-06-13 16:56:02 +0200669 rcu_barrier();
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100670 ip_set_type_unregister(&list_set_type);
671}
672
673module_init(list_set_init);
674module_exit(list_set_fini);