Blame view

net/sched/act_tunnel_key.c 22 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
d0f6dd8a9   Amir Vadai   net/sched: Introd...
2
3
4
  /*
   * Copyright (c) 2016, Amir Vadai <amir@vadai.me>
   * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
d0f6dd8a9   Amir Vadai   net/sched: Introd...
5
6
7
8
9
10
11
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
0ed5269f9   Simon Horman   net/sched: add tu...
12
  #include <net/geneve.h>
fca3f91cc   Xin Long   net: sched: add v...
13
  #include <net/vxlan.h>
e20d4ff2a   Xin Long   net: sched: add e...
14
  #include <net/erspan.h>
d0f6dd8a9   Amir Vadai   net/sched: Introd...
15
16
17
  #include <net/netlink.h>
  #include <net/pkt_sched.h>
  #include <net/dst.h>
e5fdabacb   Davide Caratti   net/sched: act_tu...
18
  #include <net/pkt_cls.h>
d0f6dd8a9   Amir Vadai   net/sched: Introd...
19
20
21
  
  #include <linux/tc_act/tc_tunnel_key.h>
  #include <net/tc_act/tc_tunnel_key.h>
c7d03a00b   Alexey Dobriyan   netns: make struc...
22
  static unsigned int tunnel_key_net_id;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
23
24
25
26
27
28
29
30
  static struct tc_action_ops act_tunnel_key_ops;
  
  static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a,
  			  struct tcf_result *res)
  {
  	struct tcf_tunnel_key *t = to_tunnel_key(a);
  	struct tcf_tunnel_key_params *params;
  	int action;
7fd4b288e   Paolo Abeni   tc/act: remove un...
31
  	params = rcu_dereference_bh(t->params);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
32
33
  
  	tcf_lastuse_update(&t->tcf_tm);
5e1ad95b6   Vlad Buslov   net: sched: extra...
34
  	tcf_action_update_bstats(&t->common, skb);
38230a3e0   Davide Caratti   net/sched: act_tu...
35
  	action = READ_ONCE(t->tcf_action);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
  	switch (params->tcft_action) {
  	case TCA_TUNNEL_KEY_ACT_RELEASE:
  		skb_dst_drop(skb);
  		break;
  	case TCA_TUNNEL_KEY_ACT_SET:
  		skb_dst_drop(skb);
  		skb_dst_set(skb, dst_clone(&params->tcft_enc_metadata->dst));
  		break;
  	default:
  		WARN_ONCE(1, "Bad tunnel_key action %d.
  ",
  			  params->tcft_action);
  		break;
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
51
52
  	return action;
  }
0ed5269f9   Simon Horman   net/sched: add tu...
53
54
  static const struct nla_policy
  enc_opts_policy[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1] = {
fca3f91cc   Xin Long   net: sched: add v...
55
56
  	[TCA_TUNNEL_KEY_ENC_OPTS_UNSPEC]	= {
  		.strict_start_type = TCA_TUNNEL_KEY_ENC_OPTS_VXLAN },
0ed5269f9   Simon Horman   net/sched: add tu...
57
  	[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]	= { .type = NLA_NESTED },
fca3f91cc   Xin Long   net: sched: add v...
58
  	[TCA_TUNNEL_KEY_ENC_OPTS_VXLAN]		= { .type = NLA_NESTED },
e20d4ff2a   Xin Long   net: sched: add e...
59
  	[TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN]	= { .type = NLA_NESTED },
0ed5269f9   Simon Horman   net/sched: add tu...
60
61
62
63
64
65
66
67
68
  };
  
  static const struct nla_policy
  geneve_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1] = {
  	[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]	   = { .type = NLA_U16 },
  	[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]	   = { .type = NLA_U8 },
  	[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]	   = { .type = NLA_BINARY,
  						       .len = 128 },
  };
fca3f91cc   Xin Long   net: sched: add v...
69
70
71
72
  static const struct nla_policy
  vxlan_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX + 1] = {
  	[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP]	   = { .type = NLA_U32 },
  };
e20d4ff2a   Xin Long   net: sched: add e...
73
74
75
76
77
78
79
  static const struct nla_policy
  erspan_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1] = {
  	[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]	   = { .type = NLA_U8 },
  	[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]	   = { .type = NLA_U32 },
  	[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR]	   = { .type = NLA_U8 },
  	[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]	   = { .type = NLA_U8 },
  };
0ed5269f9   Simon Horman   net/sched: add tu...
80
81
82
83
84
85
86
  static int
  tunnel_key_copy_geneve_opt(const struct nlattr *nla, void *dst, int dst_len,
  			   struct netlink_ext_ack *extack)
  {
  	struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
  	int err, data_len, opt_len;
  	u8 *data;
8cb081746   Johannes Berg   netlink: make val...
87
88
89
  	err = nla_parse_nested_deprecated(tb,
  					  TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX,
  					  nla, geneve_opt_policy, extack);
0ed5269f9   Simon Horman   net/sched: add tu...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  	if (err < 0)
  		return err;
  
  	if (!tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] ||
  	    !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] ||
  	    !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]) {
  		NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data");
  		return -EINVAL;
  	}
  
  	data = nla_data(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
  	data_len = nla_len(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
  	if (data_len < 4) {
  		NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long");
  		return -ERANGE;
  	}
  	if (data_len % 4) {
  		NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long");
  		return -ERANGE;
  	}
  
  	opt_len = sizeof(struct geneve_opt) + data_len;
  	if (dst) {
  		struct geneve_opt *opt = dst;
  
  		WARN_ON(dst_len < opt_len);
  
  		opt->opt_class =
  			nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]);
  		opt->type = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]);
  		opt->length = data_len / 4; /* length is in units of 4 bytes */
  		opt->r1 = 0;
  		opt->r2 = 0;
  		opt->r3 = 0;
  
  		memcpy(opt + 1, data, data_len);
  	}
  
  	return opt_len;
  }
