xref: /openbmc/linux/net/ipv6/seg6_local.c (revision d1df6fd8a1d22d37cffa0075ab8ad423ce656777)
1*d1df6fd8SDavid Lebrun /*
2*d1df6fd8SDavid Lebrun  *  SR-IPv6 implementation
3*d1df6fd8SDavid Lebrun  *
4*d1df6fd8SDavid Lebrun  *  Author:
5*d1df6fd8SDavid Lebrun  *  David Lebrun <david.lebrun@uclouvain.be>
6*d1df6fd8SDavid Lebrun  *
7*d1df6fd8SDavid Lebrun  *
8*d1df6fd8SDavid Lebrun  *  This program is free software; you can redistribute it and/or
9*d1df6fd8SDavid Lebrun  *        modify it under the terms of the GNU General Public License
10*d1df6fd8SDavid Lebrun  *        as published by the Free Software Foundation; either version
11*d1df6fd8SDavid Lebrun  *        2 of the License, or (at your option) any later version.
12*d1df6fd8SDavid Lebrun  */
13*d1df6fd8SDavid Lebrun 
14*d1df6fd8SDavid Lebrun #include <linux/types.h>
15*d1df6fd8SDavid Lebrun #include <linux/skbuff.h>
16*d1df6fd8SDavid Lebrun #include <linux/net.h>
17*d1df6fd8SDavid Lebrun #include <linux/module.h>
18*d1df6fd8SDavid Lebrun #include <net/ip.h>
19*d1df6fd8SDavid Lebrun #include <net/lwtunnel.h>
20*d1df6fd8SDavid Lebrun #include <net/netevent.h>
21*d1df6fd8SDavid Lebrun #include <net/netns/generic.h>
22*d1df6fd8SDavid Lebrun #include <net/ip6_fib.h>
23*d1df6fd8SDavid Lebrun #include <net/route.h>
24*d1df6fd8SDavid Lebrun #include <net/seg6.h>
25*d1df6fd8SDavid Lebrun #include <linux/seg6.h>
26*d1df6fd8SDavid Lebrun #include <linux/seg6_local.h>
27*d1df6fd8SDavid Lebrun #include <net/addrconf.h>
28*d1df6fd8SDavid Lebrun #include <net/ip6_route.h>
29*d1df6fd8SDavid Lebrun #include <net/dst_cache.h>
30*d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
31*d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h>
32*d1df6fd8SDavid Lebrun #endif
33*d1df6fd8SDavid Lebrun 
34*d1df6fd8SDavid Lebrun struct seg6_local_lwt;
35*d1df6fd8SDavid Lebrun 
36*d1df6fd8SDavid Lebrun struct seg6_action_desc {
37*d1df6fd8SDavid Lebrun 	int action;
38*d1df6fd8SDavid Lebrun 	unsigned long attrs;
39*d1df6fd8SDavid Lebrun 	int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
40*d1df6fd8SDavid Lebrun 	int static_headroom;
41*d1df6fd8SDavid Lebrun };
42*d1df6fd8SDavid Lebrun 
43*d1df6fd8SDavid Lebrun struct seg6_local_lwt {
44*d1df6fd8SDavid Lebrun 	int action;
45*d1df6fd8SDavid Lebrun 	struct ipv6_sr_hdr *srh;
46*d1df6fd8SDavid Lebrun 	int table;
47*d1df6fd8SDavid Lebrun 	struct in_addr nh4;
48*d1df6fd8SDavid Lebrun 	struct in6_addr nh6;
49*d1df6fd8SDavid Lebrun 	int iif;
50*d1df6fd8SDavid Lebrun 	int oif;
51*d1df6fd8SDavid Lebrun 
52*d1df6fd8SDavid Lebrun 	int headroom;
53*d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
54*d1df6fd8SDavid Lebrun };
55*d1df6fd8SDavid Lebrun 
56*d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
57*d1df6fd8SDavid Lebrun {
58*d1df6fd8SDavid Lebrun 	return (struct seg6_local_lwt *)lwt->data;
59*d1df6fd8SDavid Lebrun }
60*d1df6fd8SDavid Lebrun 
61*d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = {
62*d1df6fd8SDavid Lebrun 	{
63*d1df6fd8SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END,
64*d1df6fd8SDavid Lebrun 		.attrs		= 0,
65*d1df6fd8SDavid Lebrun 	},
66*d1df6fd8SDavid Lebrun };
67*d1df6fd8SDavid Lebrun 
68*d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action)
69*d1df6fd8SDavid Lebrun {
70*d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
71*d1df6fd8SDavid Lebrun 	int i, count;
72*d1df6fd8SDavid Lebrun 
73*d1df6fd8SDavid Lebrun 	count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc);
74*d1df6fd8SDavid Lebrun 	for (i = 0; i < count; i++) {
75*d1df6fd8SDavid Lebrun 		desc = &seg6_action_table[i];
76*d1df6fd8SDavid Lebrun 		if (desc->action == action)
77*d1df6fd8SDavid Lebrun 			return desc;
78*d1df6fd8SDavid Lebrun 	}
79*d1df6fd8SDavid Lebrun 
80*d1df6fd8SDavid Lebrun 	return NULL;
81*d1df6fd8SDavid Lebrun }
82*d1df6fd8SDavid Lebrun 
83*d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb)
84*d1df6fd8SDavid Lebrun {
85*d1df6fd8SDavid Lebrun 	struct dst_entry *orig_dst = skb_dst(skb);
86*d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
87*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
88*d1df6fd8SDavid Lebrun 
89*d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
90*d1df6fd8SDavid Lebrun 	desc = slwt->desc;
91*d1df6fd8SDavid Lebrun 
92*d1df6fd8SDavid Lebrun 	return desc->input(skb, slwt);
93*d1df6fd8SDavid Lebrun }
94*d1df6fd8SDavid Lebrun 
95*d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
96*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_ACTION]	= { .type = NLA_U32 },
97*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .type = NLA_BINARY },
98*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .type = NLA_U32 },
99*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .type = NLA_BINARY,
100*d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in_addr) },
101*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .type = NLA_BINARY,
102*d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in6_addr) },
103*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .type = NLA_U32 },
104*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .type = NLA_U32 },
105*d1df6fd8SDavid Lebrun };
106*d1df6fd8SDavid Lebrun 
107*d1df6fd8SDavid Lebrun struct seg6_action_param {
108*d1df6fd8SDavid Lebrun 	int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
109*d1df6fd8SDavid Lebrun 	int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
110*d1df6fd8SDavid Lebrun 	int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
111*d1df6fd8SDavid Lebrun };
112*d1df6fd8SDavid Lebrun 
113*d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
114*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .parse = NULL,
115*d1df6fd8SDavid Lebrun 				    .put = NULL,
116*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
117*d1df6fd8SDavid Lebrun 
118*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .parse = NULL,
119*d1df6fd8SDavid Lebrun 				    .put = NULL,
120*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
121*d1df6fd8SDavid Lebrun 
122*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .parse = NULL,
123*d1df6fd8SDavid Lebrun 				    .put = NULL,
124*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
125*d1df6fd8SDavid Lebrun 
126*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .parse = NULL,
127*d1df6fd8SDavid Lebrun 				    .put = NULL,
128*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
129*d1df6fd8SDavid Lebrun 
130*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .parse = NULL,
131*d1df6fd8SDavid Lebrun 				    .put = NULL,
132*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
133*d1df6fd8SDavid Lebrun 
134*d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .parse = NULL,
135*d1df6fd8SDavid Lebrun 				    .put = NULL,
136*d1df6fd8SDavid Lebrun 				    .cmp = NULL },
137*d1df6fd8SDavid Lebrun };
138*d1df6fd8SDavid Lebrun 
139*d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
140*d1df6fd8SDavid Lebrun {
141*d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
142*d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
143*d1df6fd8SDavid Lebrun 	int i, err;
144*d1df6fd8SDavid Lebrun 
145*d1df6fd8SDavid Lebrun 	desc = __get_action_desc(slwt->action);
146*d1df6fd8SDavid Lebrun 	if (!desc)
147*d1df6fd8SDavid Lebrun 		return -EINVAL;
148*d1df6fd8SDavid Lebrun 
149*d1df6fd8SDavid Lebrun 	if (!desc->input)
150*d1df6fd8SDavid Lebrun 		return -EOPNOTSUPP;
151*d1df6fd8SDavid Lebrun 
152*d1df6fd8SDavid Lebrun 	slwt->desc = desc;
153*d1df6fd8SDavid Lebrun 	slwt->headroom += desc->static_headroom;
154*d1df6fd8SDavid Lebrun 
155*d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
156*d1df6fd8SDavid Lebrun 		if (desc->attrs & (1 << i)) {
157*d1df6fd8SDavid Lebrun 			if (!attrs[i])
158*d1df6fd8SDavid Lebrun 				return -EINVAL;
159*d1df6fd8SDavid Lebrun 
160*d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
161*d1df6fd8SDavid Lebrun 
162*d1df6fd8SDavid Lebrun 			err = param->parse(attrs, slwt);
163*d1df6fd8SDavid Lebrun 			if (err < 0)
164*d1df6fd8SDavid Lebrun 				return err;
165*d1df6fd8SDavid Lebrun 		}
166*d1df6fd8SDavid Lebrun 	}
167*d1df6fd8SDavid Lebrun 
168*d1df6fd8SDavid Lebrun 	return 0;
169*d1df6fd8SDavid Lebrun }
170*d1df6fd8SDavid Lebrun 
171*d1df6fd8SDavid Lebrun static int seg6_local_build_state(struct nlattr *nla, unsigned int family,
172*d1df6fd8SDavid Lebrun 				  const void *cfg, struct lwtunnel_state **ts,
173*d1df6fd8SDavid Lebrun 				  struct netlink_ext_ack *extack)
174*d1df6fd8SDavid Lebrun {
175*d1df6fd8SDavid Lebrun 	struct nlattr *tb[SEG6_LOCAL_MAX + 1];
176*d1df6fd8SDavid Lebrun 	struct lwtunnel_state *newts;
177*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
178*d1df6fd8SDavid Lebrun 	int err;
179*d1df6fd8SDavid Lebrun 
180*d1df6fd8SDavid Lebrun 	err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy,
181*d1df6fd8SDavid Lebrun 			       extack);
182*d1df6fd8SDavid Lebrun 
183*d1df6fd8SDavid Lebrun 	if (err < 0)
184*d1df6fd8SDavid Lebrun 		return err;
185*d1df6fd8SDavid Lebrun 
186*d1df6fd8SDavid Lebrun 	if (!tb[SEG6_LOCAL_ACTION])
187*d1df6fd8SDavid Lebrun 		return -EINVAL;
188*d1df6fd8SDavid Lebrun 
189*d1df6fd8SDavid Lebrun 	newts = lwtunnel_state_alloc(sizeof(*slwt));
190*d1df6fd8SDavid Lebrun 	if (!newts)
191*d1df6fd8SDavid Lebrun 		return -ENOMEM;
192*d1df6fd8SDavid Lebrun 
193*d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(newts);
194*d1df6fd8SDavid Lebrun 	slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
195*d1df6fd8SDavid Lebrun 
196*d1df6fd8SDavid Lebrun 	err = parse_nla_action(tb, slwt);
197*d1df6fd8SDavid Lebrun 	if (err < 0)
198*d1df6fd8SDavid Lebrun 		goto out_free;
199*d1df6fd8SDavid Lebrun 
200*d1df6fd8SDavid Lebrun 	newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
201*d1df6fd8SDavid Lebrun 	newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
202*d1df6fd8SDavid Lebrun 	newts->headroom = slwt->headroom;
203*d1df6fd8SDavid Lebrun 
204*d1df6fd8SDavid Lebrun 	*ts = newts;
205*d1df6fd8SDavid Lebrun 
206*d1df6fd8SDavid Lebrun 	return 0;
207*d1df6fd8SDavid Lebrun 
208*d1df6fd8SDavid Lebrun out_free:
209*d1df6fd8SDavid Lebrun 	kfree(slwt->srh);
210*d1df6fd8SDavid Lebrun 	kfree(newts);
211*d1df6fd8SDavid Lebrun 	return err;
212*d1df6fd8SDavid Lebrun }
213*d1df6fd8SDavid Lebrun 
214*d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
215*d1df6fd8SDavid Lebrun {
216*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
217*d1df6fd8SDavid Lebrun 
218*d1df6fd8SDavid Lebrun 	kfree(slwt->srh);
219*d1df6fd8SDavid Lebrun }
220*d1df6fd8SDavid Lebrun 
221*d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb,
222*d1df6fd8SDavid Lebrun 				 struct lwtunnel_state *lwt)
223*d1df6fd8SDavid Lebrun {
224*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
225*d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
226*d1df6fd8SDavid Lebrun 	int i, err;
227*d1df6fd8SDavid Lebrun 
228*d1df6fd8SDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
229*d1df6fd8SDavid Lebrun 		return -EMSGSIZE;
230*d1df6fd8SDavid Lebrun 
231*d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
232*d1df6fd8SDavid Lebrun 		if (slwt->desc->attrs & (1 << i)) {
233*d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
234*d1df6fd8SDavid Lebrun 			err = param->put(skb, slwt);
235*d1df6fd8SDavid Lebrun 			if (err < 0)
236*d1df6fd8SDavid Lebrun 				return err;
237*d1df6fd8SDavid Lebrun 		}
238*d1df6fd8SDavid Lebrun 	}
239*d1df6fd8SDavid Lebrun 
240*d1df6fd8SDavid Lebrun 	return 0;
241*d1df6fd8SDavid Lebrun }
242*d1df6fd8SDavid Lebrun 
243*d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
244*d1df6fd8SDavid Lebrun {
245*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
246*d1df6fd8SDavid Lebrun 	unsigned long attrs;
247*d1df6fd8SDavid Lebrun 	int nlsize;
248*d1df6fd8SDavid Lebrun 
249*d1df6fd8SDavid Lebrun 	nlsize = nla_total_size(4); /* action */
250*d1df6fd8SDavid Lebrun 
251*d1df6fd8SDavid Lebrun 	attrs = slwt->desc->attrs;
252*d1df6fd8SDavid Lebrun 
253*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_SRH))
254*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
255*d1df6fd8SDavid Lebrun 
256*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_TABLE))
257*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
258*d1df6fd8SDavid Lebrun 
259*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_NH4))
260*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
261*d1df6fd8SDavid Lebrun 
262*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_NH6))
263*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(16);
264*d1df6fd8SDavid Lebrun 
265*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_IIF))
266*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
267*d1df6fd8SDavid Lebrun 
268*d1df6fd8SDavid Lebrun 	if (attrs & (1 << SEG6_LOCAL_OIF))
269*d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
270*d1df6fd8SDavid Lebrun 
271*d1df6fd8SDavid Lebrun 	return nlsize;
272*d1df6fd8SDavid Lebrun }
273*d1df6fd8SDavid Lebrun 
274*d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a,
275*d1df6fd8SDavid Lebrun 				struct lwtunnel_state *b)
276*d1df6fd8SDavid Lebrun {
277*d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt_a, *slwt_b;
278*d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
279*d1df6fd8SDavid Lebrun 	int i;
280*d1df6fd8SDavid Lebrun 
281*d1df6fd8SDavid Lebrun 	slwt_a = seg6_local_lwtunnel(a);
282*d1df6fd8SDavid Lebrun 	slwt_b = seg6_local_lwtunnel(b);
283*d1df6fd8SDavid Lebrun 
284*d1df6fd8SDavid Lebrun 	if (slwt_a->action != slwt_b->action)
285*d1df6fd8SDavid Lebrun 		return 1;
286*d1df6fd8SDavid Lebrun 
287*d1df6fd8SDavid Lebrun 	if (slwt_a->desc->attrs != slwt_b->desc->attrs)
288*d1df6fd8SDavid Lebrun 		return 1;
289*d1df6fd8SDavid Lebrun 
290*d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
291*d1df6fd8SDavid Lebrun 		if (slwt_a->desc->attrs & (1 << i)) {
292*d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
293*d1df6fd8SDavid Lebrun 			if (param->cmp(slwt_a, slwt_b))
294*d1df6fd8SDavid Lebrun 				return 1;
295*d1df6fd8SDavid Lebrun 		}
296*d1df6fd8SDavid Lebrun 	}
297*d1df6fd8SDavid Lebrun 
298*d1df6fd8SDavid Lebrun 	return 0;
299*d1df6fd8SDavid Lebrun }
300*d1df6fd8SDavid Lebrun 
301*d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = {
302*d1df6fd8SDavid Lebrun 	.build_state	= seg6_local_build_state,
303*d1df6fd8SDavid Lebrun 	.destroy_state	= seg6_local_destroy_state,
304*d1df6fd8SDavid Lebrun 	.input		= seg6_local_input,
305*d1df6fd8SDavid Lebrun 	.fill_encap	= seg6_local_fill_encap,
306*d1df6fd8SDavid Lebrun 	.get_encap_size	= seg6_local_get_encap_size,
307*d1df6fd8SDavid Lebrun 	.cmp_encap	= seg6_local_cmp_encap,
308*d1df6fd8SDavid Lebrun 	.owner		= THIS_MODULE,
309*d1df6fd8SDavid Lebrun };
310*d1df6fd8SDavid Lebrun 
311*d1df6fd8SDavid Lebrun int __init seg6_local_init(void)
312*d1df6fd8SDavid Lebrun {
313*d1df6fd8SDavid Lebrun 	return lwtunnel_encap_add_ops(&seg6_local_ops,
314*d1df6fd8SDavid Lebrun 				      LWTUNNEL_ENCAP_SEG6_LOCAL);
315*d1df6fd8SDavid Lebrun }
316*d1df6fd8SDavid Lebrun 
317*d1df6fd8SDavid Lebrun void seg6_local_exit(void)
318*d1df6fd8SDavid Lebrun {
319*d1df6fd8SDavid Lebrun 	lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
320*d1df6fd8SDavid Lebrun }
321