xref: /openbmc/linux/net/core/lwtunnel.c (revision 3a0af8fd)
1499a2425SRoopa Prabhu /*
2499a2425SRoopa Prabhu  * lwtunnel	Infrastructure for light weight tunnels like mpls
3499a2425SRoopa Prabhu  *
4499a2425SRoopa Prabhu  * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
5499a2425SRoopa Prabhu  *
6499a2425SRoopa Prabhu  *		This program is free software; you can redistribute it and/or
7499a2425SRoopa Prabhu  *		modify it under the terms of the GNU General Public License
8499a2425SRoopa Prabhu  *		as published by the Free Software Foundation; either version
9499a2425SRoopa Prabhu  *		2 of the License, or (at your option) any later version.
10499a2425SRoopa Prabhu  *
11499a2425SRoopa Prabhu  */
12499a2425SRoopa Prabhu 
13499a2425SRoopa Prabhu #include <linux/capability.h>
14499a2425SRoopa Prabhu #include <linux/module.h>
15499a2425SRoopa Prabhu #include <linux/types.h>
16499a2425SRoopa Prabhu #include <linux/kernel.h>
17499a2425SRoopa Prabhu #include <linux/slab.h>
18499a2425SRoopa Prabhu #include <linux/uaccess.h>
19499a2425SRoopa Prabhu #include <linux/skbuff.h>
20499a2425SRoopa Prabhu #include <linux/netdevice.h>
21499a2425SRoopa Prabhu #include <linux/lwtunnel.h>
22499a2425SRoopa Prabhu #include <linux/in.h>
23499a2425SRoopa Prabhu #include <linux/init.h>
24499a2425SRoopa Prabhu #include <linux/err.h>
25499a2425SRoopa Prabhu 
26499a2425SRoopa Prabhu #include <net/lwtunnel.h>
27499a2425SRoopa Prabhu #include <net/rtnetlink.h>
28ffce4196SRoopa Prabhu #include <net/ip6_fib.h>
29499a2425SRoopa Prabhu 
30745041e2SRobert Shearman #ifdef CONFIG_MODULES
31745041e2SRobert Shearman 
32745041e2SRobert Shearman static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
33745041e2SRobert Shearman {
34745041e2SRobert Shearman 	/* Only lwt encaps implemented without using an interface for
35745041e2SRobert Shearman 	 * the encap need to return a string here.
36745041e2SRobert Shearman 	 */
37745041e2SRobert Shearman 	switch (encap_type) {
38745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_MPLS:
39745041e2SRobert Shearman 		return "MPLS";
40745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_ILA:
41745041e2SRobert Shearman 		return "ILA";
426c8702c6SDavid Lebrun 	case LWTUNNEL_ENCAP_SEG6:
436c8702c6SDavid Lebrun 		return "SEG6";
443a0af8fdSThomas Graf 	case LWTUNNEL_ENCAP_BPF:
453a0af8fdSThomas Graf 		return "BPF";
46745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP6:
47745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP:
48745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_NONE:
49745041e2SRobert Shearman 	case __LWTUNNEL_ENCAP_MAX:
50745041e2SRobert Shearman 		/* should not have got here */
51745041e2SRobert Shearman 		WARN_ON(1);
52745041e2SRobert Shearman 		break;
53745041e2SRobert Shearman 	}
54745041e2SRobert Shearman 	return NULL;
55745041e2SRobert Shearman }
56745041e2SRobert Shearman 
57745041e2SRobert Shearman #endif /* CONFIG_MODULES */
58745041e2SRobert Shearman 
59499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
60499a2425SRoopa Prabhu {
61499a2425SRoopa Prabhu 	struct lwtunnel_state *lws;
62499a2425SRoopa Prabhu 
63499a2425SRoopa Prabhu 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
64499a2425SRoopa Prabhu 
65499a2425SRoopa Prabhu 	return lws;
66499a2425SRoopa Prabhu }
67499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_state_alloc);
68499a2425SRoopa Prabhu 
6992a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu *
70499a2425SRoopa Prabhu 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
71499a2425SRoopa Prabhu 
72499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
73499a2425SRoopa Prabhu 			   unsigned int num)
74499a2425SRoopa Prabhu {
75499a2425SRoopa Prabhu 	if (num > LWTUNNEL_ENCAP_MAX)
76499a2425SRoopa Prabhu 		return -ERANGE;
77499a2425SRoopa Prabhu 
78499a2425SRoopa Prabhu 	return !cmpxchg((const struct lwtunnel_encap_ops **)
79499a2425SRoopa Prabhu 			&lwtun_encaps[num],
80499a2425SRoopa Prabhu 			NULL, ops) ? 0 : -1;
81499a2425SRoopa Prabhu }
82499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_add_ops);
83499a2425SRoopa Prabhu 
84499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
85499a2425SRoopa Prabhu 			   unsigned int encap_type)
86499a2425SRoopa Prabhu {
87499a2425SRoopa Prabhu 	int ret;
88499a2425SRoopa Prabhu 
89499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
90499a2425SRoopa Prabhu 	    encap_type > LWTUNNEL_ENCAP_MAX)
91499a2425SRoopa Prabhu 		return -ERANGE;
92499a2425SRoopa Prabhu 
93499a2425SRoopa Prabhu 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
94499a2425SRoopa Prabhu 		       &lwtun_encaps[encap_type],
95499a2425SRoopa Prabhu 		       ops, NULL) == ops) ? 0 : -1;
96499a2425SRoopa Prabhu 
97499a2425SRoopa Prabhu 	synchronize_net();
98499a2425SRoopa Prabhu 
99499a2425SRoopa Prabhu 	return ret;
100499a2425SRoopa Prabhu }
101499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_del_ops);
102499a2425SRoopa Prabhu 
103499a2425SRoopa Prabhu int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
104127eb7cdSTom Herbert 			 struct nlattr *encap, unsigned int family,
105127eb7cdSTom Herbert 			 const void *cfg, struct lwtunnel_state **lws)
106499a2425SRoopa Prabhu {
107499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
108499a2425SRoopa Prabhu 	int ret = -EINVAL;
109499a2425SRoopa Prabhu 
110499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
111499a2425SRoopa Prabhu 	    encap_type > LWTUNNEL_ENCAP_MAX)
112499a2425SRoopa Prabhu 		return ret;
113499a2425SRoopa Prabhu 
114499a2425SRoopa Prabhu 	ret = -EOPNOTSUPP;
115499a2425SRoopa Prabhu 	rcu_read_lock();
116499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[encap_type]);
117745041e2SRobert Shearman #ifdef CONFIG_MODULES
118745041e2SRobert Shearman 	if (!ops) {
119745041e2SRobert Shearman 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
120745041e2SRobert Shearman 
121745041e2SRobert Shearman 		if (encap_type_str) {
122745041e2SRobert Shearman 			rcu_read_unlock();
123745041e2SRobert Shearman 			request_module("rtnl-lwt-%s", encap_type_str);
124745041e2SRobert Shearman 			rcu_read_lock();
125745041e2SRobert Shearman 			ops = rcu_dereference(lwtun_encaps[encap_type]);
126745041e2SRobert Shearman 		}
127745041e2SRobert Shearman 	}
128745041e2SRobert Shearman #endif
129499a2425SRoopa Prabhu 	if (likely(ops && ops->build_state))
130127eb7cdSTom Herbert 		ret = ops->build_state(dev, encap, family, cfg, lws);
131499a2425SRoopa Prabhu 	rcu_read_unlock();
132499a2425SRoopa Prabhu 
133499a2425SRoopa Prabhu 	return ret;
134499a2425SRoopa Prabhu }
135499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_build_state);
136499a2425SRoopa Prabhu 
1371104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws)
1381104d9baSTom Herbert {
1391104d9baSTom Herbert 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
1401104d9baSTom Herbert 
1411104d9baSTom Herbert 	if (ops->destroy_state) {
1421104d9baSTom Herbert 		ops->destroy_state(lws);
1431104d9baSTom Herbert 		kfree_rcu(lws, rcu);
1441104d9baSTom Herbert 	} else {
1451104d9baSTom Herbert 		kfree(lws);
1461104d9baSTom Herbert 	}
1471104d9baSTom Herbert }
1481104d9baSTom Herbert EXPORT_SYMBOL(lwtstate_free);
1491104d9baSTom Herbert 
150499a2425SRoopa Prabhu int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
151499a2425SRoopa Prabhu {
152499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
153499a2425SRoopa Prabhu 	struct nlattr *nest;
154499a2425SRoopa Prabhu 	int ret = -EINVAL;
155499a2425SRoopa Prabhu 
156499a2425SRoopa Prabhu 	if (!lwtstate)
157499a2425SRoopa Prabhu 		return 0;
158499a2425SRoopa Prabhu 
159499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
160499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
161499a2425SRoopa Prabhu 		return 0;
162499a2425SRoopa Prabhu 
163499a2425SRoopa Prabhu 	ret = -EOPNOTSUPP;
164499a2425SRoopa Prabhu 	nest = nla_nest_start(skb, RTA_ENCAP);
165499a2425SRoopa Prabhu 	rcu_read_lock();
166499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
167499a2425SRoopa Prabhu 	if (likely(ops && ops->fill_encap))
168499a2425SRoopa Prabhu 		ret = ops->fill_encap(skb, lwtstate);
169499a2425SRoopa Prabhu 	rcu_read_unlock();
170499a2425SRoopa Prabhu 
171499a2425SRoopa Prabhu 	if (ret)
172499a2425SRoopa Prabhu 		goto nla_put_failure;
173499a2425SRoopa Prabhu 	nla_nest_end(skb, nest);
174499a2425SRoopa Prabhu 	ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
175499a2425SRoopa Prabhu 	if (ret)
176499a2425SRoopa Prabhu 		goto nla_put_failure;
177499a2425SRoopa Prabhu 
178499a2425SRoopa Prabhu 	return 0;
179499a2425SRoopa Prabhu 
180499a2425SRoopa Prabhu nla_put_failure:
181499a2425SRoopa Prabhu 	nla_nest_cancel(skb, nest);
182499a2425SRoopa Prabhu 
183499a2425SRoopa Prabhu 	return (ret == -EOPNOTSUPP ? 0 : ret);
184499a2425SRoopa Prabhu }
185499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_fill_encap);
186499a2425SRoopa Prabhu 
187499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
188499a2425SRoopa Prabhu {
189499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
190499a2425SRoopa Prabhu 	int ret = 0;
191499a2425SRoopa Prabhu 
192499a2425SRoopa Prabhu 	if (!lwtstate)
193499a2425SRoopa Prabhu 		return 0;
194499a2425SRoopa Prabhu 
195499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
196499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
197499a2425SRoopa Prabhu 		return 0;
198499a2425SRoopa Prabhu 
199499a2425SRoopa Prabhu 	rcu_read_lock();
200499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
201499a2425SRoopa Prabhu 	if (likely(ops && ops->get_encap_size))
202499a2425SRoopa Prabhu 		ret = nla_total_size(ops->get_encap_size(lwtstate));
203499a2425SRoopa Prabhu 	rcu_read_unlock();
204499a2425SRoopa Prabhu 
205499a2425SRoopa Prabhu 	return ret;
206499a2425SRoopa Prabhu }
207499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_get_encap_size);
208499a2425SRoopa Prabhu 
209499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
210499a2425SRoopa Prabhu {
211499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
212499a2425SRoopa Prabhu 	int ret = 0;
213499a2425SRoopa Prabhu 
214499a2425SRoopa Prabhu 	if (!a && !b)
215499a2425SRoopa Prabhu 		return 0;
216499a2425SRoopa Prabhu 
217499a2425SRoopa Prabhu 	if (!a || !b)
218499a2425SRoopa Prabhu 		return 1;
219499a2425SRoopa Prabhu 
220499a2425SRoopa Prabhu 	if (a->type != b->type)
221499a2425SRoopa Prabhu 		return 1;
222499a2425SRoopa Prabhu 
223499a2425SRoopa Prabhu 	if (a->type == LWTUNNEL_ENCAP_NONE ||
224499a2425SRoopa Prabhu 	    a->type > LWTUNNEL_ENCAP_MAX)
225499a2425SRoopa Prabhu 		return 0;
226499a2425SRoopa Prabhu 
227499a2425SRoopa Prabhu 	rcu_read_lock();
228499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[a->type]);
229499a2425SRoopa Prabhu 	if (likely(ops && ops->cmp_encap))
230499a2425SRoopa Prabhu 		ret = ops->cmp_encap(a, b);
231499a2425SRoopa Prabhu 	rcu_read_unlock();
232499a2425SRoopa Prabhu 
233499a2425SRoopa Prabhu 	return ret;
234499a2425SRoopa Prabhu }
235499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_cmp_encap);
236ffce4196SRoopa Prabhu 
237ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
238ffce4196SRoopa Prabhu {
23961adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
240ffce4196SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
24161adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
242ffce4196SRoopa Prabhu 	int ret = -EINVAL;
243ffce4196SRoopa Prabhu 
24461adedf3SJiri Benc 	if (!dst)
245ffce4196SRoopa Prabhu 		goto drop;
24661adedf3SJiri Benc 	lwtstate = dst->lwtstate;
247ffce4196SRoopa Prabhu 
248ffce4196SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
249ffce4196SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
250ffce4196SRoopa Prabhu 		return 0;
251ffce4196SRoopa Prabhu 
252ffce4196SRoopa Prabhu 	ret = -EOPNOTSUPP;
253ffce4196SRoopa Prabhu 	rcu_read_lock();
254ffce4196SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
255ffce4196SRoopa Prabhu 	if (likely(ops && ops->output))
256ede2059dSEric W. Biederman 		ret = ops->output(net, sk, skb);
257ffce4196SRoopa Prabhu 	rcu_read_unlock();
258ffce4196SRoopa Prabhu 
259ffce4196SRoopa Prabhu 	if (ret == -EOPNOTSUPP)
260ffce4196SRoopa Prabhu 		goto drop;
261ffce4196SRoopa Prabhu 
262ffce4196SRoopa Prabhu 	return ret;
263ffce4196SRoopa Prabhu 
264ffce4196SRoopa Prabhu drop:
265e11f40b9SDan Carpenter 	kfree_skb(skb);
266ffce4196SRoopa Prabhu 
267ffce4196SRoopa Prabhu 	return ret;
268ffce4196SRoopa Prabhu }
269ffce4196SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_output);
27025368623STom Herbert 
27114972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb)
27214972cbdSRoopa Prabhu {
27314972cbdSRoopa Prabhu 	struct dst_entry *dst = skb_dst(skb);
27414972cbdSRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
27514972cbdSRoopa Prabhu 	struct lwtunnel_state *lwtstate;
27614972cbdSRoopa Prabhu 	int ret = -EINVAL;
27714972cbdSRoopa Prabhu 
27814972cbdSRoopa Prabhu 	if (!dst)
27914972cbdSRoopa Prabhu 		goto drop;
28014972cbdSRoopa Prabhu 
28114972cbdSRoopa Prabhu 	lwtstate = dst->lwtstate;
28214972cbdSRoopa Prabhu 
28314972cbdSRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
28414972cbdSRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
28514972cbdSRoopa Prabhu 		return 0;
28614972cbdSRoopa Prabhu 
28714972cbdSRoopa Prabhu 	ret = -EOPNOTSUPP;
28814972cbdSRoopa Prabhu 	rcu_read_lock();
28914972cbdSRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
29014972cbdSRoopa Prabhu 	if (likely(ops && ops->xmit))
29114972cbdSRoopa Prabhu 		ret = ops->xmit(skb);
29214972cbdSRoopa Prabhu 	rcu_read_unlock();
29314972cbdSRoopa Prabhu 
29414972cbdSRoopa Prabhu 	if (ret == -EOPNOTSUPP)
29514972cbdSRoopa Prabhu 		goto drop;
29614972cbdSRoopa Prabhu 
29714972cbdSRoopa Prabhu 	return ret;
29814972cbdSRoopa Prabhu 
29914972cbdSRoopa Prabhu drop:
30014972cbdSRoopa Prabhu 	kfree_skb(skb);
30114972cbdSRoopa Prabhu 
30214972cbdSRoopa Prabhu 	return ret;
30314972cbdSRoopa Prabhu }
30414972cbdSRoopa Prabhu EXPORT_SYMBOL(lwtunnel_xmit);
30514972cbdSRoopa Prabhu 
30661adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb)
30725368623STom Herbert {
30861adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
30925368623STom Herbert 	const struct lwtunnel_encap_ops *ops;
31061adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
31125368623STom Herbert 	int ret = -EINVAL;
31225368623STom Herbert 
31361adedf3SJiri Benc 	if (!dst)
31425368623STom Herbert 		goto drop;
31561adedf3SJiri Benc 	lwtstate = dst->lwtstate;
31625368623STom Herbert 
31725368623STom Herbert 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
31825368623STom Herbert 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
31925368623STom Herbert 		return 0;
32025368623STom Herbert 
32125368623STom Herbert 	ret = -EOPNOTSUPP;
32225368623STom Herbert 	rcu_read_lock();
32325368623STom Herbert 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
32425368623STom Herbert 	if (likely(ops && ops->input))
32525368623STom Herbert 		ret = ops->input(skb);
32625368623STom Herbert 	rcu_read_unlock();
32725368623STom Herbert 
32825368623STom Herbert 	if (ret == -EOPNOTSUPP)
32925368623STom Herbert 		goto drop;
33025368623STom Herbert 
33125368623STom Herbert 	return ret;
33225368623STom Herbert 
33325368623STom Herbert drop:
33425368623STom Herbert 	kfree_skb(skb);
33525368623STom Herbert 
33625368623STom Herbert 	return ret;
33725368623STom Herbert }
33825368623STom Herbert EXPORT_SYMBOL(lwtunnel_input);
339