fca3f91cc   Xin Long   net: sched: add v...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  static int
  tunnel_key_copy_vxlan_opt(const struct nlattr *nla, void *dst, int dst_len,
  			  struct netlink_ext_ack *extack)
  {
  	struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX + 1];
  	int err;
  
  	err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX, nla,
  			       vxlan_opt_policy, extack);
  	if (err < 0)
  		return err;
  
  	if (!tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP]) {
  		NL_SET_ERR_MSG(extack, "Missing tunnel key vxlan option gbp");
  		return -EINVAL;
  	}
  
  	if (dst) {
  		struct vxlan_metadata *md = dst;
  
  		md->gbp = nla_get_u32(tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP]);
13e6ce98a   Xin Long   net: sched: only ...
151
  		md->gbp &= VXLAN_GBP_MASK;
fca3f91cc   Xin Long   net: sched: add v...
152
153
154
155
  	}
  
  	return sizeof(struct vxlan_metadata);
  }
e20d4ff2a   Xin Long   net: sched: add e...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  static int
  tunnel_key_copy_erspan_opt(const struct nlattr *nla, void *dst, int dst_len,
  			   struct netlink_ext_ack *extack)
  {
  	struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1];
  	int err;
  	u8 ver;
  
  	err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, nla,
  			       erspan_opt_policy, extack);
  	if (err < 0)
  		return err;
  
  	if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]) {
  		NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option ver");
  		return -EINVAL;
  	}
  
  	ver = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]);
  	if (ver == 1) {
  		if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]) {
  			NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option index");
  			return -EINVAL;
  		}
  	} else if (ver == 2) {
  		if (!tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR] ||
  		    !tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]) {
  			NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option dir or hwid");
  			return -EINVAL;
  		}
  	} else {
  		NL_SET_ERR_MSG(extack, "Tunnel key erspan option ver is incorrect");
  		return -EINVAL;
  	}
  
  	if (dst) {
  		struct erspan_metadata *md = dst;
  
  		md->version = ver;
  		if (ver == 1) {
  			nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX];
  			md->u.index = nla_get_be32(nla);
  		} else {
  			nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR];
  			md->u.md2.dir = nla_get_u8(nla);
  			nla = tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID];
  			set_hwid(&md->u.md2, nla_get_u8(nla));
  		}
  	}
  
  	return sizeof(struct erspan_metadata);
  }
