Blame view

net/sched/act_tunnel_key.c 9.1 KB
d0f6dd8a9   Amir Vadai   net/sched: Introd...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * Copyright (c) 2016, Amir Vadai <amir@vadai.me>
   * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
  #include <net/netlink.h>
  #include <net/pkt_sched.h>
  #include <net/dst.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
31
32
33
34
35
36
37
  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;
  
  	rcu_read_lock();
  
  	params = rcu_dereference(t->params);
  
  	tcf_lastuse_update(&t->tcf_tm);
  	bstats_cpu_update(this_cpu_ptr(t->common.cpu_bstats), skb);
de9f2452b   Davide Caratti   net/sched: act_tu...
38
  	action = READ_ONCE(t->tcf_action);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  
  	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;
  	}
  
  	rcu_read_unlock();
  
  	return action;
  }
  
  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...
67
  	[TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16},
86087e170   Jiri Benc   net: sched: act_t...
68
  	[TCA_TUNNEL_KEY_NO_CSUM]      = { .type = NLA_U8 },
d0f6dd8a9   Amir Vadai   net/sched: Introd...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  };
  
  static int tunnel_key_init(struct net *net, struct nlattr *nla,
  			   struct nlattr *est, struct tc_action **a,
  			   int ovr, int bind)
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
  	struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1];
  	struct tcf_tunnel_key_params *params_old;
  	struct tcf_tunnel_key_params *params_new;
  	struct metadata_dst *metadata = NULL;
  	struct tc_tunnel_key *parm;
  	struct tcf_tunnel_key *t;
  	bool exists = false;
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
83
  	__be16 dst_port = 0;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
84
  	__be64 key_id;
86087e170   Jiri Benc   net: sched: act_t...
85
  	__be16 flags;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
86
87
88
89
90
  	int ret = 0;
  	int err;
  
  	if (!nla)
  		return -EINVAL;
fceb6435e   Johannes Berg   netlink: pass ext...
91
92
  	err = nla_parse_nested(tb, TCA_TUNNEL_KEY_MAX, nla, tunnel_key_policy,
  			       NULL);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
93
94
95
96
97
98
99
  	if (err < 0)
  		return err;
  
  	if (!tb[TCA_TUNNEL_KEY_PARMS])
  		return -EINVAL;
  
  	parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]);
65a206c01   Chris Mi   net/sched: Change...
100
  	exists = tcf_idr_check(tn, parm->index, a, bind);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
101
102
103
104
105
106
107
108
109
110
111
112
113
  	if (exists && bind)
  		return 0;
  
  	switch (parm->t_action) {
  	case TCA_TUNNEL_KEY_ACT_RELEASE:
  		break;
  	case TCA_TUNNEL_KEY_ACT_SET:
  		if (!tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
  			ret = -EINVAL;
  			goto err_out;
  		}
  
  		key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]));
86087e170   Jiri Benc   net: sched: act_t...
114
115
116
117
  		flags = TUNNEL_KEY | TUNNEL_CSUM;
  		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...
118
119
  		if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT])
  			dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
120
121
122
123
124
125
126
127
128
  		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]);
  
  			metadata = __ip_tun_set_dst(saddr, daddr, 0, 0,
86087e170   Jiri Benc   net: sched: act_t...
129
  						    dst_port, flags,
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
130
  						    key_id, 0);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
131
132
133
134
135
136
137
  		} 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]);
dc594ecd4   Or Gerlitz   net/sched: act_tu...
138
  			metadata = __ipv6_tun_set_dst(&saddr, &daddr, 0, 0, dst_port,
86087e170   Jiri Benc   net: sched: act_t...
139
  						      0, flags,
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
140
  						      key_id, 0);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
141
142
143
144
145
146
147
148
149
150
  		}
  
  		if (!metadata) {
  			ret = -EINVAL;
  			goto err_out;
  		}
  
  		metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
  		break;
  	default:
28b488f7c   Roman Mashak   net sched actions...
151
  		ret = -EINVAL;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
152
153
154
155
  		goto err_out;
  	}
  
  	if (!exists) {
65a206c01   Chris Mi   net/sched: Change...
156
157
  		ret = tcf_idr_create(tn, parm->index, est, a,
  				     &act_tunnel_key_ops, bind, true);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
158
159
160
161
162
  		if (ret)
  			return ret;
  
  		ret = ACT_P_CREATED;
  	} else {
65a206c01   Chris Mi   net/sched: Change...
163
  		tcf_idr_release(*a, bind);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
164
165
166
167
168
169
170
171
172
173
  		if (!ovr)
  			return -EEXIST;
  	}
  
  	t = to_tunnel_key(*a);
  
  	ASSERT_RTNL();
  	params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
  	if (unlikely(!params_new)) {
  		if (ret == ACT_P_CREATED)
65a206c01   Chris Mi   net/sched: Change...
174
  			tcf_idr_release(*a, bind);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
175
176
177
178
  		return -ENOMEM;
  	}
  
  	params_old = rtnl_dereference(t->params);
