]> git.ipfire.org Git - thirdparty/kernel/stable.git/blame - net/sched/act_tunnel_key.c
net: sched: atomically check-allocate action
[thirdparty/kernel/stable.git] / net / sched / act_tunnel_key.c
CommitLineData
d0f6dd8a
AV
1/*
2 * Copyright (c) 2016, Amir Vadai <amir@vadai.me>
3 * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
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 as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/skbuff.h>
15#include <linux/rtnetlink.h>
0ed5269f 16#include <net/geneve.h>
d0f6dd8a
AV
17#include <net/netlink.h>
18#include <net/pkt_sched.h>
19#include <net/dst.h>
d0f6dd8a
AV
20
21#include <linux/tc_act/tc_tunnel_key.h>
22#include <net/tc_act/tc_tunnel_key.h>
23
c7d03a00 24static unsigned int tunnel_key_net_id;
d0f6dd8a
AV
25static struct tc_action_ops act_tunnel_key_ops;
26
27static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a,
28 struct tcf_result *res)
29{
30 struct tcf_tunnel_key *t = to_tunnel_key(a);
31 struct tcf_tunnel_key_params *params;
32 int action;
33
34 rcu_read_lock();
35
36 params = rcu_dereference(t->params);
37
38 tcf_lastuse_update(&t->tcf_tm);
39 bstats_cpu_update(this_cpu_ptr(t->common.cpu_bstats), skb);
40 action = params->action;
41
42 switch (params->tcft_action) {
43 case TCA_TUNNEL_KEY_ACT_RELEASE:
44 skb_dst_drop(skb);
45 break;
46 case TCA_TUNNEL_KEY_ACT_SET:
47 skb_dst_drop(skb);
48 skb_dst_set(skb, dst_clone(&params->tcft_enc_metadata->dst));
49 break;
50 default:
51 WARN_ONCE(1, "Bad tunnel_key action %d.\n",
52 params->tcft_action);
53 break;
54 }
55
56 rcu_read_unlock();
57
58 return action;
59}
60
0ed5269f
SH
61static const struct nla_policy
62enc_opts_policy[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1] = {
63 [TCA_TUNNEL_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED },
64};
65
66static const struct nla_policy
67geneve_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1] = {
68 [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 },
69 [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 },
70 [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY,
71 .len = 128 },
72};
73
74static int
75tunnel_key_copy_geneve_opt(const struct nlattr *nla, void *dst, int dst_len,
76 struct netlink_ext_ack *extack)
77{
78 struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
79 int err, data_len, opt_len;
80 u8 *data;
81
82 err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX,
83 nla, geneve_opt_policy, extack);
84 if (err < 0)
85 return err;
86
87 if (!tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] ||
88 !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] ||
89 !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]) {
90 NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data");
91 return -EINVAL;
92 }
93
94 data = nla_data(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
95 data_len = nla_len(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
96 if (data_len < 4) {
97 NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long");
98 return -ERANGE;
99 }
100 if (data_len % 4) {
101 NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long");
102 return -ERANGE;
103 }
104
105 opt_len = sizeof(struct geneve_opt) + data_len;
106 if (dst) {
107 struct geneve_opt *opt = dst;
108
109 WARN_ON(dst_len < opt_len);
110
111 opt->opt_class =
112 nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]);
113 opt->type = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]);
114 opt->length = data_len / 4; /* length is in units of 4 bytes */
115 opt->r1 = 0;
116 opt->r2 = 0;
117 opt->r3 = 0;
118
119 memcpy(opt + 1, data, data_len);
120 }
121
122 return opt_len;
123}
124
125static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst,
126 int dst_len, struct netlink_ext_ack *extack)
127{
128 int err, rem, opt_len, len = nla_len(nla), opts_len = 0;
129 const struct nlattr *attr, *head = nla_data(nla);
130
131 err = nla_validate(head, len, TCA_TUNNEL_KEY_ENC_OPTS_MAX,
132 enc_opts_policy, extack);
133 if (err)
134 return err;
135
136 nla_for_each_attr(attr, head, len, rem) {
137 switch (nla_type(attr)) {
138 case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
139 opt_len = tunnel_key_copy_geneve_opt(attr, dst,
140 dst_len, extack);
141 if (opt_len < 0)
142 return opt_len;
143 opts_len += opt_len;
144 if (dst) {
145 dst_len -= opt_len;
146 dst += opt_len;
147 }
148 break;
149 }
150 }
151
152 if (!opts_len) {
153 NL_SET_ERR_MSG(extack, "Empty list of tunnel options");
154 return -EINVAL;
155 }
156
157 if (rem > 0) {
158 NL_SET_ERR_MSG(extack, "Trailing data after parsing tunnel key options attributes");
159 return -EINVAL;
160 }
161
162 return opts_len;
163}
164
165static int tunnel_key_get_opts_len(struct nlattr *nla,
166 struct netlink_ext_ack *extack)
167{
168 return tunnel_key_copy_opts(nla, NULL, 0, extack);
169}
170
171static int tunnel_key_opts_set(struct nlattr *nla, struct ip_tunnel_info *info,
172 int opts_len, struct netlink_ext_ack *extack)
173{
174 info->options_len = opts_len;
175 switch (nla_type(nla_data(nla))) {
176 case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
177#if IS_ENABLED(CONFIG_INET)
178 info->key.tun_flags |= TUNNEL_GENEVE_OPT;
179 return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info),
180 opts_len, extack);
181#else
182 return -EAFNOSUPPORT;
183#endif
184 default:
185 NL_SET_ERR_MSG(extack, "Cannot set tunnel options for unknown tunnel type");
186 return -EINVAL;
187 }
188}
189
d0f6dd8a
AV
190static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
191 [TCA_TUNNEL_KEY_PARMS] = { .len = sizeof(struct tc_tunnel_key) },
192 [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 },
193 [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NLA_U32 },
194 [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) },
195 [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) },
196 [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NLA_U32 },
75bfbca0 197 [TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16},
86087e17 198 [TCA_TUNNEL_KEY_NO_CSUM] = { .type = NLA_U8 },
0ed5269f 199 [TCA_TUNNEL_KEY_ENC_OPTS] = { .type = NLA_NESTED },
d0f6dd8a
AV
200};
201
202static int tunnel_key_init(struct net *net, struct nlattr *nla,
203 struct nlattr *est, struct tc_action **a,
789871bb
VB
204 int ovr, int bind, bool rtnl_held,
205 struct netlink_ext_ack *extack)
d0f6dd8a
AV
206{
207 struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
208 struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1];
209 struct tcf_tunnel_key_params *params_old;
210 struct tcf_tunnel_key_params *params_new;
211 struct metadata_dst *metadata = NULL;
212 struct tc_tunnel_key *parm;
213 struct tcf_tunnel_key *t;
214 bool exists = false;
75bfbca0 215 __be16 dst_port = 0;
0ed5269f 216 int opts_len = 0;
d0f6dd8a 217 __be64 key_id;
86087e17 218 __be16 flags;
d0f6dd8a
AV
219 int ret = 0;
220 int err;
221
9d7298cd
SH
222 if (!nla) {
223 NL_SET_ERR_MSG(extack, "Tunnel requires attributes to be passed");
d0f6dd8a 224 return -EINVAL;
9d7298cd 225 }
d0f6dd8a 226
fceb6435 227 err = nla_parse_nested(tb, TCA_TUNNEL_KEY_MAX, nla, tunnel_key_policy,
9d7298cd
SH
228 extack);
229 if (err < 0) {
230 NL_SET_ERR_MSG(extack, "Failed to parse nested tunnel key attributes");
d0f6dd8a 231 return err;
9d7298cd 232 }
d0f6dd8a 233
9d7298cd
SH
234 if (!tb[TCA_TUNNEL_KEY_PARMS]) {
235 NL_SET_ERR_MSG(extack, "Missing tunnel key parameters");
d0f6dd8a 236 return -EINVAL;
9d7298cd 237 }
d0f6dd8a
AV
238
239 parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]);
0190c1d4
VB
240 err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
241 if (err < 0)
242 return err;
243 exists = err;
d0f6dd8a
AV
244 if (exists && bind)
245 return 0;
246
247 switch (parm->t_action) {
248 case TCA_TUNNEL_KEY_ACT_RELEASE:
249 break;
250 case TCA_TUNNEL_KEY_ACT_SET:
251 if (!tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
9d7298cd 252 NL_SET_ERR_MSG(extack, "Missing tunnel key id");
d0f6dd8a
AV
253 ret = -EINVAL;
254 goto err_out;
255 }
256
257 key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]));
258
86087e17
JB
259 flags = TUNNEL_KEY | TUNNEL_CSUM;
260 if (tb[TCA_TUNNEL_KEY_NO_CSUM] &&
261 nla_get_u8(tb[TCA_TUNNEL_KEY_NO_CSUM]))
262 flags &= ~TUNNEL_CSUM;
263
75bfbca0
HHZ
264 if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT])
265 dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
266
0ed5269f
SH
267 if (tb[TCA_TUNNEL_KEY_ENC_OPTS]) {
268 opts_len = tunnel_key_get_opts_len(tb[TCA_TUNNEL_KEY_ENC_OPTS],
269 extack);
270 if (opts_len < 0) {
271 ret = opts_len;
272 goto err_out;
273 }
274 }
275
d0f6dd8a
AV
276 if (tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC] &&
277 tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]) {
278 __be32 saddr;
279 __be32 daddr;
280
281 saddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC]);
282 daddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]);
283
284 metadata = __ip_tun_set_dst(saddr, daddr, 0, 0,
86087e17 285 dst_port, flags,
0ed5269f 286 key_id, opts_len);
d0f6dd8a
AV
287 } else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] &&
288 tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) {
289 struct in6_addr saddr;
290 struct in6_addr daddr;
291
292 saddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC]);
293 daddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]);
294
dc594ecd 295 metadata = __ipv6_tun_set_dst(&saddr, &daddr, 0, 0, dst_port,
86087e17 296 0, flags,
75bfbca0 297 key_id, 0);
a1165b59 298 } else {
9d7298cd 299 NL_SET_ERR_MSG(extack, "Missing either ipv4 or ipv6 src and dst");
a1165b59
SH
300 ret = -EINVAL;
301 goto err_out;
d0f6dd8a
AV
302 }
303
304 if (!metadata) {
9d7298cd 305 NL_SET_ERR_MSG(extack, "Cannot allocate tunnel metadata dst");
a1165b59 306 ret = -ENOMEM;
d0f6dd8a
AV
307 goto err_out;
308 }
309
0ed5269f
SH
310 if (opts_len) {
311 ret = tunnel_key_opts_set(tb[TCA_TUNNEL_KEY_ENC_OPTS],
312 &metadata->u.tun_info,
313 opts_len, extack);
314 if (ret < 0)
315 goto err_out;
316 }
317
d0f6dd8a
AV
318 metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
319 break;
320 default:
9d7298cd 321 NL_SET_ERR_MSG(extack, "Unknown tunnel key action");
51d4740f 322 ret = -EINVAL;
d0f6dd8a
AV
323 goto err_out;
324 }
325
326 if (!exists) {
65a206c0
CM
327 ret = tcf_idr_create(tn, parm->index, est, a,
328 &act_tunnel_key_ops, bind, true);
9d7298cd
SH
329 if (ret) {
330 NL_SET_ERR_MSG(extack, "Cannot create TC IDR");
0190c1d4 331 goto err_out;
9d7298cd 332 }
d0f6dd8a
AV
333
334 ret = ACT_P_CREATED;
4e8ddd7f 335 } else if (!ovr) {
65a206c0 336 tcf_idr_release(*a, bind);
4e8ddd7f
VB
337 NL_SET_ERR_MSG(extack, "TC IDR already exists");
338 return -EEXIST;
d0f6dd8a
AV
339 }
340
341 t = to_tunnel_key(*a);
342
343 ASSERT_RTNL();
344 params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
345 if (unlikely(!params_new)) {
4e8ddd7f 346 tcf_idr_release(*a, bind);
9d7298cd 347 NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters");
d0f6dd8a
AV
348 return -ENOMEM;
349 }
350
351 params_old = rtnl_dereference(t->params);
352
353 params_new->action = parm->action;
354 params_new->tcft_action = parm->t_action;
355 params_new->tcft_enc_metadata = metadata;
356
357 rcu_assign_pointer(t->params, params_new);
358
359 if (params_old)
360 kfree_rcu(params_old, rcu);
361
362 if (ret == ACT_P_CREATED)
65a206c0 363 tcf_idr_insert(tn, *a);
d0f6dd8a
AV
364
365 return ret;
366
367err_out:
368 if (exists)
65a206c0 369 tcf_idr_release(*a, bind);
0190c1d4
VB
370 else
371 tcf_idr_cleanup(tn, parm->index);
d0f6dd8a
AV
372 return ret;
373}
374
9a63b255 375static void tunnel_key_release(struct tc_action *a)
d0f6dd8a
AV
376{
377 struct tcf_tunnel_key *t = to_tunnel_key(a);
378 struct tcf_tunnel_key_params *params;
379
07c0f09e 380 params = rcu_dereference_protected(t->params, 1);
abdadd3c
DC
381 if (params) {
382 if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
383 dst_release(&params->tcft_enc_metadata->dst);
d0f6dd8a 384
abdadd3c
DC
385 kfree_rcu(params, rcu);
386 }
d0f6dd8a
AV
387}
388
0ed5269f
SH
389static int tunnel_key_geneve_opts_dump(struct sk_buff *skb,
390 const struct ip_tunnel_info *info)
391{
392 int len = info->options_len;
393 u8 *src = (u8 *)(info + 1);
394 struct nlattr *start;
395
396 start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE);
397 if (!start)
398 return -EMSGSIZE;
399
400 while (len > 0) {
401 struct geneve_opt *opt = (struct geneve_opt *)src;
402
403 if (nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS,
404 opt->opt_class) ||
405 nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE,
406 opt->type) ||
407 nla_put(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA,
408 opt->length * 4, opt + 1))
409 return -EMSGSIZE;
410
411 len -= sizeof(struct geneve_opt) + opt->length * 4;
412 src += sizeof(struct geneve_opt) + opt->length * 4;
413 }
414
415 nla_nest_end(skb, start);
416 return 0;
417}
418
419static int tunnel_key_opts_dump(struct sk_buff *skb,
420 const struct ip_tunnel_info *info)
421{
422 struct nlattr *start;
423 int err;
424
425 if (!info->options_len)
426 return 0;
427
428 start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS);
429 if (!start)
430 return -EMSGSIZE;
431
432 if (info->key.tun_flags & TUNNEL_GENEVE_OPT) {
433 err = tunnel_key_geneve_opts_dump(skb, info);
434 if (err)
435 return err;
436 } else {
437 return -EINVAL;
438 }
439
440 nla_nest_end(skb, start);
441 return 0;
442}
443
d0f6dd8a
AV
444static int tunnel_key_dump_addresses(struct sk_buff *skb,
445 const struct ip_tunnel_info *info)
446{
447 unsigned short family = ip_tunnel_info_af(info);
448
449 if (family == AF_INET) {
450 __be32 saddr = info->key.u.ipv4.src;
451 __be32 daddr = info->key.u.ipv4.dst;
452
453 if (!nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_SRC, saddr) &&
454 !nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_DST, daddr))
455 return 0;
456 }
457
458 if (family == AF_INET6) {
459 const struct in6_addr *saddr6 = &info->key.u.ipv6.src;
460 const struct in6_addr *daddr6 = &info->key.u.ipv6.dst;
461
462 if (!nla_put_in6_addr(skb,
463 TCA_TUNNEL_KEY_ENC_IPV6_SRC, saddr6) &&
464 !nla_put_in6_addr(skb,
465 TCA_TUNNEL_KEY_ENC_IPV6_DST, daddr6))
466 return 0;
467 }
468
469 return -EINVAL;
470}
471
472static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
473 int bind, int ref)
474{
475 unsigned char *b = skb_tail_pointer(skb);
476 struct tcf_tunnel_key *t = to_tunnel_key(a);
477 struct tcf_tunnel_key_params *params;
478 struct tc_tunnel_key opt = {
479 .index = t->tcf_index,
036bb443
VB
480 .refcnt = refcount_read(&t->tcf_refcnt) - ref,
481 .bindcnt = atomic_read(&t->tcf_bindcnt) - bind,
d0f6dd8a
AV
482 };
483 struct tcf_t tm;
d0f6dd8a 484
07c0f09e 485 params = rtnl_dereference(t->params);
d0f6dd8a
AV
486
487 opt.t_action = params->tcft_action;
488 opt.action = params->action;
489
490 if (nla_put(skb, TCA_TUNNEL_KEY_PARMS, sizeof(opt), &opt))
491 goto nla_put_failure;
492
493 if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) {
0ed5269f
SH
494 struct ip_tunnel_info *info =
495 &params->tcft_enc_metadata->u.tun_info;
496 struct ip_tunnel_key *key = &info->key;
d0f6dd8a
AV
497 __be32 key_id = tunnel_id_to_key32(key->tun_id);
498
499 if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) ||
500 tunnel_key_dump_addresses(skb,
75bfbca0 501 &params->tcft_enc_metadata->u.tun_info) ||
86087e17
JB
502 nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) ||
503 nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
0ed5269f
SH
504 !(key->tun_flags & TUNNEL_CSUM)) ||
505 tunnel_key_opts_dump(skb, info))
d0f6dd8a
AV
506 goto nla_put_failure;
507 }
508
509 tcf_tm_dump(&tm, &t->tcf_tm);
510 if (nla_put_64bit(skb, TCA_TUNNEL_KEY_TM, sizeof(tm),
511 &tm, TCA_TUNNEL_KEY_PAD))
512 goto nla_put_failure;
513
07c0f09e 514 return skb->len;
d0f6dd8a
AV
515
516nla_put_failure:
517 nlmsg_trim(skb, b);
07c0f09e 518 return -1;
d0f6dd8a
AV
519}
520
521static int tunnel_key_walker(struct net *net, struct sk_buff *skb,
522 struct netlink_callback *cb, int type,
41780105
AA
523 const struct tc_action_ops *ops,
524 struct netlink_ext_ack *extack)
d0f6dd8a
AV
525{
526 struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
527
b3620145 528 return tcf_generic_walker(tn, skb, cb, type, ops, extack);
d0f6dd8a
AV
529}
530
331a9295
AA
531static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index,
532 struct netlink_ext_ack *extack)
d0f6dd8a
AV
533{
534 struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
535
65a206c0 536 return tcf_idr_search(tn, a, index);
d0f6dd8a
AV
537}
538
b409074e
VB
539static int tunnel_key_delete(struct net *net, u32 index)
540{
541 struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
542
543 return tcf_idr_delete_index(tn, index);
544}
545
d0f6dd8a
AV
546static struct tc_action_ops act_tunnel_key_ops = {
547 .kind = "tunnel_key",
548 .type = TCA_ACT_TUNNEL_KEY,
549 .owner = THIS_MODULE,
550 .act = tunnel_key_act,
551 .dump = tunnel_key_dump,
552 .init = tunnel_key_init,
553 .cleanup = tunnel_key_release,
554 .walk = tunnel_key_walker,
555 .lookup = tunnel_key_search,
b409074e 556 .delete = tunnel_key_delete,
d0f6dd8a
AV
557 .size = sizeof(struct tcf_tunnel_key),
558};
559
560static __net_init int tunnel_key_init_net(struct net *net)
561{
562 struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
563
c7e460ce 564 return tc_action_net_init(tn, &act_tunnel_key_ops);
d0f6dd8a
AV
565}
566
039af9c6 567static void __net_exit tunnel_key_exit_net(struct list_head *net_list)
d0f6dd8a 568{
039af9c6 569 tc_action_net_exit(net_list, tunnel_key_net_id);
d0f6dd8a
AV
570}
571
572static struct pernet_operations tunnel_key_net_ops = {
573 .init = tunnel_key_init_net,
039af9c6 574 .exit_batch = tunnel_key_exit_net,
d0f6dd8a
AV
575 .id = &tunnel_key_net_id,
576 .size = sizeof(struct tc_action_net),
577};
578
579static int __init tunnel_key_init_module(void)
580{
581 return tcf_register_action(&act_tunnel_key_ops, &tunnel_key_net_ops);
582}
583
584static void __exit tunnel_key_cleanup_module(void)
585{
586 tcf_unregister_action(&act_tunnel_key_ops, &tunnel_key_net_ops);
587}
588
589module_init(tunnel_key_init_module);
590module_exit(tunnel_key_cleanup_module);
591
592MODULE_AUTHOR("Amir Vadai <amir@vadai.me>");
593MODULE_DESCRIPTION("ip tunnel manipulation actions");
594MODULE_LICENSE("GPL v2");