0ed5269f9   Simon Horman   net/sched: add tu...
208
209
210
  static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst,
  				int dst_len, struct netlink_ext_ack *extack)
  {
fca3f91cc   Xin Long   net: sched: add v...
211
  	int err, rem, opt_len, len = nla_len(nla), opts_len = 0, type = 0;
0ed5269f9   Simon Horman   net/sched: add tu...
212
  	const struct nlattr *attr, *head = nla_data(nla);
8cb081746   Johannes Berg   netlink: make val...
213
214
  	err = nla_validate_deprecated(head, len, TCA_TUNNEL_KEY_ENC_OPTS_MAX,
  				      enc_opts_policy, extack);
0ed5269f9   Simon Horman   net/sched: add tu...
215
216
217
218
219
220
  	if (err)
  		return err;
  
  	nla_for_each_attr(attr, head, len, rem) {
  		switch (nla_type(attr)) {
  		case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
fca3f91cc   Xin Long   net: sched: add v...
221
222
223
224
  			if (type && type != TUNNEL_GENEVE_OPT) {
  				NL_SET_ERR_MSG(extack, "Duplicate type for geneve options");
  				return -EINVAL;
  			}
0ed5269f9   Simon Horman   net/sched: add tu...
225
226
227
228
229
  			opt_len = tunnel_key_copy_geneve_opt(attr, dst,
  							     dst_len, extack);
  			if (opt_len < 0)
  				return opt_len;
  			opts_len += opt_len;
4f0e97d07   Xin Long   net: sched: ensur...
230
231
232
233
  			if (opts_len > IP_TUNNEL_OPTS_MAX) {
  				NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size");
  				return -EINVAL;
  			}
0ed5269f9   Simon Horman   net/sched: add tu...
234
235
236
237
  			if (dst) {
  				dst_len -= opt_len;
  				dst += opt_len;
  			}
fca3f91cc   Xin Long   net: sched: add v...
238
239
240
241
242
243
244
245
246
247
248
249
250
  			type = TUNNEL_GENEVE_OPT;
  			break;
  		case TCA_TUNNEL_KEY_ENC_OPTS_VXLAN:
  			if (type) {
  				NL_SET_ERR_MSG(extack, "Duplicate type for vxlan options");
  				return -EINVAL;
  			}
  			opt_len = tunnel_key_copy_vxlan_opt(attr, dst,
  							    dst_len, extack);
  			if (opt_len < 0)
  				return opt_len;
  			opts_len += opt_len;
  			type = TUNNEL_VXLAN_OPT;
0ed5269f9   Simon Horman   net/sched: add tu...
251
  			break;
e20d4ff2a   Xin Long   net: sched: add e...
252
253
254
255
256
257
258
259
260
261
262
263
  		case TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN:
  			if (type) {
  				NL_SET_ERR_MSG(extack, "Duplicate type for erspan options");
  				return -EINVAL;
  			}
  			opt_len = tunnel_key_copy_erspan_opt(attr, dst,
  							     dst_len, extack);
  			if (opt_len < 0)
  				return opt_len;
  			opts_len += opt_len;
  			type = TUNNEL_ERSPAN_OPT;
  			break;
0ed5269f9   Simon Horman   net/sched: add tu...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  		}
  	}
  
  	if (!opts_len) {
  		NL_SET_ERR_MSG(extack, "Empty list of tunnel options");
  		return -EINVAL;
  	}
  
  	if (rem > 0) {
  		NL_SET_ERR_MSG(extack, "Trailing data after parsing tunnel key options attributes");
  		return -EINVAL;
  	}
  
  	return opts_len;
  }
  
  static int tunnel_key_get_opts_len(struct nlattr *nla,
  				   struct netlink_ext_ack *extack)
  {
  	return tunnel_key_copy_opts(nla, NULL, 0, extack);
  }
  
  static int tunnel_key_opts_set(struct nlattr *nla, struct ip_tunnel_info *info,
  			       int opts_len, struct netlink_ext_ack *extack)
  {
  	info->options_len = opts_len;
  	switch (nla_type(nla_data(nla))) {
  	case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
  #if IS_ENABLED(CONFIG_INET)
  		info->key.tun_flags |= TUNNEL_GENEVE_OPT;
  		return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info),
  					    opts_len, extack);
  #else
  		return -EAFNOSUPPORT;
  #endif
fca3f91cc   Xin Long   net: sched: add v...
299
300
301
302
303
304
305
306
  	case TCA_TUNNEL_KEY_ENC_OPTS_VXLAN:
  #if IS_ENABLED(CONFIG_INET)
  		info->key.tun_flags |= TUNNEL_VXLAN_OPT;
  		return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info),
  					    opts_len, extack);
  #else
  		return -EAFNOSUPPORT;
  #endif
e20d4ff2a   Xin Long   net: sched: add e...
307
308
309
310
311
312
313
314
  	case TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN:
  #if IS_ENABLED(CONFIG_INET)
  		info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
  		return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info),
  					    opts_len, extack);
  #else
  		return -EAFNOSUPPORT;
  #endif
0ed5269f9   Simon Horman   net/sched: add tu...
315
316
317
318
319
  	default:
  		NL_SET_ERR_MSG(extack, "Cannot set tunnel options for unknown tunnel type");
  		return -EINVAL;
  	}
  }
d0f6dd8a9   Amir Vadai   net/sched: Introd...
320
321
322
323
324
325
326
  static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
  	[TCA_TUNNEL_KEY_PARMS]	    = { .len = sizeof(struct tc_tunnel_key) },
  	[TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 },
  	[TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NLA_U32 },
  	[TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) },
  	[TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) },
  	[TCA_TUNNEL_KEY_ENC_KEY_ID]   = { .type = NLA_U32 },
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
327
  	[TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16},
86087e170   Jiri Benc   net: sched: act_t...
328
  	[TCA_TUNNEL_KEY_NO_CSUM]      = { .type = NLA_U8 },
0ed5269f9   Simon Horman   net/sched: add tu...
329
  	[TCA_TUNNEL_KEY_ENC_OPTS]     = { .type = NLA_NESTED },
07a557f47   Or Gerlitz   net/sched: tunnel...
330
331
  	[TCA_TUNNEL_KEY_ENC_TOS]      = { .type = NLA_U8 },
  	[TCA_TUNNEL_KEY_ENC_TTL]      = { .type = NLA_U8 },
d0f6dd8a9   Amir Vadai   net/sched: Introd...
332
  };
9174c3df1   Davide Caratti   net/sched: act_tu...
333
334
335
336
  static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
  {
  	if (!p)
  		return;
4177c5d94   wenxu   net/sched: act_tu...
337
  	if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
9174c3df1   Davide Caratti   net/sched: act_tu...
338
  		dst_release(&p->tcft_enc_metadata->dst);
4177c5d94   wenxu   net/sched: act_tu...
339

9174c3df1   Davide Caratti   net/sched: act_tu...
340
341
  	kfree_rcu(p, rcu);
  }