de9f2452b   Davide Caratti   net/sched: act_tu...
179
  	t->tcf_action = parm->action;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
180
181
182
183
184
185
186
187
188
  	params_new->tcft_action = parm->t_action;
  	params_new->tcft_enc_metadata = metadata;
  
  	rcu_assign_pointer(t->params, params_new);
  
  	if (params_old)
  		kfree_rcu(params_old, rcu);
  
  	if (ret == ACT_P_CREATED)
65a206c01   Chris Mi   net/sched: Change...
189
  		tcf_idr_insert(tn, *a);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
190
191
192
193
194
  
  	return ret;
  
  err_out:
  	if (exists)
65a206c01   Chris Mi   net/sched: Change...
195
  		tcf_idr_release(*a, bind);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
196
197
198
199
200
201
202
  	return ret;
  }
  
  static void tunnel_key_release(struct tc_action *a, int bind)
  {
  	struct tcf_tunnel_key *t = to_tunnel_key(a);
  	struct tcf_tunnel_key_params *params;
07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
203
  	params = rcu_dereference_protected(t->params, 1);
a19024a3f   Davide Caratti   net/sched: fix NU...
204
205
206
  	if (params) {
  		if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
  			dst_release(&params->tcft_enc_metadata->dst);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
207

a19024a3f   Davide Caratti   net/sched: fix NU...
208
209
  		kfree_rcu(params, rcu);
  	}
d0f6dd8a9   Amir Vadai   net/sched: Introd...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  }
  
  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,
  		.refcnt   = t->tcf_refcnt - ref,
  		.bindcnt  = t->tcf_bindcnt - bind,
de9f2452b   Davide Caratti   net/sched: act_tu...
250
  		.action   = t->tcf_action,
d0f6dd8a9   Amir Vadai   net/sched: Introd...
251
252
  	};
  	struct tcf_t tm;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
253

07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
254
  	params = rtnl_dereference(t->params);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
255
256
  
  	opt.t_action = params->tcft_action;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
257
258
259
260
261
262
263
264
265
266
267
  
  	if (nla_put(skb, TCA_TUNNEL_KEY_PARMS, sizeof(opt), &opt))
  		goto nla_put_failure;
  
  	if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) {
  		struct ip_tunnel_key *key =
  			&params->tcft_enc_metadata->u.tun_info.key;
  		__be32 key_id = tunnel_id_to_key32(key->tun_id);
  
  		if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) ||
  		    tunnel_key_dump_addresses(skb,
75bfbca01   Hadar Hen Zion   net/sched: act_tu...
268
  					      &params->tcft_enc_metadata->u.tun_info) ||
86087e170   Jiri Benc   net: sched: act_t...
269
270
271
  		    nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) ||
  		    nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
  			       !(key->tun_flags & TUNNEL_CSUM)))
d0f6dd8a9   Amir Vadai   net/sched: Introd...
272
273
274
275
276
277
278
  			goto nla_put_failure;
  	}
  
  	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;
07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
279
  	return skb->len;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
280
281
282
  
  nla_put_failure:
  	nlmsg_trim(skb, b);
07c0f09e2   Hadar Hen Zion   net/sched: act_tu...
283
  	return -1;
d0f6dd8a9   Amir Vadai   net/sched: Introd...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  }
  
  static int tunnel_key_walker(struct net *net, struct sk_buff *skb,
  			     struct netlink_callback *cb, int type,
  			     const struct tc_action_ops *ops)
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
  
  	return tcf_generic_walker(tn, skb, cb, type, ops);
  }
  
  static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index)
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
65a206c01   Chris Mi   net/sched: Change...
298
  	return tcf_idr_search(tn, a, index);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  }
  
  static struct tc_action_ops act_tunnel_key_ops = {
  	.kind		=	"tunnel_key",
  	.type		=	TCA_ACT_TUNNEL_KEY,
  	.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);
c7e460ce5   Cong Wang   Revert "net_sched...
317
  	return tc_action_net_init(tn, &act_tunnel_key_ops);
d0f6dd8a9   Amir Vadai   net/sched: Introd...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  }
  
  static void __net_exit tunnel_key_exit_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
  
  	tc_action_net_exit(tn);
  }
  
  static struct pernet_operations tunnel_key_net_ops = {
  	.init = tunnel_key_init_net,
  	.exit = tunnel_key_exit_net,
  	.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");