d0f6dd8a9   Amir Vadai   net/sched: Introd...
342
343
  static int tunnel_key_init(struct net *net, struct nlattr *nla,
  			   struct nlattr *est, struct tc_action **a,
789871bb2   Vlad Buslov   net: sched: imple...
344
  			   int ovr, int bind, bool rtnl_held,
abbb0d336   Vlad Buslov   net: sched: exten...
345
  			   struct tcf_proto *tp, u32 act_flags,
789871bb2   Vlad Buslov   net: sched: imple...
346
  			   struct netlink_ext_ack *extack)
d0f6dd8a9   Amir Vadai   net/sched: Introd...
347
348
349
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
  	struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1];
d0f6dd8a9   Amir Vadai   net/sched: Introd...
350
351
  	struct tcf_tunnel_key_params *params_new;
  	struct metadata_dst *metadata = NULL;
e5fdabacb   Davide Caratti   net/sched: act_tu...
352
  	struct tcf_chain *goto_ch = NULL;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
353
354
355
  	struct tc_tunnel_key *parm;
  	struct tcf_tunnel_key *t;
  	bool exists = false;
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
356
  	__be16 dst_port = 0;
80ef0f22c   Adi Nissim   net/sched: act_tu...
357
  	__be64 key_id = 0;
0ed5269f9   Simon Horman   net/sched: add tu...
358
  	int opts_len = 0;
80ef0f22c   Adi Nissim   net/sched: act_tu...
359
  	__be16 flags = 0;
07a557f47   Or Gerlitz   net/sched: tunnel...
360
  	u8 tos, ttl;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
361
  	int ret = 0;
7be8ef2cd   Dmytro Linkin   net: sched: use t...
362
  	u32 index;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
363
  	int err;
9d7298cd1   Simon Horman   net/sched: act_tu...
364
365
  	if (!nla) {
  		NL_SET_ERR_MSG(extack, "Tunnel requires attributes to be passed");
d0f6dd8a9   Amir Vadai   net/sched: Introd...
366
  		return -EINVAL;
9d7298cd1   Simon Horman   net/sched: act_tu...
367
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
368

8cb081746   Johannes Berg   netlink: make val...
369
370
  	err = nla_parse_nested_deprecated(tb, TCA_TUNNEL_KEY_MAX, nla,
  					  tunnel_key_policy, extack);
9d7298cd1   Simon Horman   net/sched: act_tu...
371
372
  	if (err < 0) {
  		NL_SET_ERR_MSG(extack, "Failed to parse nested tunnel key attributes");
d0f6dd8a9   Amir Vadai   net/sched: Introd...
373
  		return err;
9d7298cd1   Simon Horman   net/sched: act_tu...
374
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
375

9d7298cd1   Simon Horman   net/sched: act_tu...
376
377
  	if (!tb[TCA_TUNNEL_KEY_PARMS]) {
  		NL_SET_ERR_MSG(extack, "Missing tunnel key parameters");
d0f6dd8a9   Amir Vadai   net/sched: Introd...
378
  		return -EINVAL;
9d7298cd1   Simon Horman   net/sched: act_tu...
379
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
380
381
  
  	parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]);
7be8ef2cd   Dmytro Linkin   net: sched: use t...
382
383
  	index = parm->index;
  	err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
384
385
386
  	if (err < 0)
  		return err;
  	exists = err;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
387
388
389
390
391
392
393
  	if (exists && bind)
  		return 0;
  
  	switch (parm->t_action) {
  	case TCA_TUNNEL_KEY_ACT_RELEASE:
  		break;
  	case TCA_TUNNEL_KEY_ACT_SET:
80ef0f22c   Adi Nissim   net/sched: act_tu...
394
395
  		if (tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
  			__be32 key32;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
396

80ef0f22c   Adi Nissim   net/sched: act_tu...
397
398
399
400
  			key32 = nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
  			key_id = key32_to_tunnel_id(key32);
  			flags = TUNNEL_KEY;
  		}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
401

80ef0f22c   Adi Nissim   net/sched: act_tu...
402
  		flags |= TUNNEL_CSUM;
86087e170   Jiri Benc   net: sched: act_t...
403
404
405
  		if (tb[TCA_TUNNEL_KEY_NO_CSUM] &&
  		    nla_get_u8(tb[TCA_TUNNEL_KEY_NO_CSUM]))
  			flags &= ~TUNNEL_CSUM;
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
406
407
  		if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT])
  			dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
0ed5269f9   Simon Horman   net/sched: add tu...
408
409
410
411
412
413
414
415
  		if (tb[TCA_TUNNEL_KEY_ENC_OPTS]) {
  			opts_len = tunnel_key_get_opts_len(tb[TCA_TUNNEL_KEY_ENC_OPTS],
  							   extack);
  			if (opts_len < 0) {
  				ret = opts_len;
  				goto err_out;
  			}
  		}
07a557f47   Or Gerlitz   net/sched: tunnel...
416
417
418
419
420
421
  		tos = 0;
  		if (tb[TCA_TUNNEL_KEY_ENC_TOS])
  			tos = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_TOS]);
  		ttl = 0;
  		if (tb[TCA_TUNNEL_KEY_ENC_TTL])
  			ttl = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_TTL]);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
422
423
424
425
426
427
428
  		if (tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC] &&
  		    tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]) {
  			__be32 saddr;
  			__be32 daddr;
  
  			saddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC]);
  			daddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]);
07a557f47   Or Gerlitz   net/sched: tunnel...
429
  			metadata = __ip_tun_set_dst(saddr, daddr, tos, ttl,
86087e170   Jiri Benc   net: sched: act_t...
430
  						    dst_port, flags,
0ed5269f9   Simon Horman   net/sched: add tu...
431
  						    key_id, opts_len);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
432
433
434
435
436
437
438
  		} else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] &&
  			   tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) {
  			struct in6_addr saddr;
  			struct in6_addr daddr;
  
  			saddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC]);
  			daddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]);
07a557f47   Or Gerlitz   net/sched: tunnel...
439
  			metadata = __ipv6_tun_set_dst(&saddr, &daddr, tos, ttl, dst_port,
86087e170   Jiri Benc   net: sched: act_t...
440
  						      0, flags,
a7a12b5a0   Davide Caratti   net/sched: act_tu...
441
  						      key_id, opts_len);
a1165b591   Simon Horman   net/sched: act_tu...
442
  		} else {
9d7298cd1   Simon Horman   net/sched: act_tu...
443
  			NL_SET_ERR_MSG(extack, "Missing either ipv4 or ipv6 src and dst");
a1165b591   Simon Horman   net/sched: act_tu...
444
445
  			ret = -EINVAL;
  			goto err_out;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
446
447
448
  		}
  
  		if (!metadata) {
9d7298cd1   Simon Horman   net/sched: act_tu...
449
  			NL_SET_ERR_MSG(extack, "Cannot allocate tunnel metadata dst");
a1165b591   Simon Horman   net/sched: act_tu...
450
  			ret = -ENOMEM;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
451
452
  			goto err_out;
  		}
41411e2fd   wenxu   net/sched: act_tu...
453
454
455
456
457
  #ifdef CONFIG_DST_CACHE
  		ret = dst_cache_init(&metadata->u.tun_info.dst_cache, GFP_KERNEL);
  		if (ret)
  			goto release_tun_meta;
  #endif
0ed5269f9   Simon Horman   net/sched: add tu...
458
459
460
461
462
  		if (opts_len) {
  			ret = tunnel_key_opts_set(tb[TCA_TUNNEL_KEY_ENC_OPTS],
  						  &metadata->u.tun_info,
  						  opts_len, extack);
  			if (ret < 0)
4177c5d94   wenxu   net/sched: act_tu...
463
  				goto release_tun_meta;
0ed5269f9   Simon Horman   net/sched: add tu...
464
  		}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
465
466
467
  		metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
  		break;
  	default:
9d7298cd1   Simon Horman   net/sched: act_tu...
468
  		NL_SET_ERR_MSG(extack, "Unknown tunnel key action");
51d4740f8   Roman Mashak   net sched actions...
469
  		ret = -EINVAL;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
470
471
472
473
  		goto err_out;
  	}
  
  	if (!exists) {
e38226786   Vlad Buslov   net: sched: updat...
474
475
476
  		ret = tcf_idr_create_from_flags(tn, index, est, a,
  						&act_tunnel_key_ops, bind,
  						act_flags);
9d7298cd1   Simon Horman   net/sched: act_tu...
477
478
  		if (ret) {
  			NL_SET_ERR_MSG(extack, "Cannot create TC IDR");
4177c5d94   wenxu   net/sched: act_tu...
479
  			goto release_tun_meta;
9d7298cd1   Simon Horman   net/sched: act_tu...
480
  		}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
481
482
  
  		ret = ACT_P_CREATED;
4e8ddd7f1   Vlad Buslov   net: sched: don't...
483
  	} else if (!ovr) {
4e8ddd7f1   Vlad Buslov   net: sched: don't...
484
  		NL_SET_ERR_MSG(extack, "TC IDR already exists");
ee28bb56a   Davide Caratti   net/sched: fix me...
485
  		ret = -EEXIST;
4177c5d94   wenxu   net/sched: act_tu...
486
  		goto release_tun_meta;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
487
  	}
e5fdabacb   Davide Caratti   net/sched: act_tu...
488
489
490
491
492
493
  	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
  	if (err < 0) {
  		ret = err;
  		exists = true;
  		goto release_tun_meta;
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
494
  	t = to_tunnel_key(*a);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
495
496
  	params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
  	if (unlikely(!params_new)) {
9d7298cd1   Simon Horman   net/sched: act_tu...
497
  		NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters");
ee28bb56a   Davide Caratti   net/sched: fix me...
498
499
  		ret = -ENOMEM;
  		exists = true;
e5fdabacb   Davide Caratti   net/sched: act_tu...
500
  		goto put_chain;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
501
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
502
503
  	params_new->tcft_action = parm->t_action;
  	params_new->tcft_enc_metadata = metadata;
653cd284a   Vlad Buslov   net: sched: alway...
504
  	spin_lock_bh(&t->tcf_lock);
e5fdabacb   Davide Caratti   net/sched: act_tu...
505
  	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
445d37493   Paul E. McKenney   net/sched: Replac...
506
507
  	params_new = rcu_replace_pointer(t->params, params_new,
  					 lockdep_is_held(&t->tcf_lock));
653cd284a   Vlad Buslov   net: sched: alway...
508
  	spin_unlock_bh(&t->tcf_lock);
9174c3df1   Davide Caratti   net/sched: act_tu...
509
  	tunnel_key_release_params(params_new);
e5fdabacb   Davide Caratti   net/sched: act_tu...
510
511
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
512

d0f6dd8a9   Amir Vadai   net/sched: Introd...
513
  	return ret;
e5fdabacb   Davide Caratti   net/sched: act_tu...
514
515
516
  put_chain:
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
ee28bb56a   Davide Caratti   net/sched: fix me...
517
  release_tun_meta:
a3df633a3   Vlad Buslov   net: sched: act_t...
518
519
  	if (metadata)
  		dst_release(&metadata->dst);
ee28bb56a   Davide Caratti   net/sched: fix me...
520

d0f6dd8a9   Amir Vadai   net/sched: Introd...
521
522
  err_out:
  	if (exists)
65a206c01   Chris Mi   net/sched: Change...
523
  		tcf_idr_release(*a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
524
  	else
7be8ef2cd   Dmytro Linkin   net: sched: use t...
525
  		tcf_idr_cleanup(tn, index);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
526
527
  	return ret;
  }
9a63b255d   Cong Wang   net_sched: remove...
528
  static void tunnel_key_release(struct tc_action *a)
d0f6dd8a9   Amir Vadai   net/sched: Introd...
529
530
531
  {
  	struct tcf_tunnel_key *t = to_tunnel_key(a);
  	struct tcf_tunnel_key_params *params;
07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
532
  	params = rcu_dereference_protected(t->params, 1);
9174c3df1   Davide Caratti   net/sched: act_tu...
533
  	tunnel_key_release_params(params);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
534
  }
0ed5269f9   Simon Horman   net/sched: add tu...
535
536
537
538
539
540
  static int tunnel_key_geneve_opts_dump(struct sk_buff *skb,
  				       const struct ip_tunnel_info *info)
  {
  	int len = info->options_len;
  	u8 *src = (u8 *)(info + 1);
  	struct nlattr *start;
ae0be8de9   Michal Kubecek   netlink: make nla...
541
  	start = nla_nest_start_noflag(skb, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE);
0ed5269f9   Simon Horman   net/sched: add tu...
542
543
544
545
546
547
548
549
550
551
552
  	if (!start)
  		return -EMSGSIZE;
  
  	while (len > 0) {
  		struct geneve_opt *opt = (struct geneve_opt *)src;
  
  		if (nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS,
  				 opt->opt_class) ||
  		    nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE,
  			       opt->type) ||
  		    nla_put(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA,
a162c3511   Cong Wang   net_sched: proper...
553
554
  			    opt->length * 4, opt + 1)) {
  			nla_nest_cancel(skb, start);
0ed5269f9   Simon Horman   net/sched: add tu...
555
  			return -EMSGSIZE;
a162c3511   Cong Wang   net_sched: proper...
556
  		}
0ed5269f9   Simon Horman   net/sched: add tu...
557
558
559
560
561
562
563
564
  
  		len -= sizeof(struct geneve_opt) + opt->length * 4;
  		src += sizeof(struct geneve_opt) + opt->length * 4;
  	}
  
  	nla_nest_end(skb, start);
  	return 0;
  }
fca3f91cc   Xin Long   net: sched: add v...
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
  static int tunnel_key_vxlan_opts_dump(struct sk_buff *skb,
  				      const struct ip_tunnel_info *info)
  {
  	struct vxlan_metadata *md = (struct vxlan_metadata *)(info + 1);
  	struct nlattr *start;
  
  	start = nla_nest_start_noflag(skb, TCA_TUNNEL_KEY_ENC_OPTS_VXLAN);
  	if (!start)
  		return -EMSGSIZE;
  
  	if (nla_put_u32(skb, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, md->gbp)) {
  		nla_nest_cancel(skb, start);
  		return -EMSGSIZE;
  	}
  
  	nla_nest_end(skb, start);
  	return 0;
  }
e20d4ff2a   Xin Long   net: sched: add e...
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  static int tunnel_key_erspan_opts_dump(struct sk_buff *skb,
  				       const struct ip_tunnel_info *info)
  {
  	struct erspan_metadata *md = (struct erspan_metadata *)(info + 1);
  	struct nlattr *start;
  
  	start = nla_nest_start_noflag(skb, TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN);
  	if (!start)
  		return -EMSGSIZE;
  
  	if (nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER, md->version))
  		goto err;
  
  	if (md->version == 1 &&
  	    nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX, md->u.index))
  		goto err;
  
  	if (md->version == 2 &&
  	    (nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR,
  			md->u.md2.dir) ||
  	     nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID,
  			get_hwid(&md->u.md2))))
  		goto err;
  
  	nla_nest_end(skb, start);
  	return 0;
  err:
  	nla_nest_cancel(skb, start);
  	return -EMSGSIZE;
  }
0ed5269f9   Simon Horman   net/sched: add tu...
613
614
615
616
  static int tunnel_key_opts_dump(struct sk_buff *skb,
  				const struct ip_tunnel_info *info)
  {
  	struct nlattr *start;
a162c3511   Cong Wang   net_sched: proper...
617
  	int err = -EINVAL;
0ed5269f9   Simon Horman   net/sched: add tu...
618
619
620
  
  	if (!info->options_len)
  		return 0;
ae0be8de9   Michal Kubecek   netlink: make nla...
621
  	start = nla_nest_start_noflag(skb, TCA_TUNNEL_KEY_ENC_OPTS);
0ed5269f9   Simon Horman   net/sched: add tu...
622
623
624
625
626
627
  	if (!start)
  		return -EMSGSIZE;
  
  	if (info->key.tun_flags & TUNNEL_GENEVE_OPT) {
  		err = tunnel_key_geneve_opts_dump(skb, info);
  		if (err)
a162c3511   Cong Wang   net_sched: proper...
628
  			goto err_out;
fca3f91cc   Xin Long   net: sched: add v...
629
630
631
632
  	} else if (info->key.tun_flags & TUNNEL_VXLAN_OPT) {
  		err = tunnel_key_vxlan_opts_dump(skb, info);
  		if (err)
  			goto err_out;
e20d4ff2a   Xin Long   net: sched: add e...
633
634
635
636
  	} else if (info->key.tun_flags & TUNNEL_ERSPAN_OPT) {
  		err = tunnel_key_erspan_opts_dump(skb, info);
  		if (err)
  			goto err_out;
0ed5269f9   Simon Horman   net/sched: add tu...
637
  	} else {
a162c3511   Cong Wang   net_sched: proper...
638
639
640
  err_out:
  		nla_nest_cancel(skb, start);
  		return err;
0ed5269f9   Simon Horman   net/sched: add tu...
641
642
643
644
645
  	}
  
  	nla_nest_end(skb, start);
  	return 0;
  }
d0f6dd8a9   Amir Vadai   net/sched: Introd...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
  static int tunnel_key_dump_addresses(struct sk_buff *skb,
  				     const struct ip_tunnel_info *info)
  {
  	unsigned short family = ip_tunnel_info_af(info);
  
  	if (family == AF_INET) {
  		__be32 saddr = info->key.u.ipv4.src;
  		__be32 daddr = info->key.u.ipv4.dst;
  
  		if (!nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_SRC, saddr) &&
  		    !nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_DST, daddr))
  			return 0;
  	}
  
  	if (family == AF_INET6) {
  		const struct in6_addr *saddr6 = &info->key.u.ipv6.src;
  		const struct in6_addr *daddr6 = &info->key.u.ipv6.dst;
  
  		if (!nla_put_in6_addr(skb,
  				      TCA_TUNNEL_KEY_ENC_IPV6_SRC, saddr6) &&
  		    !nla_put_in6_addr(skb,
  				      TCA_TUNNEL_KEY_ENC_IPV6_DST, daddr6))
  			return 0;
  	}
  
  	return -EINVAL;
  }
  
  static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
  			   int bind, int ref)
  {
  	unsigned char *b = skb_tail_pointer(skb);
  	struct tcf_tunnel_key *t = to_tunnel_key(a);
  	struct tcf_tunnel_key_params *params;
  	struct tc_tunnel_key opt = {
  		.index    = t->tcf_index,
036bb4432   Vlad Buslov   net: sched: chang...
682
683
  		.refcnt   = refcount_read(&t->tcf_refcnt) - ref,
  		.bindcnt  = atomic_read(&t->tcf_bindcnt) - bind,
d0f6dd8a9   Amir Vadai   net/sched: Introd...
684
685
  	};
  	struct tcf_t tm;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
686

653cd284a   Vlad Buslov   net: sched: alway...
687
  	spin_lock_bh(&t->tcf_lock);
729e01260   Vlad Buslov   net: sched: act_t...
688
689
690
  	params = rcu_dereference_protected(t->params,
  					   lockdep_is_held(&t->tcf_lock));
  	opt.action   = t->tcf_action;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
691
  	opt.t_action = params->tcft_action;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
692
693
694
695
696
  
  	if (nla_put(skb, TCA_TUNNEL_KEY_PARMS, sizeof(opt), &opt))
  		goto nla_put_failure;
  
  	if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) {
0ed5269f9   Simon Horman   net/sched: add tu...
697
698
699
  		struct ip_tunnel_info *info =
  			&params->tcft_enc_metadata->u.tun_info;
  		struct ip_tunnel_key *key = &info->key;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
700
  		__be32 key_id = tunnel_id_to_key32(key->tun_id);
80ef0f22c   Adi Nissim   net/sched: act_tu...
701
702
  		if (((key->tun_flags & TUNNEL_KEY) &&
  		     nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id)) ||
d0f6dd8a9   Amir Vadai   net/sched: Introd...
703
  		    tunnel_key_dump_addresses(skb,
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
704
  					      &params->tcft_enc_metadata->u.tun_info) ||
1c25324ca   Adi Nissim   net/sched: act_tu...
705
706
707
  		    (key->tp_dst &&
  		      nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT,
  				   key->tp_dst)) ||
86087e170   Jiri Benc   net: sched: act_t...
708
  		    nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
0ed5269f9   Simon Horman   net/sched: add tu...
709
710
  			       !(key->tun_flags & TUNNEL_CSUM)) ||
  		    tunnel_key_opts_dump(skb, info))
d0f6dd8a9   Amir Vadai   net/sched: Introd...
711
  			goto nla_put_failure;
07a557f47   Or Gerlitz   net/sched: tunnel...
712
713
714
715
716
717
  
  		if (key->tos && nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_TOS, key->tos))
  			goto nla_put_failure;
  
  		if (key->ttl && nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_TTL, key->ttl))
  			goto nla_put_failure;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
718
719
720
721
722
723
  	}
  
  	tcf_tm_dump(&tm, &t->tcf_tm);
  	if (nla_put_64bit(skb, TCA_TUNNEL_KEY_TM, sizeof(tm),
  			  &tm, TCA_TUNNEL_KEY_PAD))
  		goto nla_put_failure;
653cd284a   Vlad Buslov   net: sched: alway...
724
  	spin_unlock_bh(&t->tcf_lock);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
725

07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
726
  	return skb->len;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
727
728
  
  nla_put_failure:
653cd284a   Vlad Buslov   net: sched: alway...
729
  	spin_unlock_bh(&t->tcf_lock);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
730
  	nlmsg_trim(skb, b);
07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
731
  	return -1;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
732
733
734
735
  }
  
  static int tunnel_key_walker(struct net *net, struct sk_buff *skb,
  			     struct netlink_callback *cb, int type,
417801055   Alexander Aring   net: sched: act: ...
736
737
  			     const struct tc_action_ops *ops,
  			     struct netlink_ext_ack *extack)
d0f6dd8a9   Amir Vadai   net/sched: Introd...
738
739
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
b36201455   Alexander Aring   net: sched: act: ...
740
  	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
741
  }
f061b48c1   Cong Wang   Revert "net: sche...
742
  static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index)
d0f6dd8a9   Amir Vadai   net/sched: Introd...
743
744
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
65a206c01   Chris Mi   net/sched: Change...
745
  	return tcf_idr_search(tn, a, index);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
746
747
748
749
  }
  
  static struct tc_action_ops act_tunnel_key_ops = {
  	.kind		=	"tunnel_key",
eddd2cf19   Eli Cohen   net: Change TCA_A...
750
  	.id		=	TCA_ID_TUNNEL_KEY,
d0f6dd8a9   Amir Vadai   net/sched: Introd...
751
752
753
754
755
756
757
758
759
760
761
762
763
  	.owner		=	THIS_MODULE,
  	.act		=	tunnel_key_act,
  	.dump		=	tunnel_key_dump,
  	.init		=	tunnel_key_init,
  	.cleanup	=	tunnel_key_release,
  	.walk		=	tunnel_key_walker,
  	.lookup		=	tunnel_key_search,
  	.size		=	sizeof(struct tcf_tunnel_key),
  };
  
  static __net_init int tunnel_key_init_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
981471bd3   Cong Wang   net_sched: fix a ...
764
  	return tc_action_net_init(net, tn, &act_tunnel_key_ops);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
765
  }
039af9c66   Cong Wang   net_sched: switch...
766
  static void __net_exit tunnel_key_exit_net(struct list_head *net_list)
d0f6dd8a9   Amir Vadai   net/sched: Introd...
767
  {
039af9c66   Cong Wang   net_sched: switch...
768
  	tc_action_net_exit(net_list, tunnel_key_net_id);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
769
770
771
772
  }
  
  static struct pernet_operations tunnel_key_net_ops = {
  	.init = tunnel_key_init_net,
039af9c66   Cong Wang   net_sched: switch...
773
  	.exit_batch = tunnel_key_exit_net,
d0f6dd8a9   Amir Vadai   net/sched: Introd...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
  	.id   = &tunnel_key_net_id,
  	.size = sizeof(struct tc_action_net),
  };
  
  static int __init tunnel_key_init_module(void)
  {
  	return tcf_register_action(&act_tunnel_key_ops, &tunnel_key_net_ops);
  }
  
  static void __exit tunnel_key_cleanup_module(void)
  {
  	tcf_unregister_action(&act_tunnel_key_ops, &tunnel_key_net_ops);
  }
  
  module_init(tunnel_key_init_module);
  module_exit(tunnel_key_cleanup_module);
  
  MODULE_AUTHOR("Amir Vadai <amir@vadai.me>");
  MODULE_DESCRIPTION("ip tunnel manipulation actions");
  MODULE_LICENSE("GPL v2");