xref: /openbmc/linux/net/ipv6/seg6_local.c (revision 7a3f5b0de3647c854e34269c3332d7a1e902901a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2d1df6fd8SDavid Lebrun /*
3d1df6fd8SDavid Lebrun  *  SR-IPv6 implementation
4d1df6fd8SDavid Lebrun  *
5004d4b27SMathieu Xhonneux  *  Authors:
6d1df6fd8SDavid Lebrun  *  David Lebrun <david.lebrun@uclouvain.be>
7004d4b27SMathieu Xhonneux  *  eBPF support: Mathieu Xhonneux <m.xhonneux@gmail.com>
8d1df6fd8SDavid Lebrun  */
9d1df6fd8SDavid Lebrun 
10d1df6fd8SDavid Lebrun #include <linux/types.h>
11d1df6fd8SDavid Lebrun #include <linux/skbuff.h>
12d1df6fd8SDavid Lebrun #include <linux/net.h>
13d1df6fd8SDavid Lebrun #include <linux/module.h>
14d1df6fd8SDavid Lebrun #include <net/ip.h>
15d1df6fd8SDavid Lebrun #include <net/lwtunnel.h>
16d1df6fd8SDavid Lebrun #include <net/netevent.h>
17d1df6fd8SDavid Lebrun #include <net/netns/generic.h>
18d1df6fd8SDavid Lebrun #include <net/ip6_fib.h>
19d1df6fd8SDavid Lebrun #include <net/route.h>
20d1df6fd8SDavid Lebrun #include <net/seg6.h>
21d1df6fd8SDavid Lebrun #include <linux/seg6.h>
22d1df6fd8SDavid Lebrun #include <linux/seg6_local.h>
23d1df6fd8SDavid Lebrun #include <net/addrconf.h>
24d1df6fd8SDavid Lebrun #include <net/ip6_route.h>
25d1df6fd8SDavid Lebrun #include <net/dst_cache.h>
2662ebaeaeSYuki Taguchi #include <net/ip_tunnels.h>
27d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
28d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h>
29d1df6fd8SDavid Lebrun #endif
301c1e761eSMathieu Xhonneux #include <net/seg6_local.h>
31891ef8ddSDavid Lebrun #include <linux/etherdevice.h>
32004d4b27SMathieu Xhonneux #include <linux/bpf.h>
33*7a3f5b0dSRyoga Saito #include <net/lwtunnel.h>
34*7a3f5b0dSRyoga Saito #include <linux/netfilter.h>
35d1df6fd8SDavid Lebrun 
36300a0fd8SAndrea Mayer #define SEG6_F_ATTR(i)		BIT(i)
37300a0fd8SAndrea Mayer 
38d1df6fd8SDavid Lebrun struct seg6_local_lwt;
39d1df6fd8SDavid Lebrun 
40cfdf64a0SAndrea Mayer /* callbacks used for customizing the creation and destruction of a behavior */
41cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops {
42cfdf64a0SAndrea Mayer 	int (*build_state)(struct seg6_local_lwt *slwt, const void *cfg,
43cfdf64a0SAndrea Mayer 			   struct netlink_ext_ack *extack);
44cfdf64a0SAndrea Mayer 	void (*destroy_state)(struct seg6_local_lwt *slwt);
45cfdf64a0SAndrea Mayer };
46cfdf64a0SAndrea Mayer 
47d1df6fd8SDavid Lebrun struct seg6_action_desc {
48d1df6fd8SDavid Lebrun 	int action;
49d1df6fd8SDavid Lebrun 	unsigned long attrs;
500a3021f1SAndrea Mayer 
510a3021f1SAndrea Mayer 	/* The optattrs field is used for specifying all the optional
520a3021f1SAndrea Mayer 	 * attributes supported by a specific behavior.
530a3021f1SAndrea Mayer 	 * It means that if one of these attributes is not provided in the
540a3021f1SAndrea Mayer 	 * netlink message during the behavior creation, no errors will be
550a3021f1SAndrea Mayer 	 * returned to the userspace.
560a3021f1SAndrea Mayer 	 *
570a3021f1SAndrea Mayer 	 * Each attribute can be only of two types (mutually exclusive):
580a3021f1SAndrea Mayer 	 * 1) required or 2) optional.
590a3021f1SAndrea Mayer 	 * Every user MUST obey to this rule! If you set an attribute as
600a3021f1SAndrea Mayer 	 * required the same attribute CANNOT be set as optional and vice
610a3021f1SAndrea Mayer 	 * versa.
620a3021f1SAndrea Mayer 	 */
630a3021f1SAndrea Mayer 	unsigned long optattrs;
640a3021f1SAndrea Mayer 
65d1df6fd8SDavid Lebrun 	int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
66d1df6fd8SDavid Lebrun 	int static_headroom;
67cfdf64a0SAndrea Mayer 
68cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops slwt_ops;
69d1df6fd8SDavid Lebrun };
70d1df6fd8SDavid Lebrun 
71004d4b27SMathieu Xhonneux struct bpf_lwt_prog {
72004d4b27SMathieu Xhonneux 	struct bpf_prog *prog;
73004d4b27SMathieu Xhonneux 	char *name;
74004d4b27SMathieu Xhonneux };
75004d4b27SMathieu Xhonneux 
76664d6f86SAndrea Mayer enum seg6_end_dt_mode {
77664d6f86SAndrea Mayer 	DT_INVALID_MODE	= -EINVAL,
78664d6f86SAndrea Mayer 	DT_LEGACY_MODE	= 0,
79664d6f86SAndrea Mayer 	DT_VRF_MODE	= 1,
80664d6f86SAndrea Mayer };
81664d6f86SAndrea Mayer 
82664d6f86SAndrea Mayer struct seg6_end_dt_info {
83664d6f86SAndrea Mayer 	enum seg6_end_dt_mode mode;
84664d6f86SAndrea Mayer 
85664d6f86SAndrea Mayer 	struct net *net;
86664d6f86SAndrea Mayer 	/* VRF device associated to the routing table used by the SRv6
87664d6f86SAndrea Mayer 	 * End.DT4/DT6 behavior for routing IPv4/IPv6 packets.
88664d6f86SAndrea Mayer 	 */
89664d6f86SAndrea Mayer 	int vrf_ifindex;
90664d6f86SAndrea Mayer 	int vrf_table;
91664d6f86SAndrea Mayer 
928b532109SAndrea Mayer 	/* tunneled packet family (IPv4 or IPv6).
938b532109SAndrea Mayer 	 * Protocol and header length are inferred from family.
948b532109SAndrea Mayer 	 */
95664d6f86SAndrea Mayer 	u16 family;
96664d6f86SAndrea Mayer };
97664d6f86SAndrea Mayer 
9894604548SAndrea Mayer struct pcpu_seg6_local_counters {
9994604548SAndrea Mayer 	u64_stats_t packets;
10094604548SAndrea Mayer 	u64_stats_t bytes;
10194604548SAndrea Mayer 	u64_stats_t errors;
10294604548SAndrea Mayer 
10394604548SAndrea Mayer 	struct u64_stats_sync syncp;
10494604548SAndrea Mayer };
10594604548SAndrea Mayer 
10694604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far.
10794604548SAndrea Mayer  *
10894604548SAndrea Mayer  * put_nla_counters() makes use of this data structure to collect all counter
10994604548SAndrea Mayer  * values after the per-CPU counter evaluation has been performed.
11094604548SAndrea Mayer  * Finally, each counter value (in seg6_local_counters) is stored in the
11194604548SAndrea Mayer  * corresponding netlink attribute and sent to user space.
11294604548SAndrea Mayer  *
11394604548SAndrea Mayer  * NB: we don't want to expose this structure to user space!
11494604548SAndrea Mayer  */
11594604548SAndrea Mayer struct seg6_local_counters {
11694604548SAndrea Mayer 	__u64 packets;
11794604548SAndrea Mayer 	__u64 bytes;
11894604548SAndrea Mayer 	__u64 errors;
11994604548SAndrea Mayer };
12094604548SAndrea Mayer 
12194604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp)				\
12294604548SAndrea Mayer 	__netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters,	\
12394604548SAndrea Mayer 				  ((__gfp) | __GFP_ZERO))
12494604548SAndrea Mayer 
12594604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS	SEG6_F_ATTR(SEG6_LOCAL_COUNTERS)
12694604548SAndrea Mayer 
127d1df6fd8SDavid Lebrun struct seg6_local_lwt {
128d1df6fd8SDavid Lebrun 	int action;
129d1df6fd8SDavid Lebrun 	struct ipv6_sr_hdr *srh;
130d1df6fd8SDavid Lebrun 	int table;
131d1df6fd8SDavid Lebrun 	struct in_addr nh4;
132d1df6fd8SDavid Lebrun 	struct in6_addr nh6;
133d1df6fd8SDavid Lebrun 	int iif;
134d1df6fd8SDavid Lebrun 	int oif;
135004d4b27SMathieu Xhonneux 	struct bpf_lwt_prog bpf;
136664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
137664d6f86SAndrea Mayer 	struct seg6_end_dt_info dt_info;
138664d6f86SAndrea Mayer #endif
13994604548SAndrea Mayer 	struct pcpu_seg6_local_counters __percpu *pcpu_counters;
140d1df6fd8SDavid Lebrun 
141d1df6fd8SDavid Lebrun 	int headroom;
142d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1430a3021f1SAndrea Mayer 	/* unlike the required attrs, we have to track the optional attributes
1440a3021f1SAndrea Mayer 	 * that have been effectively parsed.
1450a3021f1SAndrea Mayer 	 */
1460a3021f1SAndrea Mayer 	unsigned long parsed_optattrs;
147d1df6fd8SDavid Lebrun };
148d1df6fd8SDavid Lebrun 
149d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
150d1df6fd8SDavid Lebrun {
151d1df6fd8SDavid Lebrun 	return (struct seg6_local_lwt *)lwt->data;
152d1df6fd8SDavid Lebrun }
153d1df6fd8SDavid Lebrun 
154fbbc5bc2SJulien Massonneau static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb, int flags)
155140f04c3SDavid Lebrun {
156140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
1575829d70bSAhmed Abdelsalam 	int len, srhoff = 0;
158140f04c3SDavid Lebrun 
159fbbc5bc2SJulien Massonneau 	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0)
160140f04c3SDavid Lebrun 		return NULL;
161140f04c3SDavid Lebrun 
1625829d70bSAhmed Abdelsalam 	if (!pskb_may_pull(skb, srhoff + sizeof(*srh)))
1635829d70bSAhmed Abdelsalam 		return NULL;
1645829d70bSAhmed Abdelsalam 
1655829d70bSAhmed Abdelsalam 	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
1665829d70bSAhmed Abdelsalam 
167140f04c3SDavid Lebrun 	len = (srh->hdrlen + 1) << 3;
168140f04c3SDavid Lebrun 
1695829d70bSAhmed Abdelsalam 	if (!pskb_may_pull(skb, srhoff + len))
170140f04c3SDavid Lebrun 		return NULL;
171140f04c3SDavid Lebrun 
1727f91ed8cSAndrea Mayer 	/* note that pskb_may_pull may change pointers in header;
1737f91ed8cSAndrea Mayer 	 * for this reason it is necessary to reload them when needed.
1747f91ed8cSAndrea Mayer 	 */
1757f91ed8cSAndrea Mayer 	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
1767f91ed8cSAndrea Mayer 
177bb986a50SAhmed Abdelsalam 	if (!seg6_validate_srh(srh, len, true))
178140f04c3SDavid Lebrun 		return NULL;
179140f04c3SDavid Lebrun 
180140f04c3SDavid Lebrun 	return srh;
181140f04c3SDavid Lebrun }
182140f04c3SDavid Lebrun 
183140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
184140f04c3SDavid Lebrun {
185140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
186140f04c3SDavid Lebrun 
187fbbc5bc2SJulien Massonneau 	srh = get_srh(skb, IP6_FH_F_SKIP_RH);
188140f04c3SDavid Lebrun 	if (!srh)
189140f04c3SDavid Lebrun 		return NULL;
190140f04c3SDavid Lebrun 
191140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
192140f04c3SDavid Lebrun 	if (!seg6_hmac_validate_skb(skb))
193140f04c3SDavid Lebrun 		return NULL;
194140f04c3SDavid Lebrun #endif
195140f04c3SDavid Lebrun 
196140f04c3SDavid Lebrun 	return srh;
197140f04c3SDavid Lebrun }
198140f04c3SDavid Lebrun 
199d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto)
200d7a669ddSDavid Lebrun {
201d7a669ddSDavid Lebrun 	struct ipv6_sr_hdr *srh;
202d7a669ddSDavid Lebrun 	unsigned int off = 0;
203d7a669ddSDavid Lebrun 
204fbbc5bc2SJulien Massonneau 	srh = get_srh(skb, 0);
205d7a669ddSDavid Lebrun 	if (srh && srh->segments_left > 0)
206d7a669ddSDavid Lebrun 		return false;
207d7a669ddSDavid Lebrun 
208d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
209d7a669ddSDavid Lebrun 	if (srh && !seg6_hmac_validate_skb(skb))
210d7a669ddSDavid Lebrun 		return false;
211d7a669ddSDavid Lebrun #endif
212d7a669ddSDavid Lebrun 
213d7a669ddSDavid Lebrun 	if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0)
214d7a669ddSDavid Lebrun 		return false;
215d7a669ddSDavid Lebrun 
216d7a669ddSDavid Lebrun 	if (!pskb_pull(skb, off))
217d7a669ddSDavid Lebrun 		return false;
218d7a669ddSDavid Lebrun 
219d7a669ddSDavid Lebrun 	skb_postpull_rcsum(skb, skb_network_header(skb), off);
220d7a669ddSDavid Lebrun 
221d7a669ddSDavid Lebrun 	skb_reset_network_header(skb);
222d7a669ddSDavid Lebrun 	skb_reset_transport_header(skb);
22362ebaeaeSYuki Taguchi 	if (iptunnel_pull_offloads(skb))
22462ebaeaeSYuki Taguchi 		return false;
225d7a669ddSDavid Lebrun 
226d7a669ddSDavid Lebrun 	return true;
227d7a669ddSDavid Lebrun }
228d7a669ddSDavid Lebrun 
229d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr)
230d7a669ddSDavid Lebrun {
231d7a669ddSDavid Lebrun 	struct in6_addr *addr;
232d7a669ddSDavid Lebrun 
233d7a669ddSDavid Lebrun 	srh->segments_left--;
234d7a669ddSDavid Lebrun 	addr = srh->segments + srh->segments_left;
235d7a669ddSDavid Lebrun 	*daddr = *addr;
236d7a669ddSDavid Lebrun }
237d7a669ddSDavid Lebrun 
238fd1fef0cSAndrea Mayer static int
239fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
240fd1fef0cSAndrea Mayer 			u32 tbl_id, bool local_delivery)
241d7a669ddSDavid Lebrun {
242d7a669ddSDavid Lebrun 	struct net *net = dev_net(skb->dev);
243d7a669ddSDavid Lebrun 	struct ipv6hdr *hdr = ipv6_hdr(skb);
244d7a669ddSDavid Lebrun 	int flags = RT6_LOOKUP_F_HAS_SADDR;
245d7a669ddSDavid Lebrun 	struct dst_entry *dst = NULL;
246d7a669ddSDavid Lebrun 	struct rt6_info *rt;
247d7a669ddSDavid Lebrun 	struct flowi6 fl6;
248fd1fef0cSAndrea Mayer 	int dev_flags = 0;
249d7a669ddSDavid Lebrun 
250d7a669ddSDavid Lebrun 	fl6.flowi6_iif = skb->dev->ifindex;
251d7a669ddSDavid Lebrun 	fl6.daddr = nhaddr ? *nhaddr : hdr->daddr;
252d7a669ddSDavid Lebrun 	fl6.saddr = hdr->saddr;
253d7a669ddSDavid Lebrun 	fl6.flowlabel = ip6_flowinfo(hdr);
254d7a669ddSDavid Lebrun 	fl6.flowi6_mark = skb->mark;
255d7a669ddSDavid Lebrun 	fl6.flowi6_proto = hdr->nexthdr;
256d7a669ddSDavid Lebrun 
257d7a669ddSDavid Lebrun 	if (nhaddr)
258d7a669ddSDavid Lebrun 		fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH;
259d7a669ddSDavid Lebrun 
260d7a669ddSDavid Lebrun 	if (!tbl_id) {
261b75cc8f9SDavid Ahern 		dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags);
262d7a669ddSDavid Lebrun 	} else {
263d7a669ddSDavid Lebrun 		struct fib6_table *table;
264d7a669ddSDavid Lebrun 
265d7a669ddSDavid Lebrun 		table = fib6_get_table(net, tbl_id);
266d7a669ddSDavid Lebrun 		if (!table)
267d7a669ddSDavid Lebrun 			goto out;
268d7a669ddSDavid Lebrun 
269b75cc8f9SDavid Ahern 		rt = ip6_pol_route(net, table, 0, &fl6, skb, flags);
270d7a669ddSDavid Lebrun 		dst = &rt->dst;
271d7a669ddSDavid Lebrun 	}
272d7a669ddSDavid Lebrun 
273fd1fef0cSAndrea Mayer 	/* we want to discard traffic destined for local packet processing,
274fd1fef0cSAndrea Mayer 	 * if @local_delivery is set to false.
275fd1fef0cSAndrea Mayer 	 */
276fd1fef0cSAndrea Mayer 	if (!local_delivery)
277fd1fef0cSAndrea Mayer 		dev_flags |= IFF_LOOPBACK;
278fd1fef0cSAndrea Mayer 
279fd1fef0cSAndrea Mayer 	if (dst && (dst->dev->flags & dev_flags) && !dst->error) {
280d7a669ddSDavid Lebrun 		dst_release(dst);
281d7a669ddSDavid Lebrun 		dst = NULL;
282d7a669ddSDavid Lebrun 	}
283d7a669ddSDavid Lebrun 
284d7a669ddSDavid Lebrun out:
285d7a669ddSDavid Lebrun 	if (!dst) {
286d7a669ddSDavid Lebrun 		rt = net->ipv6.ip6_blk_hole_entry;
287d7a669ddSDavid Lebrun 		dst = &rt->dst;
288d7a669ddSDavid Lebrun 		dst_hold(dst);
289d7a669ddSDavid Lebrun 	}
290d7a669ddSDavid Lebrun 
291d7a669ddSDavid Lebrun 	skb_dst_drop(skb);
292d7a669ddSDavid Lebrun 	skb_dst_set(skb, dst);
2931c1e761eSMathieu Xhonneux 	return dst->error;
294d7a669ddSDavid Lebrun }
295d7a669ddSDavid Lebrun 
296fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb,
297fd1fef0cSAndrea Mayer 			struct in6_addr *nhaddr, u32 tbl_id)
298fd1fef0cSAndrea Mayer {
299fd1fef0cSAndrea Mayer 	return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false);
300fd1fef0cSAndrea Mayer }
301fd1fef0cSAndrea Mayer 
302140f04c3SDavid Lebrun /* regular endpoint function */
303140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
304140f04c3SDavid Lebrun {
305140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
306140f04c3SDavid Lebrun 
307140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
308140f04c3SDavid Lebrun 	if (!srh)
309140f04c3SDavid Lebrun 		goto drop;
310140f04c3SDavid Lebrun 
311d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
312140f04c3SDavid Lebrun 
3131c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
314140f04c3SDavid Lebrun 
315140f04c3SDavid Lebrun 	return dst_input(skb);
316140f04c3SDavid Lebrun 
317140f04c3SDavid Lebrun drop:
318140f04c3SDavid Lebrun 	kfree_skb(skb);
319140f04c3SDavid Lebrun 	return -EINVAL;
320140f04c3SDavid Lebrun }
321140f04c3SDavid Lebrun 
322140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */
323140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
324140f04c3SDavid Lebrun {
325140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
326140f04c3SDavid Lebrun 
327140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
328140f04c3SDavid Lebrun 	if (!srh)
329140f04c3SDavid Lebrun 		goto drop;
330140f04c3SDavid Lebrun 
331d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
332140f04c3SDavid Lebrun 
3331c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, &slwt->nh6, 0);
334140f04c3SDavid Lebrun 
335140f04c3SDavid Lebrun 	return dst_input(skb);
336140f04c3SDavid Lebrun 
337140f04c3SDavid Lebrun drop:
338140f04c3SDavid Lebrun 	kfree_skb(skb);
339140f04c3SDavid Lebrun 	return -EINVAL;
340140f04c3SDavid Lebrun }
341140f04c3SDavid Lebrun 
342891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt)
343891ef8ddSDavid Lebrun {
344891ef8ddSDavid Lebrun 	struct ipv6_sr_hdr *srh;
345891ef8ddSDavid Lebrun 
346891ef8ddSDavid Lebrun 	srh = get_and_validate_srh(skb);
347891ef8ddSDavid Lebrun 	if (!srh)
348891ef8ddSDavid Lebrun 		goto drop;
349891ef8ddSDavid Lebrun 
350891ef8ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
351891ef8ddSDavid Lebrun 
3521c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, slwt->table);
353891ef8ddSDavid Lebrun 
354891ef8ddSDavid Lebrun 	return dst_input(skb);
355891ef8ddSDavid Lebrun 
356891ef8ddSDavid Lebrun drop:
357891ef8ddSDavid Lebrun 	kfree_skb(skb);
358891ef8ddSDavid Lebrun 	return -EINVAL;
359891ef8ddSDavid Lebrun }
360891ef8ddSDavid Lebrun 
361891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */
362891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb,
363891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
364891ef8ddSDavid Lebrun {
365891ef8ddSDavid Lebrun 	struct net *net = dev_net(skb->dev);
366891ef8ddSDavid Lebrun 	struct net_device *odev;
367891ef8ddSDavid Lebrun 	struct ethhdr *eth;
368891ef8ddSDavid Lebrun 
36926776253SPaolo Lungaroni 	if (!decap_and_validate(skb, IPPROTO_ETHERNET))
370891ef8ddSDavid Lebrun 		goto drop;
371891ef8ddSDavid Lebrun 
372891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, ETH_HLEN))
373891ef8ddSDavid Lebrun 		goto drop;
374891ef8ddSDavid Lebrun 
375891ef8ddSDavid Lebrun 	skb_reset_mac_header(skb);
376891ef8ddSDavid Lebrun 	eth = (struct ethhdr *)skb->data;
377891ef8ddSDavid Lebrun 
378891ef8ddSDavid Lebrun 	/* To determine the frame's protocol, we assume it is 802.3. This avoids
379891ef8ddSDavid Lebrun 	 * a call to eth_type_trans(), which is not really relevant for our
380891ef8ddSDavid Lebrun 	 * use case.
381891ef8ddSDavid Lebrun 	 */
382891ef8ddSDavid Lebrun 	if (!eth_proto_is_802_3(eth->h_proto))
383891ef8ddSDavid Lebrun 		goto drop;
384891ef8ddSDavid Lebrun 
385891ef8ddSDavid Lebrun 	odev = dev_get_by_index_rcu(net, slwt->oif);
386891ef8ddSDavid Lebrun 	if (!odev)
387891ef8ddSDavid Lebrun 		goto drop;
388891ef8ddSDavid Lebrun 
389891ef8ddSDavid Lebrun 	/* As we accept Ethernet frames, make sure the egress device is of
390891ef8ddSDavid Lebrun 	 * the correct type.
391891ef8ddSDavid Lebrun 	 */
392891ef8ddSDavid Lebrun 	if (odev->type != ARPHRD_ETHER)
393891ef8ddSDavid Lebrun 		goto drop;
394891ef8ddSDavid Lebrun 
395891ef8ddSDavid Lebrun 	if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev))
396891ef8ddSDavid Lebrun 		goto drop;
397891ef8ddSDavid Lebrun 
398891ef8ddSDavid Lebrun 	skb_orphan(skb);
399891ef8ddSDavid Lebrun 
400891ef8ddSDavid Lebrun 	if (skb_warn_if_lro(skb))
401891ef8ddSDavid Lebrun 		goto drop;
402891ef8ddSDavid Lebrun 
403891ef8ddSDavid Lebrun 	skb_forward_csum(skb);
404891ef8ddSDavid Lebrun 
405891ef8ddSDavid Lebrun 	if (skb->len - ETH_HLEN > odev->mtu)
406891ef8ddSDavid Lebrun 		goto drop;
407891ef8ddSDavid Lebrun 
408891ef8ddSDavid Lebrun 	skb->dev = odev;
409891ef8ddSDavid Lebrun 	skb->protocol = eth->h_proto;
410891ef8ddSDavid Lebrun 
411891ef8ddSDavid Lebrun 	return dev_queue_xmit(skb);
412891ef8ddSDavid Lebrun 
413891ef8ddSDavid Lebrun drop:
414891ef8ddSDavid Lebrun 	kfree_skb(skb);
415891ef8ddSDavid Lebrun 	return -EINVAL;
416891ef8ddSDavid Lebrun }
417891ef8ddSDavid Lebrun 
418*7a3f5b0dSRyoga Saito static int input_action_end_dx6_finish(struct net *net, struct sock *sk,
419*7a3f5b0dSRyoga Saito 				       struct sk_buff *skb)
420*7a3f5b0dSRyoga Saito {
421*7a3f5b0dSRyoga Saito 	struct dst_entry *orig_dst = skb_dst(skb);
422*7a3f5b0dSRyoga Saito 	struct in6_addr *nhaddr = NULL;
423*7a3f5b0dSRyoga Saito 	struct seg6_local_lwt *slwt;
424*7a3f5b0dSRyoga Saito 
425*7a3f5b0dSRyoga Saito 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
426*7a3f5b0dSRyoga Saito 
427*7a3f5b0dSRyoga Saito 	/* The inner packet is not associated to any local interface,
428*7a3f5b0dSRyoga Saito 	 * so we do not call netif_rx().
429*7a3f5b0dSRyoga Saito 	 *
430*7a3f5b0dSRyoga Saito 	 * If slwt->nh6 is set to ::, then lookup the nexthop for the
431*7a3f5b0dSRyoga Saito 	 * inner packet's DA. Otherwise, use the specified nexthop.
432*7a3f5b0dSRyoga Saito 	 */
433*7a3f5b0dSRyoga Saito 	if (!ipv6_addr_any(&slwt->nh6))
434*7a3f5b0dSRyoga Saito 		nhaddr = &slwt->nh6;
435*7a3f5b0dSRyoga Saito 
436*7a3f5b0dSRyoga Saito 	seg6_lookup_nexthop(skb, nhaddr, 0);
437*7a3f5b0dSRyoga Saito 
438*7a3f5b0dSRyoga Saito 	return dst_input(skb);
439*7a3f5b0dSRyoga Saito }
440*7a3f5b0dSRyoga Saito 
441140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */
442140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb,
443140f04c3SDavid Lebrun 				struct seg6_local_lwt *slwt)
444140f04c3SDavid Lebrun {
445140f04c3SDavid Lebrun 	/* this function accepts IPv6 encapsulated packets, with either
446140f04c3SDavid Lebrun 	 * an SRH with SL=0, or no SRH.
447140f04c3SDavid Lebrun 	 */
448140f04c3SDavid Lebrun 
449d7a669ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPV6))
450140f04c3SDavid Lebrun 		goto drop;
451140f04c3SDavid Lebrun 
452d7a669ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
453140f04c3SDavid Lebrun 		goto drop;
454140f04c3SDavid Lebrun 
455c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
456*7a3f5b0dSRyoga Saito 	nf_reset_ct(skb);
457c71644d0SAndrea Mayer 
458*7a3f5b0dSRyoga Saito 	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
459*7a3f5b0dSRyoga Saito 		return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
460*7a3f5b0dSRyoga Saito 			       dev_net(skb->dev), NULL, skb, NULL,
461*7a3f5b0dSRyoga Saito 			       skb_dst(skb)->dev, input_action_end_dx6_finish);
462140f04c3SDavid Lebrun 
463*7a3f5b0dSRyoga Saito 	return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb);
464140f04c3SDavid Lebrun drop:
465140f04c3SDavid Lebrun 	kfree_skb(skb);
466140f04c3SDavid Lebrun 	return -EINVAL;
467140f04c3SDavid Lebrun }
468140f04c3SDavid Lebrun 
469*7a3f5b0dSRyoga Saito static int input_action_end_dx4_finish(struct net *net, struct sock *sk,
470*7a3f5b0dSRyoga Saito 				       struct sk_buff *skb)
471891ef8ddSDavid Lebrun {
472*7a3f5b0dSRyoga Saito 	struct dst_entry *orig_dst = skb_dst(skb);
473*7a3f5b0dSRyoga Saito 	struct seg6_local_lwt *slwt;
474891ef8ddSDavid Lebrun 	struct iphdr *iph;
475891ef8ddSDavid Lebrun 	__be32 nhaddr;
476891ef8ddSDavid Lebrun 	int err;
477891ef8ddSDavid Lebrun 
478*7a3f5b0dSRyoga Saito 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
479891ef8ddSDavid Lebrun 
480891ef8ddSDavid Lebrun 	iph = ip_hdr(skb);
481891ef8ddSDavid Lebrun 
482891ef8ddSDavid Lebrun 	nhaddr = slwt->nh4.s_addr ?: iph->daddr;
483891ef8ddSDavid Lebrun 
484891ef8ddSDavid Lebrun 	skb_dst_drop(skb);
485891ef8ddSDavid Lebrun 
486891ef8ddSDavid Lebrun 	err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
487*7a3f5b0dSRyoga Saito 	if (err) {
488*7a3f5b0dSRyoga Saito 		kfree_skb(skb);
489*7a3f5b0dSRyoga Saito 		return -EINVAL;
490*7a3f5b0dSRyoga Saito 	}
491891ef8ddSDavid Lebrun 
492891ef8ddSDavid Lebrun 	return dst_input(skb);
493*7a3f5b0dSRyoga Saito }
494891ef8ddSDavid Lebrun 
495*7a3f5b0dSRyoga Saito static int input_action_end_dx4(struct sk_buff *skb,
496*7a3f5b0dSRyoga Saito 				struct seg6_local_lwt *slwt)
497*7a3f5b0dSRyoga Saito {
498*7a3f5b0dSRyoga Saito 	if (!decap_and_validate(skb, IPPROTO_IPIP))
499*7a3f5b0dSRyoga Saito 		goto drop;
500*7a3f5b0dSRyoga Saito 
501*7a3f5b0dSRyoga Saito 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
502*7a3f5b0dSRyoga Saito 		goto drop;
503*7a3f5b0dSRyoga Saito 
504*7a3f5b0dSRyoga Saito 	skb->protocol = htons(ETH_P_IP);
505*7a3f5b0dSRyoga Saito 	skb_set_transport_header(skb, sizeof(struct iphdr));
506*7a3f5b0dSRyoga Saito 	nf_reset_ct(skb);
507*7a3f5b0dSRyoga Saito 
508*7a3f5b0dSRyoga Saito 	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
509*7a3f5b0dSRyoga Saito 		return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
510*7a3f5b0dSRyoga Saito 			       dev_net(skb->dev), NULL, skb, NULL,
511*7a3f5b0dSRyoga Saito 			       skb_dst(skb)->dev, input_action_end_dx4_finish);
512*7a3f5b0dSRyoga Saito 
513*7a3f5b0dSRyoga Saito 	return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb);
514891ef8ddSDavid Lebrun drop:
515891ef8ddSDavid Lebrun 	kfree_skb(skb);
516891ef8ddSDavid Lebrun 	return -EINVAL;
517891ef8ddSDavid Lebrun }
518891ef8ddSDavid Lebrun 
519664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
520664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg)
521664d6f86SAndrea Mayer {
522664d6f86SAndrea Mayer 	const struct nl_info *nli = &fib6_cfg->fc_nlinfo;
523664d6f86SAndrea Mayer 
524664d6f86SAndrea Mayer 	return nli->nl_net;
525664d6f86SAndrea Mayer }
526664d6f86SAndrea Mayer 
527664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg,
528664d6f86SAndrea Mayer 				   u16 family, struct netlink_ext_ack *extack)
529664d6f86SAndrea Mayer {
530664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
531664d6f86SAndrea Mayer 	int vrf_ifindex;
532664d6f86SAndrea Mayer 	struct net *net;
533664d6f86SAndrea Mayer 
534664d6f86SAndrea Mayer 	net = fib6_config_get_net(cfg);
535664d6f86SAndrea Mayer 
536664d6f86SAndrea Mayer 	/* note that vrf_table was already set by parse_nla_vrftable() */
537664d6f86SAndrea Mayer 	vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net,
538664d6f86SAndrea Mayer 							info->vrf_table);
539664d6f86SAndrea Mayer 	if (vrf_ifindex < 0) {
540664d6f86SAndrea Mayer 		if (vrf_ifindex == -EPERM) {
541664d6f86SAndrea Mayer 			NL_SET_ERR_MSG(extack,
542664d6f86SAndrea Mayer 				       "Strict mode for VRF is disabled");
543664d6f86SAndrea Mayer 		} else if (vrf_ifindex == -ENODEV) {
544664d6f86SAndrea Mayer 			NL_SET_ERR_MSG(extack,
545664d6f86SAndrea Mayer 				       "Table has no associated VRF device");
546664d6f86SAndrea Mayer 		} else {
547664d6f86SAndrea Mayer 			pr_debug("seg6local: SRv6 End.DT* creation error=%d\n",
548664d6f86SAndrea Mayer 				 vrf_ifindex);
549664d6f86SAndrea Mayer 		}
550664d6f86SAndrea Mayer 
551664d6f86SAndrea Mayer 		return vrf_ifindex;
552664d6f86SAndrea Mayer 	}
553664d6f86SAndrea Mayer 
554664d6f86SAndrea Mayer 	info->net = net;
555664d6f86SAndrea Mayer 	info->vrf_ifindex = vrf_ifindex;
556664d6f86SAndrea Mayer 
557664d6f86SAndrea Mayer 	info->family = family;
558664d6f86SAndrea Mayer 	info->mode = DT_VRF_MODE;
559664d6f86SAndrea Mayer 
560664d6f86SAndrea Mayer 	return 0;
561664d6f86SAndrea Mayer }
562664d6f86SAndrea Mayer 
563664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and
564664d6f86SAndrea Mayer  * routes the IPv4/IPv6 packet by looking at the configured routing table.
565664d6f86SAndrea Mayer  *
566664d6f86SAndrea Mayer  * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment
567664d6f86SAndrea Mayer  * Routing Header packets) from several interfaces and the outer IPv6
568664d6f86SAndrea Mayer  * destination address (DA) is used for retrieving the specific instance of the
569664d6f86SAndrea Mayer  * End.DT4/DT6 behavior that should process the packets.
570664d6f86SAndrea Mayer  *
571664d6f86SAndrea Mayer  * However, the inner IPv4/IPv6 packet is not really bound to any receiving
572664d6f86SAndrea Mayer  * interface and thus the End.DT4/DT6 sets the VRF (associated with the
573664d6f86SAndrea Mayer  * corresponding routing table) as the *receiving* interface.
574664d6f86SAndrea Mayer  * In other words, the End.DT4/DT6 processes a packet as if it has been received
575664d6f86SAndrea Mayer  * directly by the VRF (and not by one of its slave devices, if any).
576664d6f86SAndrea Mayer  * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in
577664d6f86SAndrea Mayer  * according to the routing table configured by the End.DT4/DT6 instance.
578664d6f86SAndrea Mayer  *
579664d6f86SAndrea Mayer  * This design allows you to get some interesting features like:
580664d6f86SAndrea Mayer  *  1) the statistics on rx packets;
581664d6f86SAndrea Mayer  *  2) the possibility to install a packet sniffer on the receiving interface
582664d6f86SAndrea Mayer  *     (the VRF one) for looking at the incoming packets;
583664d6f86SAndrea Mayer  *  3) the possibility to leverage the netfilter prerouting hook for the inner
584664d6f86SAndrea Mayer  *     IPv4 packet.
585664d6f86SAndrea Mayer  *
586664d6f86SAndrea Mayer  * This function returns:
587664d6f86SAndrea Mayer  *  - the sk_buff* when the VRF rcv handler has processed the packet correctly;
588664d6f86SAndrea Mayer  *  - NULL when the skb is consumed by the VRF rcv handler;
589664d6f86SAndrea Mayer  *  - a pointer which encodes a negative error number in case of error.
590664d6f86SAndrea Mayer  *    Note that in this case, the function takes care of freeing the skb.
591664d6f86SAndrea Mayer  */
592664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family,
593664d6f86SAndrea Mayer 				      struct net_device *dev)
594664d6f86SAndrea Mayer {
595664d6f86SAndrea Mayer 	/* based on l3mdev_ip_rcv; we are only interested in the master */
596664d6f86SAndrea Mayer 	if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev)))
597664d6f86SAndrea Mayer 		goto drop;
598664d6f86SAndrea Mayer 
599664d6f86SAndrea Mayer 	if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv))
600664d6f86SAndrea Mayer 		goto drop;
601664d6f86SAndrea Mayer 
602664d6f86SAndrea Mayer 	/* the decap packet IPv4/IPv6 does not come with any mac header info.
603664d6f86SAndrea Mayer 	 * We must unset the mac header to allow the VRF device to rebuild it,
604664d6f86SAndrea Mayer 	 * just in case there is a sniffer attached on the device.
605664d6f86SAndrea Mayer 	 */
606664d6f86SAndrea Mayer 	skb_unset_mac_header(skb);
607664d6f86SAndrea Mayer 
608664d6f86SAndrea Mayer 	skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family);
609664d6f86SAndrea Mayer 	if (!skb)
610664d6f86SAndrea Mayer 		/* the skb buffer was consumed by the handler */
611664d6f86SAndrea Mayer 		return NULL;
612664d6f86SAndrea Mayer 
613664d6f86SAndrea Mayer 	/* when a packet is received by a VRF or by one of its slaves, the
614664d6f86SAndrea Mayer 	 * master device reference is set into the skb.
615664d6f86SAndrea Mayer 	 */
616664d6f86SAndrea Mayer 	if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex))
617664d6f86SAndrea Mayer 		goto drop;
618664d6f86SAndrea Mayer 
619664d6f86SAndrea Mayer 	return skb;
620664d6f86SAndrea Mayer 
621664d6f86SAndrea Mayer drop:
622664d6f86SAndrea Mayer 	kfree_skb(skb);
623664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
624664d6f86SAndrea Mayer }
625664d6f86SAndrea Mayer 
626664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb,
627664d6f86SAndrea Mayer 					     struct seg6_end_dt_info *info)
628664d6f86SAndrea Mayer {
629664d6f86SAndrea Mayer 	int vrf_ifindex = info->vrf_ifindex;
630664d6f86SAndrea Mayer 	struct net *net = info->net;
631664d6f86SAndrea Mayer 
632664d6f86SAndrea Mayer 	if (unlikely(vrf_ifindex < 0))
633664d6f86SAndrea Mayer 		goto error;
634664d6f86SAndrea Mayer 
635664d6f86SAndrea Mayer 	if (unlikely(!net_eq(dev_net(skb->dev), net)))
636664d6f86SAndrea Mayer 		goto error;
637664d6f86SAndrea Mayer 
638664d6f86SAndrea Mayer 	return dev_get_by_index_rcu(net, vrf_ifindex);
639664d6f86SAndrea Mayer 
640664d6f86SAndrea Mayer error:
641664d6f86SAndrea Mayer 	return NULL;
642664d6f86SAndrea Mayer }
643664d6f86SAndrea Mayer 
644664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
6458b532109SAndrea Mayer 				       struct seg6_local_lwt *slwt, u16 family)
646664d6f86SAndrea Mayer {
647664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
648664d6f86SAndrea Mayer 	struct net_device *vrf;
6498b532109SAndrea Mayer 	__be16 protocol;
6508b532109SAndrea Mayer 	int hdrlen;
651664d6f86SAndrea Mayer 
652664d6f86SAndrea Mayer 	vrf = end_dt_get_vrf_rcu(skb, info);
653664d6f86SAndrea Mayer 	if (unlikely(!vrf))
654664d6f86SAndrea Mayer 		goto drop;
655664d6f86SAndrea Mayer 
6568b532109SAndrea Mayer 	switch (family) {
6578b532109SAndrea Mayer 	case AF_INET:
6588b532109SAndrea Mayer 		protocol = htons(ETH_P_IP);
6598b532109SAndrea Mayer 		hdrlen = sizeof(struct iphdr);
6608b532109SAndrea Mayer 		break;
6618b532109SAndrea Mayer 	case AF_INET6:
6628b532109SAndrea Mayer 		protocol = htons(ETH_P_IPV6);
6638b532109SAndrea Mayer 		hdrlen = sizeof(struct ipv6hdr);
6648b532109SAndrea Mayer 		break;
6658b532109SAndrea Mayer 	case AF_UNSPEC:
6668b532109SAndrea Mayer 		fallthrough;
6678b532109SAndrea Mayer 	default:
6688b532109SAndrea Mayer 		goto drop;
6698b532109SAndrea Mayer 	}
6708b532109SAndrea Mayer 
6718b532109SAndrea Mayer 	if (unlikely(info->family != AF_UNSPEC && info->family != family)) {
6728b532109SAndrea Mayer 		pr_warn_once("seg6local: SRv6 End.DT* family mismatch");
6738b532109SAndrea Mayer 		goto drop;
6748b532109SAndrea Mayer 	}
6758b532109SAndrea Mayer 
6768b532109SAndrea Mayer 	skb->protocol = protocol;
677664d6f86SAndrea Mayer 
678664d6f86SAndrea Mayer 	skb_dst_drop(skb);
679664d6f86SAndrea Mayer 
6808b532109SAndrea Mayer 	skb_set_transport_header(skb, hdrlen);
681*7a3f5b0dSRyoga Saito 	nf_reset_ct(skb);
682664d6f86SAndrea Mayer 
6838b532109SAndrea Mayer 	return end_dt_vrf_rcv(skb, family, vrf);
684664d6f86SAndrea Mayer 
685664d6f86SAndrea Mayer drop:
686664d6f86SAndrea Mayer 	kfree_skb(skb);
687664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
688664d6f86SAndrea Mayer }
689664d6f86SAndrea Mayer 
690664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb,
691664d6f86SAndrea Mayer 				struct seg6_local_lwt *slwt)
692664d6f86SAndrea Mayer {
693664d6f86SAndrea Mayer 	struct iphdr *iph;
694664d6f86SAndrea Mayer 	int err;
695664d6f86SAndrea Mayer 
696664d6f86SAndrea Mayer 	if (!decap_and_validate(skb, IPPROTO_IPIP))
697664d6f86SAndrea Mayer 		goto drop;
698664d6f86SAndrea Mayer 
699664d6f86SAndrea Mayer 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
700664d6f86SAndrea Mayer 		goto drop;
701664d6f86SAndrea Mayer 
7028b532109SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt, AF_INET);
703664d6f86SAndrea Mayer 	if (!skb)
704664d6f86SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
705664d6f86SAndrea Mayer 		return 0;
706664d6f86SAndrea Mayer 
707664d6f86SAndrea Mayer 	if (IS_ERR(skb))
708664d6f86SAndrea Mayer 		return PTR_ERR(skb);
709664d6f86SAndrea Mayer 
710664d6f86SAndrea Mayer 	iph = ip_hdr(skb);
711664d6f86SAndrea Mayer 
712664d6f86SAndrea Mayer 	err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev);
713664d6f86SAndrea Mayer 	if (unlikely(err))
714664d6f86SAndrea Mayer 		goto drop;
715664d6f86SAndrea Mayer 
716664d6f86SAndrea Mayer 	return dst_input(skb);
717664d6f86SAndrea Mayer 
718664d6f86SAndrea Mayer drop:
719664d6f86SAndrea Mayer 	kfree_skb(skb);
720664d6f86SAndrea Mayer 	return -EINVAL;
721664d6f86SAndrea Mayer }
722664d6f86SAndrea Mayer 
723664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg,
724664d6f86SAndrea Mayer 			      struct netlink_ext_ack *extack)
725664d6f86SAndrea Mayer {
726664d6f86SAndrea Mayer 	return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack);
727664d6f86SAndrea Mayer }
72820a081b7SAndrea Mayer 
72920a081b7SAndrea Mayer static enum
73020a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt)
73120a081b7SAndrea Mayer {
73220a081b7SAndrea Mayer 	unsigned long parsed_optattrs = slwt->parsed_optattrs;
73320a081b7SAndrea Mayer 	bool legacy, vrfmode;
73420a081b7SAndrea Mayer 
735300a0fd8SAndrea Mayer 	legacy	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE));
736300a0fd8SAndrea Mayer 	vrfmode	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE));
73720a081b7SAndrea Mayer 
73820a081b7SAndrea Mayer 	if (!(legacy ^ vrfmode))
73920a081b7SAndrea Mayer 		/* both are absent or present: invalid DT6 mode */
74020a081b7SAndrea Mayer 		return DT_INVALID_MODE;
74120a081b7SAndrea Mayer 
74220a081b7SAndrea Mayer 	return legacy ? DT_LEGACY_MODE : DT_VRF_MODE;
74320a081b7SAndrea Mayer }
74420a081b7SAndrea Mayer 
74520a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt)
74620a081b7SAndrea Mayer {
74720a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
74820a081b7SAndrea Mayer 
74920a081b7SAndrea Mayer 	return info->mode;
75020a081b7SAndrea Mayer }
75120a081b7SAndrea Mayer 
75220a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg,
75320a081b7SAndrea Mayer 			      struct netlink_ext_ack *extack)
75420a081b7SAndrea Mayer {
75520a081b7SAndrea Mayer 	enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt);
75620a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
75720a081b7SAndrea Mayer 
75820a081b7SAndrea Mayer 	switch (mode) {
75920a081b7SAndrea Mayer 	case DT_LEGACY_MODE:
76020a081b7SAndrea Mayer 		info->mode = DT_LEGACY_MODE;
76120a081b7SAndrea Mayer 		return 0;
76220a081b7SAndrea Mayer 	case DT_VRF_MODE:
76320a081b7SAndrea Mayer 		return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack);
76420a081b7SAndrea Mayer 	default:
76520a081b7SAndrea Mayer 		NL_SET_ERR_MSG(extack, "table or vrftable must be specified");
76620a081b7SAndrea Mayer 		return -EINVAL;
76720a081b7SAndrea Mayer 	}
76820a081b7SAndrea Mayer }
769664d6f86SAndrea Mayer #endif
770664d6f86SAndrea Mayer 
771891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb,
772891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
773891ef8ddSDavid Lebrun {
774891ef8ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPV6))
775891ef8ddSDavid Lebrun 		goto drop;
776891ef8ddSDavid Lebrun 
777891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
778891ef8ddSDavid Lebrun 		goto drop;
779891ef8ddSDavid Lebrun 
78020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
78120a081b7SAndrea Mayer 	if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE)
78220a081b7SAndrea Mayer 		goto legacy_mode;
78320a081b7SAndrea Mayer 
78420a081b7SAndrea Mayer 	/* DT6_VRF_MODE */
7858b532109SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt, AF_INET6);
78620a081b7SAndrea Mayer 	if (!skb)
78720a081b7SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
78820a081b7SAndrea Mayer 		return 0;
78920a081b7SAndrea Mayer 
79020a081b7SAndrea Mayer 	if (IS_ERR(skb))
79120a081b7SAndrea Mayer 		return PTR_ERR(skb);
79220a081b7SAndrea Mayer 
79320a081b7SAndrea Mayer 	/* note: this time we do not need to specify the table because the VRF
79420a081b7SAndrea Mayer 	 * takes care of selecting the correct table.
79520a081b7SAndrea Mayer 	 */
79620a081b7SAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, 0, true);
79720a081b7SAndrea Mayer 
79820a081b7SAndrea Mayer 	return dst_input(skb);
79920a081b7SAndrea Mayer 
80020a081b7SAndrea Mayer legacy_mode:
80120a081b7SAndrea Mayer #endif
802c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
803c71644d0SAndrea Mayer 
804fd1fef0cSAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, slwt->table, true);
805891ef8ddSDavid Lebrun 
806891ef8ddSDavid Lebrun 	return dst_input(skb);
807891ef8ddSDavid Lebrun 
808891ef8ddSDavid Lebrun drop:
809891ef8ddSDavid Lebrun 	kfree_skb(skb);
810891ef8ddSDavid Lebrun 	return -EINVAL;
811891ef8ddSDavid Lebrun }
812891ef8ddSDavid Lebrun 
8138b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
8148b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg,
8158b532109SAndrea Mayer 			       struct netlink_ext_ack *extack)
8168b532109SAndrea Mayer {
8178b532109SAndrea Mayer 	return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack);
8188b532109SAndrea Mayer }
8198b532109SAndrea Mayer 
8208b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb,
8218b532109SAndrea Mayer 				 struct seg6_local_lwt *slwt)
8228b532109SAndrea Mayer {
8238b532109SAndrea Mayer 	unsigned int off = 0;
8248b532109SAndrea Mayer 	int nexthdr;
8258b532109SAndrea Mayer 
8268b532109SAndrea Mayer 	nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL);
8278b532109SAndrea Mayer 	if (unlikely(nexthdr < 0))
8288b532109SAndrea Mayer 		goto drop;
8298b532109SAndrea Mayer 
8308b532109SAndrea Mayer 	switch (nexthdr) {
8318b532109SAndrea Mayer 	case IPPROTO_IPIP:
8328b532109SAndrea Mayer 		return input_action_end_dt4(skb, slwt);
8338b532109SAndrea Mayer 	case IPPROTO_IPV6:
8348b532109SAndrea Mayer 		return input_action_end_dt6(skb, slwt);
8358b532109SAndrea Mayer 	}
8368b532109SAndrea Mayer 
8378b532109SAndrea Mayer drop:
8388b532109SAndrea Mayer 	kfree_skb(skb);
8398b532109SAndrea Mayer 	return -EINVAL;
8408b532109SAndrea Mayer }
8418b532109SAndrea Mayer #endif
8428b532109SAndrea Mayer 
843140f04c3SDavid Lebrun /* push an SRH on top of the current one */
844140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
845140f04c3SDavid Lebrun {
846140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
847140f04c3SDavid Lebrun 	int err = -EINVAL;
848140f04c3SDavid Lebrun 
849140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
850140f04c3SDavid Lebrun 	if (!srh)
851140f04c3SDavid Lebrun 		goto drop;
852140f04c3SDavid Lebrun 
853140f04c3SDavid Lebrun 	err = seg6_do_srh_inline(skb, slwt->srh);
854140f04c3SDavid Lebrun 	if (err)
855140f04c3SDavid Lebrun 		goto drop;
856140f04c3SDavid Lebrun 
857140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
858140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
859140f04c3SDavid Lebrun 
8601c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
861140f04c3SDavid Lebrun 
862140f04c3SDavid Lebrun 	return dst_input(skb);
863140f04c3SDavid Lebrun 
864140f04c3SDavid Lebrun drop:
865140f04c3SDavid Lebrun 	kfree_skb(skb);
866140f04c3SDavid Lebrun 	return err;
867140f04c3SDavid Lebrun }
868140f04c3SDavid Lebrun 
869140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */
870140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb,
871140f04c3SDavid Lebrun 				     struct seg6_local_lwt *slwt)
872140f04c3SDavid Lebrun {
873140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
874140f04c3SDavid Lebrun 	int err = -EINVAL;
875140f04c3SDavid Lebrun 
876140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
877140f04c3SDavid Lebrun 	if (!srh)
878140f04c3SDavid Lebrun 		goto drop;
879140f04c3SDavid Lebrun 
880d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
881140f04c3SDavid Lebrun 
882140f04c3SDavid Lebrun 	skb_reset_inner_headers(skb);
883140f04c3SDavid Lebrun 	skb->encapsulation = 1;
884140f04c3SDavid Lebrun 
88532d99d0bSDavid Lebrun 	err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6);
886140f04c3SDavid Lebrun 	if (err)
887140f04c3SDavid Lebrun 		goto drop;
888140f04c3SDavid Lebrun 
889140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
890140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
891140f04c3SDavid Lebrun 
8921c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
893140f04c3SDavid Lebrun 
894140f04c3SDavid Lebrun 	return dst_input(skb);
895140f04c3SDavid Lebrun 
896140f04c3SDavid Lebrun drop:
897140f04c3SDavid Lebrun 	kfree_skb(skb);
898140f04c3SDavid Lebrun 	return err;
899140f04c3SDavid Lebrun }
900140f04c3SDavid Lebrun 
901fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
902fe94cc29SMathieu Xhonneux 
903486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
904486cdf21SMathieu Xhonneux {
905486cdf21SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
906486cdf21SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
907486cdf21SMathieu Xhonneux 	struct ipv6_sr_hdr *srh = srh_state->srh;
908486cdf21SMathieu Xhonneux 
909486cdf21SMathieu Xhonneux 	if (unlikely(srh == NULL))
910486cdf21SMathieu Xhonneux 		return false;
911486cdf21SMathieu Xhonneux 
912486cdf21SMathieu Xhonneux 	if (unlikely(!srh_state->valid)) {
913486cdf21SMathieu Xhonneux 		if ((srh_state->hdrlen & 7) != 0)
914486cdf21SMathieu Xhonneux 			return false;
915486cdf21SMathieu Xhonneux 
916486cdf21SMathieu Xhonneux 		srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
917bb986a50SAhmed Abdelsalam 		if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
918486cdf21SMathieu Xhonneux 			return false;
919486cdf21SMathieu Xhonneux 
920486cdf21SMathieu Xhonneux 		srh_state->valid = true;
921486cdf21SMathieu Xhonneux 	}
922486cdf21SMathieu Xhonneux 
923486cdf21SMathieu Xhonneux 	return true;
924486cdf21SMathieu Xhonneux }
925486cdf21SMathieu Xhonneux 
926004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb,
927004d4b27SMathieu Xhonneux 				struct seg6_local_lwt *slwt)
928004d4b27SMathieu Xhonneux {
929004d4b27SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
930004d4b27SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
931004d4b27SMathieu Xhonneux 	struct ipv6_sr_hdr *srh;
932004d4b27SMathieu Xhonneux 	int ret;
933004d4b27SMathieu Xhonneux 
934004d4b27SMathieu Xhonneux 	srh = get_and_validate_srh(skb);
935486cdf21SMathieu Xhonneux 	if (!srh) {
936486cdf21SMathieu Xhonneux 		kfree_skb(skb);
937486cdf21SMathieu Xhonneux 		return -EINVAL;
938486cdf21SMathieu Xhonneux 	}
939004d4b27SMathieu Xhonneux 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
940004d4b27SMathieu Xhonneux 
941004d4b27SMathieu Xhonneux 	/* preempt_disable is needed to protect the per-CPU buffer srh_state,
942004d4b27SMathieu Xhonneux 	 * which is also accessed by the bpf_lwt_seg6_* helpers
943004d4b27SMathieu Xhonneux 	 */
944004d4b27SMathieu Xhonneux 	preempt_disable();
945486cdf21SMathieu Xhonneux 	srh_state->srh = srh;
946004d4b27SMathieu Xhonneux 	srh_state->hdrlen = srh->hdrlen << 3;
947486cdf21SMathieu Xhonneux 	srh_state->valid = true;
948004d4b27SMathieu Xhonneux 
949004d4b27SMathieu Xhonneux 	rcu_read_lock();
950004d4b27SMathieu Xhonneux 	bpf_compute_data_pointers(skb);
951004d4b27SMathieu Xhonneux 	ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb);
952004d4b27SMathieu Xhonneux 	rcu_read_unlock();
953004d4b27SMathieu Xhonneux 
954004d4b27SMathieu Xhonneux 	switch (ret) {
955004d4b27SMathieu Xhonneux 	case BPF_OK:
956004d4b27SMathieu Xhonneux 	case BPF_REDIRECT:
957004d4b27SMathieu Xhonneux 		break;
958004d4b27SMathieu Xhonneux 	case BPF_DROP:
959004d4b27SMathieu Xhonneux 		goto drop;
960004d4b27SMathieu Xhonneux 	default:
961004d4b27SMathieu Xhonneux 		pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret);
962004d4b27SMathieu Xhonneux 		goto drop;
963004d4b27SMathieu Xhonneux 	}
964004d4b27SMathieu Xhonneux 
965486cdf21SMathieu Xhonneux 	if (srh_state->srh && !seg6_bpf_has_valid_srh(skb))
966004d4b27SMathieu Xhonneux 		goto drop;
967004d4b27SMathieu Xhonneux 
968486cdf21SMathieu Xhonneux 	preempt_enable();
969004d4b27SMathieu Xhonneux 	if (ret != BPF_REDIRECT)
970004d4b27SMathieu Xhonneux 		seg6_lookup_nexthop(skb, NULL, 0);
971004d4b27SMathieu Xhonneux 
972004d4b27SMathieu Xhonneux 	return dst_input(skb);
973004d4b27SMathieu Xhonneux 
974004d4b27SMathieu Xhonneux drop:
975486cdf21SMathieu Xhonneux 	preempt_enable();
976004d4b27SMathieu Xhonneux 	kfree_skb(skb);
977004d4b27SMathieu Xhonneux 	return -EINVAL;
978004d4b27SMathieu Xhonneux }
979004d4b27SMathieu Xhonneux 
980d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = {
981d1df6fd8SDavid Lebrun 	{
982d1df6fd8SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END,
983d1df6fd8SDavid Lebrun 		.attrs		= 0,
98494604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
985140f04c3SDavid Lebrun 		.input		= input_action_end,
986d1df6fd8SDavid Lebrun 	},
987140f04c3SDavid Lebrun 	{
988140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_X,
989300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
99094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
991140f04c3SDavid Lebrun 		.input		= input_action_end_x,
992140f04c3SDavid Lebrun 	},
993140f04c3SDavid Lebrun 	{
994891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_T,
995300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
99694604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
997891ef8ddSDavid Lebrun 		.input		= input_action_end_t,
998891ef8ddSDavid Lebrun 	},
999891ef8ddSDavid Lebrun 	{
1000891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX2,
1001300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_OIF),
100294604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1003891ef8ddSDavid Lebrun 		.input		= input_action_end_dx2,
1004891ef8ddSDavid Lebrun 	},
1005891ef8ddSDavid Lebrun 	{
1006140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX6,
1007300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
100894604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1009140f04c3SDavid Lebrun 		.input		= input_action_end_dx6,
1010140f04c3SDavid Lebrun 	},
1011140f04c3SDavid Lebrun 	{
1012891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX4,
1013300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH4),
101494604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1015891ef8ddSDavid Lebrun 		.input		= input_action_end_dx4,
1016891ef8ddSDavid Lebrun 	},
1017891ef8ddSDavid Lebrun 	{
1018664d6f86SAndrea Mayer 		.action		= SEG6_LOCAL_ACTION_END_DT4,
1019300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
102094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1021664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
1022664d6f86SAndrea Mayer 		.input		= input_action_end_dt4,
1023664d6f86SAndrea Mayer 		.slwt_ops	= {
1024664d6f86SAndrea Mayer 					.build_state = seg6_end_dt4_build,
1025664d6f86SAndrea Mayer 				  },
1026664d6f86SAndrea Mayer #endif
1027664d6f86SAndrea Mayer 	},
1028664d6f86SAndrea Mayer 	{
1029891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DT6,
103020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
103120a081b7SAndrea Mayer 		.attrs		= 0,
103294604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS		|
103394604548SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_TABLE) |
1034300a0fd8SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
103520a081b7SAndrea Mayer 		.slwt_ops	= {
103620a081b7SAndrea Mayer 					.build_state = seg6_end_dt6_build,
103720a081b7SAndrea Mayer 				  },
103820a081b7SAndrea Mayer #else
1039300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
104094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
104120a081b7SAndrea Mayer #endif
1042891ef8ddSDavid Lebrun 		.input		= input_action_end_dt6,
1043891ef8ddSDavid Lebrun 	},
1044891ef8ddSDavid Lebrun 	{
10458b532109SAndrea Mayer 		.action		= SEG6_LOCAL_ACTION_END_DT46,
10468b532109SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
10478b532109SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
10488b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
10498b532109SAndrea Mayer 		.input		= input_action_end_dt46,
10508b532109SAndrea Mayer 		.slwt_ops	= {
10518b532109SAndrea Mayer 					.build_state = seg6_end_dt46_build,
10528b532109SAndrea Mayer 				  },
10538b532109SAndrea Mayer #endif
10548b532109SAndrea Mayer 	},
10558b532109SAndrea Mayer 	{
1056140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6,
1057300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
105894604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1059140f04c3SDavid Lebrun 		.input		= input_action_end_b6,
1060140f04c3SDavid Lebrun 	},
1061140f04c3SDavid Lebrun 	{
1062140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6_ENCAP,
1063300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
106494604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1065140f04c3SDavid Lebrun 		.input		= input_action_end_b6_encap,
1066140f04c3SDavid Lebrun 		.static_headroom	= sizeof(struct ipv6hdr),
1067004d4b27SMathieu Xhonneux 	},
1068004d4b27SMathieu Xhonneux 	{
1069004d4b27SMathieu Xhonneux 		.action		= SEG6_LOCAL_ACTION_END_BPF,
1070300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_BPF),
107194604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1072004d4b27SMathieu Xhonneux 		.input		= input_action_end_bpf,
1073004d4b27SMathieu Xhonneux 	},
1074004d4b27SMathieu Xhonneux 
1075d1df6fd8SDavid Lebrun };
1076d1df6fd8SDavid Lebrun 
1077d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action)
1078d1df6fd8SDavid Lebrun {
1079d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1080d1df6fd8SDavid Lebrun 	int i, count;
1081d1df6fd8SDavid Lebrun 
1082709af180SColin Ian King 	count = ARRAY_SIZE(seg6_action_table);
1083d1df6fd8SDavid Lebrun 	for (i = 0; i < count; i++) {
1084d1df6fd8SDavid Lebrun 		desc = &seg6_action_table[i];
1085d1df6fd8SDavid Lebrun 		if (desc->action == action)
1086d1df6fd8SDavid Lebrun 			return desc;
1087d1df6fd8SDavid Lebrun 	}
1088d1df6fd8SDavid Lebrun 
1089d1df6fd8SDavid Lebrun 	return NULL;
1090d1df6fd8SDavid Lebrun }
1091d1df6fd8SDavid Lebrun 
109294604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt)
109394604548SAndrea Mayer {
109494604548SAndrea Mayer 	return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS;
109594604548SAndrea Mayer }
109694604548SAndrea Mayer 
109794604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt,
109894604548SAndrea Mayer 				       unsigned int len, int err)
109994604548SAndrea Mayer {
110094604548SAndrea Mayer 	struct pcpu_seg6_local_counters *pcounters;
110194604548SAndrea Mayer 
110294604548SAndrea Mayer 	pcounters = this_cpu_ptr(slwt->pcpu_counters);
110394604548SAndrea Mayer 	u64_stats_update_begin(&pcounters->syncp);
110494604548SAndrea Mayer 
110594604548SAndrea Mayer 	if (likely(!err)) {
110694604548SAndrea Mayer 		u64_stats_inc(&pcounters->packets);
110794604548SAndrea Mayer 		u64_stats_add(&pcounters->bytes, len);
110894604548SAndrea Mayer 	} else {
110994604548SAndrea Mayer 		u64_stats_inc(&pcounters->errors);
111094604548SAndrea Mayer 	}
111194604548SAndrea Mayer 
111294604548SAndrea Mayer 	u64_stats_update_end(&pcounters->syncp);
111394604548SAndrea Mayer }
111494604548SAndrea Mayer 
1115*7a3f5b0dSRyoga Saito static int seg6_local_input_core(struct net *net, struct sock *sk,
1116*7a3f5b0dSRyoga Saito 				 struct sk_buff *skb)
1117d1df6fd8SDavid Lebrun {
1118d1df6fd8SDavid Lebrun 	struct dst_entry *orig_dst = skb_dst(skb);
1119d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1120d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
112194604548SAndrea Mayer 	unsigned int len = skb->len;
112294604548SAndrea Mayer 	int rc;
1123d1df6fd8SDavid Lebrun 
1124d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
1125d1df6fd8SDavid Lebrun 	desc = slwt->desc;
1126d1df6fd8SDavid Lebrun 
112794604548SAndrea Mayer 	rc = desc->input(skb, slwt);
112894604548SAndrea Mayer 
112994604548SAndrea Mayer 	if (!seg6_lwtunnel_counters_enabled(slwt))
113094604548SAndrea Mayer 		return rc;
113194604548SAndrea Mayer 
113294604548SAndrea Mayer 	seg6_local_update_counters(slwt, len, rc);
113394604548SAndrea Mayer 
113494604548SAndrea Mayer 	return rc;
1135d1df6fd8SDavid Lebrun }
1136d1df6fd8SDavid Lebrun 
1137*7a3f5b0dSRyoga Saito static int seg6_local_input(struct sk_buff *skb)
1138*7a3f5b0dSRyoga Saito {
1139*7a3f5b0dSRyoga Saito 	if (skb->protocol != htons(ETH_P_IPV6)) {
1140*7a3f5b0dSRyoga Saito 		kfree_skb(skb);
1141*7a3f5b0dSRyoga Saito 		return -EINVAL;
1142*7a3f5b0dSRyoga Saito 	}
1143*7a3f5b0dSRyoga Saito 
1144*7a3f5b0dSRyoga Saito 	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
1145*7a3f5b0dSRyoga Saito 		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,
1146*7a3f5b0dSRyoga Saito 			       dev_net(skb->dev), NULL, skb, skb->dev, NULL,
1147*7a3f5b0dSRyoga Saito 			       seg6_local_input_core);
1148*7a3f5b0dSRyoga Saito 
1149*7a3f5b0dSRyoga Saito 	return seg6_local_input_core(dev_net(skb->dev), NULL, skb);
1150*7a3f5b0dSRyoga Saito }
1151*7a3f5b0dSRyoga Saito 
1152d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
1153d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_ACTION]	= { .type = NLA_U32 },
1154d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .type = NLA_BINARY },
1155d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .type = NLA_U32 },
1156664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .type = NLA_U32 },
1157d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .type = NLA_BINARY,
1158d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in_addr) },
1159d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .type = NLA_BINARY,
1160d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in6_addr) },
1161d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .type = NLA_U32 },
1162d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .type = NLA_U32 },
1163004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .type = NLA_NESTED },
116494604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .type = NLA_NESTED },
1165d1df6fd8SDavid Lebrun };
1166d1df6fd8SDavid Lebrun 
11672d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
11682d9cc60aSDavid Lebrun {
11692d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
11702d9cc60aSDavid Lebrun 	int len;
11712d9cc60aSDavid Lebrun 
11722d9cc60aSDavid Lebrun 	srh = nla_data(attrs[SEG6_LOCAL_SRH]);
11732d9cc60aSDavid Lebrun 	len = nla_len(attrs[SEG6_LOCAL_SRH]);
11742d9cc60aSDavid Lebrun 
11752d9cc60aSDavid Lebrun 	/* SRH must contain at least one segment */
11762d9cc60aSDavid Lebrun 	if (len < sizeof(*srh) + sizeof(struct in6_addr))
11772d9cc60aSDavid Lebrun 		return -EINVAL;
11782d9cc60aSDavid Lebrun 
1179bb986a50SAhmed Abdelsalam 	if (!seg6_validate_srh(srh, len, false))
11802d9cc60aSDavid Lebrun 		return -EINVAL;
11812d9cc60aSDavid Lebrun 
11827fa41efaSYueHaibing 	slwt->srh = kmemdup(srh, len, GFP_KERNEL);
11832d9cc60aSDavid Lebrun 	if (!slwt->srh)
11842d9cc60aSDavid Lebrun 		return -ENOMEM;
11852d9cc60aSDavid Lebrun 
11862d9cc60aSDavid Lebrun 	slwt->headroom += len;
11872d9cc60aSDavid Lebrun 
11882d9cc60aSDavid Lebrun 	return 0;
11892d9cc60aSDavid Lebrun }
11902d9cc60aSDavid Lebrun 
11912d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt)
11922d9cc60aSDavid Lebrun {
11932d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
11942d9cc60aSDavid Lebrun 	struct nlattr *nla;
11952d9cc60aSDavid Lebrun 	int len;
11962d9cc60aSDavid Lebrun 
11972d9cc60aSDavid Lebrun 	srh = slwt->srh;
11982d9cc60aSDavid Lebrun 	len = (srh->hdrlen + 1) << 3;
11992d9cc60aSDavid Lebrun 
12002d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_SRH, len);
12012d9cc60aSDavid Lebrun 	if (!nla)
12022d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12032d9cc60aSDavid Lebrun 
12042d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), srh, len);
12052d9cc60aSDavid Lebrun 
12062d9cc60aSDavid Lebrun 	return 0;
12072d9cc60aSDavid Lebrun }
12082d9cc60aSDavid Lebrun 
12092d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12102d9cc60aSDavid Lebrun {
12112d9cc60aSDavid Lebrun 	int len = (a->srh->hdrlen + 1) << 3;
12122d9cc60aSDavid Lebrun 
12132d9cc60aSDavid Lebrun 	if (len != ((b->srh->hdrlen + 1) << 3))
12142d9cc60aSDavid Lebrun 		return 1;
12152d9cc60aSDavid Lebrun 
12162d9cc60aSDavid Lebrun 	return memcmp(a->srh, b->srh, len);
12172d9cc60aSDavid Lebrun }
12182d9cc60aSDavid Lebrun 
1219964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt)
1220964adce5SAndrea Mayer {
1221964adce5SAndrea Mayer 	kfree(slwt->srh);
1222964adce5SAndrea Mayer }
1223964adce5SAndrea Mayer 
12242d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12252d9cc60aSDavid Lebrun {
12262d9cc60aSDavid Lebrun 	slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]);
12272d9cc60aSDavid Lebrun 
12282d9cc60aSDavid Lebrun 	return 0;
12292d9cc60aSDavid Lebrun }
12302d9cc60aSDavid Lebrun 
12312d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12322d9cc60aSDavid Lebrun {
12332d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table))
12342d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12352d9cc60aSDavid Lebrun 
12362d9cc60aSDavid Lebrun 	return 0;
12372d9cc60aSDavid Lebrun }
12382d9cc60aSDavid Lebrun 
12392d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12402d9cc60aSDavid Lebrun {
12412d9cc60aSDavid Lebrun 	if (a->table != b->table)
12422d9cc60aSDavid Lebrun 		return 1;
12432d9cc60aSDavid Lebrun 
12442d9cc60aSDavid Lebrun 	return 0;
12452d9cc60aSDavid Lebrun }
12462d9cc60aSDavid Lebrun 
1247664d6f86SAndrea Mayer static struct
1248664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt)
1249664d6f86SAndrea Mayer {
1250664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
1251664d6f86SAndrea Mayer 	return &slwt->dt_info;
1252664d6f86SAndrea Mayer #else
1253664d6f86SAndrea Mayer 	return ERR_PTR(-EOPNOTSUPP);
1254664d6f86SAndrea Mayer #endif
1255664d6f86SAndrea Mayer }
1256664d6f86SAndrea Mayer 
1257664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs,
1258664d6f86SAndrea Mayer 			      struct seg6_local_lwt *slwt)
1259664d6f86SAndrea Mayer {
1260664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1261664d6f86SAndrea Mayer 
1262664d6f86SAndrea Mayer 	if (IS_ERR(info))
1263664d6f86SAndrea Mayer 		return PTR_ERR(info);
1264664d6f86SAndrea Mayer 
1265664d6f86SAndrea Mayer 	info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]);
1266664d6f86SAndrea Mayer 
1267664d6f86SAndrea Mayer 	return 0;
1268664d6f86SAndrea Mayer }
1269664d6f86SAndrea Mayer 
1270664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1271664d6f86SAndrea Mayer {
1272664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1273664d6f86SAndrea Mayer 
1274664d6f86SAndrea Mayer 	if (IS_ERR(info))
1275664d6f86SAndrea Mayer 		return PTR_ERR(info);
1276664d6f86SAndrea Mayer 
1277664d6f86SAndrea Mayer 	if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table))
1278664d6f86SAndrea Mayer 		return -EMSGSIZE;
1279664d6f86SAndrea Mayer 
1280664d6f86SAndrea Mayer 	return 0;
1281664d6f86SAndrea Mayer }
1282664d6f86SAndrea Mayer 
1283664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1284664d6f86SAndrea Mayer {
1285664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a);
1286664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b);
1287664d6f86SAndrea Mayer 
1288664d6f86SAndrea Mayer 	if (info_a->vrf_table != info_b->vrf_table)
1289664d6f86SAndrea Mayer 		return 1;
1290664d6f86SAndrea Mayer 
1291664d6f86SAndrea Mayer 	return 0;
1292664d6f86SAndrea Mayer }
1293664d6f86SAndrea Mayer 
12942d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12952d9cc60aSDavid Lebrun {
12962d9cc60aSDavid Lebrun 	memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]),
12972d9cc60aSDavid Lebrun 	       sizeof(struct in_addr));
12982d9cc60aSDavid Lebrun 
12992d9cc60aSDavid Lebrun 	return 0;
13002d9cc60aSDavid Lebrun }
13012d9cc60aSDavid Lebrun 
13022d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13032d9cc60aSDavid Lebrun {
13042d9cc60aSDavid Lebrun 	struct nlattr *nla;
13052d9cc60aSDavid Lebrun 
13062d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr));
13072d9cc60aSDavid Lebrun 	if (!nla)
13082d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13092d9cc60aSDavid Lebrun 
13102d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr));
13112d9cc60aSDavid Lebrun 
13122d9cc60aSDavid Lebrun 	return 0;
13132d9cc60aSDavid Lebrun }
13142d9cc60aSDavid Lebrun 
13152d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13162d9cc60aSDavid Lebrun {
13172d9cc60aSDavid Lebrun 	return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr));
13182d9cc60aSDavid Lebrun }
13192d9cc60aSDavid Lebrun 
13202d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt)
13212d9cc60aSDavid Lebrun {
13222d9cc60aSDavid Lebrun 	memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]),
13232d9cc60aSDavid Lebrun 	       sizeof(struct in6_addr));
13242d9cc60aSDavid Lebrun 
13252d9cc60aSDavid Lebrun 	return 0;
13262d9cc60aSDavid Lebrun }
13272d9cc60aSDavid Lebrun 
13282d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13292d9cc60aSDavid Lebrun {
13302d9cc60aSDavid Lebrun 	struct nlattr *nla;
13312d9cc60aSDavid Lebrun 
13322d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr));
13332d9cc60aSDavid Lebrun 	if (!nla)
13342d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13352d9cc60aSDavid Lebrun 
13362d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr));
13372d9cc60aSDavid Lebrun 
13382d9cc60aSDavid Lebrun 	return 0;
13392d9cc60aSDavid Lebrun }
13402d9cc60aSDavid Lebrun 
13412d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13422d9cc60aSDavid Lebrun {
13432d9cc60aSDavid Lebrun 	return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr));
13442d9cc60aSDavid Lebrun }
13452d9cc60aSDavid Lebrun 
13462d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
13472d9cc60aSDavid Lebrun {
13482d9cc60aSDavid Lebrun 	slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]);
13492d9cc60aSDavid Lebrun 
13502d9cc60aSDavid Lebrun 	return 0;
13512d9cc60aSDavid Lebrun }
13522d9cc60aSDavid Lebrun 
13532d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13542d9cc60aSDavid Lebrun {
13552d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif))
13562d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13572d9cc60aSDavid Lebrun 
13582d9cc60aSDavid Lebrun 	return 0;
13592d9cc60aSDavid Lebrun }
13602d9cc60aSDavid Lebrun 
13612d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13622d9cc60aSDavid Lebrun {
13632d9cc60aSDavid Lebrun 	if (a->iif != b->iif)
13642d9cc60aSDavid Lebrun 		return 1;
13652d9cc60aSDavid Lebrun 
13662d9cc60aSDavid Lebrun 	return 0;
13672d9cc60aSDavid Lebrun }
13682d9cc60aSDavid Lebrun 
13692d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
13702d9cc60aSDavid Lebrun {
13712d9cc60aSDavid Lebrun 	slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]);
13722d9cc60aSDavid Lebrun 
13732d9cc60aSDavid Lebrun 	return 0;
13742d9cc60aSDavid Lebrun }
13752d9cc60aSDavid Lebrun 
13762d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13772d9cc60aSDavid Lebrun {
13782d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif))
13792d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13802d9cc60aSDavid Lebrun 
13812d9cc60aSDavid Lebrun 	return 0;
13822d9cc60aSDavid Lebrun }
13832d9cc60aSDavid Lebrun 
13842d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13852d9cc60aSDavid Lebrun {
13862d9cc60aSDavid Lebrun 	if (a->oif != b->oif)
13872d9cc60aSDavid Lebrun 		return 1;
13882d9cc60aSDavid Lebrun 
13892d9cc60aSDavid Lebrun 	return 0;
13902d9cc60aSDavid Lebrun }
13912d9cc60aSDavid Lebrun 
1392004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256
1393004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
1394004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG]	   = { .type = NLA_U32, },
1395004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
1396004d4b27SMathieu Xhonneux 				       .len = MAX_PROG_NAME },
1397004d4b27SMathieu Xhonneux };
1398004d4b27SMathieu Xhonneux 
1399004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1400004d4b27SMathieu Xhonneux {
1401004d4b27SMathieu Xhonneux 	struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
1402004d4b27SMathieu Xhonneux 	struct bpf_prog *p;
1403004d4b27SMathieu Xhonneux 	int ret;
1404004d4b27SMathieu Xhonneux 	u32 fd;
1405004d4b27SMathieu Xhonneux 
14068cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX,
14078cb08174SJohannes Berg 					  attrs[SEG6_LOCAL_BPF],
14088cb08174SJohannes Berg 					  bpf_prog_policy, NULL);
1409004d4b27SMathieu Xhonneux 	if (ret < 0)
1410004d4b27SMathieu Xhonneux 		return ret;
1411004d4b27SMathieu Xhonneux 
1412004d4b27SMathieu Xhonneux 	if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME])
1413004d4b27SMathieu Xhonneux 		return -EINVAL;
1414004d4b27SMathieu Xhonneux 
1415004d4b27SMathieu Xhonneux 	slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL);
1416004d4b27SMathieu Xhonneux 	if (!slwt->bpf.name)
1417004d4b27SMathieu Xhonneux 		return -ENOMEM;
1418004d4b27SMathieu Xhonneux 
1419004d4b27SMathieu Xhonneux 	fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]);
1420004d4b27SMathieu Xhonneux 	p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL);
1421004d4b27SMathieu Xhonneux 	if (IS_ERR(p)) {
1422004d4b27SMathieu Xhonneux 		kfree(slwt->bpf.name);
1423004d4b27SMathieu Xhonneux 		return PTR_ERR(p);
1424004d4b27SMathieu Xhonneux 	}
1425004d4b27SMathieu Xhonneux 
1426004d4b27SMathieu Xhonneux 	slwt->bpf.prog = p;
1427004d4b27SMathieu Xhonneux 	return 0;
1428004d4b27SMathieu Xhonneux }
1429004d4b27SMathieu Xhonneux 
1430004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1431004d4b27SMathieu Xhonneux {
1432004d4b27SMathieu Xhonneux 	struct nlattr *nest;
1433004d4b27SMathieu Xhonneux 
1434004d4b27SMathieu Xhonneux 	if (!slwt->bpf.prog)
1435004d4b27SMathieu Xhonneux 		return 0;
1436004d4b27SMathieu Xhonneux 
1437ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF);
1438004d4b27SMathieu Xhonneux 	if (!nest)
1439004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1440004d4b27SMathieu Xhonneux 
1441004d4b27SMathieu Xhonneux 	if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id))
1442004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1443004d4b27SMathieu Xhonneux 
1444004d4b27SMathieu Xhonneux 	if (slwt->bpf.name &&
1445004d4b27SMathieu Xhonneux 	    nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name))
1446004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1447004d4b27SMathieu Xhonneux 
1448004d4b27SMathieu Xhonneux 	return nla_nest_end(skb, nest);
1449004d4b27SMathieu Xhonneux }
1450004d4b27SMathieu Xhonneux 
1451004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1452004d4b27SMathieu Xhonneux {
1453004d4b27SMathieu Xhonneux 	if (!a->bpf.name && !b->bpf.name)
1454004d4b27SMathieu Xhonneux 		return 0;
1455004d4b27SMathieu Xhonneux 
1456004d4b27SMathieu Xhonneux 	if (!a->bpf.name || !b->bpf.name)
1457004d4b27SMathieu Xhonneux 		return 1;
1458004d4b27SMathieu Xhonneux 
1459004d4b27SMathieu Xhonneux 	return strcmp(a->bpf.name, b->bpf.name);
1460004d4b27SMathieu Xhonneux }
1461004d4b27SMathieu Xhonneux 
1462964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt)
1463964adce5SAndrea Mayer {
1464964adce5SAndrea Mayer 	kfree(slwt->bpf.name);
1465964adce5SAndrea Mayer 	if (slwt->bpf.prog)
1466964adce5SAndrea Mayer 		bpf_prog_put(slwt->bpf.prog);
1467964adce5SAndrea Mayer }
1468964adce5SAndrea Mayer 
146994604548SAndrea Mayer static const struct
147094604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = {
147194604548SAndrea Mayer 	[SEG6_LOCAL_CNT_PACKETS]	= { .type = NLA_U64 },
147294604548SAndrea Mayer 	[SEG6_LOCAL_CNT_BYTES]		= { .type = NLA_U64 },
147394604548SAndrea Mayer 	[SEG6_LOCAL_CNT_ERRORS]		= { .type = NLA_U64 },
147494604548SAndrea Mayer };
147594604548SAndrea Mayer 
147694604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs,
147794604548SAndrea Mayer 			      struct seg6_local_lwt *slwt)
147894604548SAndrea Mayer {
147994604548SAndrea Mayer 	struct pcpu_seg6_local_counters __percpu *pcounters;
148094604548SAndrea Mayer 	struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1];
148194604548SAndrea Mayer 	int ret;
148294604548SAndrea Mayer 
148394604548SAndrea Mayer 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX,
148494604548SAndrea Mayer 					  attrs[SEG6_LOCAL_COUNTERS],
148594604548SAndrea Mayer 					  seg6_local_counters_policy, NULL);
148694604548SAndrea Mayer 	if (ret < 0)
148794604548SAndrea Mayer 		return ret;
148894604548SAndrea Mayer 
148994604548SAndrea Mayer 	/* basic support for SRv6 Behavior counters requires at least:
149094604548SAndrea Mayer 	 * packets, bytes and errors.
149194604548SAndrea Mayer 	 */
149294604548SAndrea Mayer 	if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] ||
149394604548SAndrea Mayer 	    !tb[SEG6_LOCAL_CNT_ERRORS])
149494604548SAndrea Mayer 		return -EINVAL;
149594604548SAndrea Mayer 
149694604548SAndrea Mayer 	/* counters are always zero initialized */
149794604548SAndrea Mayer 	pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL);
149894604548SAndrea Mayer 	if (!pcounters)
149994604548SAndrea Mayer 		return -ENOMEM;
150094604548SAndrea Mayer 
150194604548SAndrea Mayer 	slwt->pcpu_counters = pcounters;
150294604548SAndrea Mayer 
150394604548SAndrea Mayer 	return 0;
150494604548SAndrea Mayer }
150594604548SAndrea Mayer 
150694604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb,
150794604548SAndrea Mayer 					struct seg6_local_counters *counters)
150894604548SAndrea Mayer {
150994604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets,
151094604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
151194604548SAndrea Mayer 		return -EMSGSIZE;
151294604548SAndrea Mayer 
151394604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes,
151494604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
151594604548SAndrea Mayer 		return -EMSGSIZE;
151694604548SAndrea Mayer 
151794604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors,
151894604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
151994604548SAndrea Mayer 		return -EMSGSIZE;
152094604548SAndrea Mayer 
152194604548SAndrea Mayer 	return 0;
152294604548SAndrea Mayer }
152394604548SAndrea Mayer 
152494604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt)
152594604548SAndrea Mayer {
152694604548SAndrea Mayer 	struct seg6_local_counters counters = { 0, 0, 0 };
152794604548SAndrea Mayer 	struct nlattr *nest;
152894604548SAndrea Mayer 	int rc, i;
152994604548SAndrea Mayer 
153094604548SAndrea Mayer 	nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS);
153194604548SAndrea Mayer 	if (!nest)
153294604548SAndrea Mayer 		return -EMSGSIZE;
153394604548SAndrea Mayer 
153494604548SAndrea Mayer 	for_each_possible_cpu(i) {
153594604548SAndrea Mayer 		struct pcpu_seg6_local_counters *pcounters;
153694604548SAndrea Mayer 		u64 packets, bytes, errors;
153794604548SAndrea Mayer 		unsigned int start;
153894604548SAndrea Mayer 
153994604548SAndrea Mayer 		pcounters = per_cpu_ptr(slwt->pcpu_counters, i);
154094604548SAndrea Mayer 		do {
154194604548SAndrea Mayer 			start = u64_stats_fetch_begin_irq(&pcounters->syncp);
154294604548SAndrea Mayer 
154394604548SAndrea Mayer 			packets = u64_stats_read(&pcounters->packets);
154494604548SAndrea Mayer 			bytes = u64_stats_read(&pcounters->bytes);
154594604548SAndrea Mayer 			errors = u64_stats_read(&pcounters->errors);
154694604548SAndrea Mayer 
154794604548SAndrea Mayer 		} while (u64_stats_fetch_retry_irq(&pcounters->syncp, start));
154894604548SAndrea Mayer 
154994604548SAndrea Mayer 		counters.packets += packets;
155094604548SAndrea Mayer 		counters.bytes += bytes;
155194604548SAndrea Mayer 		counters.errors += errors;
155294604548SAndrea Mayer 	}
155394604548SAndrea Mayer 
155494604548SAndrea Mayer 	rc = seg6_local_fill_nla_counters(skb, &counters);
155594604548SAndrea Mayer 	if (rc < 0) {
155694604548SAndrea Mayer 		nla_nest_cancel(skb, nest);
155794604548SAndrea Mayer 		return rc;
155894604548SAndrea Mayer 	}
155994604548SAndrea Mayer 
156094604548SAndrea Mayer 	return nla_nest_end(skb, nest);
156194604548SAndrea Mayer }
156294604548SAndrea Mayer 
156394604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
156494604548SAndrea Mayer {
156594604548SAndrea Mayer 	/* a and b are equal if both have pcpu_counters set or not */
156694604548SAndrea Mayer 	return (!!((unsigned long)a->pcpu_counters)) ^
156794604548SAndrea Mayer 		(!!((unsigned long)b->pcpu_counters));
156894604548SAndrea Mayer }
156994604548SAndrea Mayer 
157094604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt)
157194604548SAndrea Mayer {
157294604548SAndrea Mayer 	free_percpu(slwt->pcpu_counters);
157394604548SAndrea Mayer }
157494604548SAndrea Mayer 
1575d1df6fd8SDavid Lebrun struct seg6_action_param {
1576d1df6fd8SDavid Lebrun 	int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
1577d1df6fd8SDavid Lebrun 	int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
1578d1df6fd8SDavid Lebrun 	int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
1579964adce5SAndrea Mayer 
1580964adce5SAndrea Mayer 	/* optional destroy() callback useful for releasing resources which
1581964adce5SAndrea Mayer 	 * have been previously acquired in the corresponding parse()
1582964adce5SAndrea Mayer 	 * function.
1583964adce5SAndrea Mayer 	 */
1584964adce5SAndrea Mayer 	void (*destroy)(struct seg6_local_lwt *slwt);
1585d1df6fd8SDavid Lebrun };
1586d1df6fd8SDavid Lebrun 
1587d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
15882d9cc60aSDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .parse = parse_nla_srh,
15892d9cc60aSDavid Lebrun 				    .put = put_nla_srh,
1590964adce5SAndrea Mayer 				    .cmp = cmp_nla_srh,
1591964adce5SAndrea Mayer 				    .destroy = destroy_attr_srh },
1592d1df6fd8SDavid Lebrun 
15932d9cc60aSDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .parse = parse_nla_table,
15942d9cc60aSDavid Lebrun 				    .put = put_nla_table,
15952d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_table },
1596d1df6fd8SDavid Lebrun 
15972d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .parse = parse_nla_nh4,
15982d9cc60aSDavid Lebrun 				    .put = put_nla_nh4,
15992d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh4 },
1600d1df6fd8SDavid Lebrun 
16012d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .parse = parse_nla_nh6,
16022d9cc60aSDavid Lebrun 				    .put = put_nla_nh6,
16032d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh6 },
1604d1df6fd8SDavid Lebrun 
16052d9cc60aSDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .parse = parse_nla_iif,
16062d9cc60aSDavid Lebrun 				    .put = put_nla_iif,
16072d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_iif },
1608d1df6fd8SDavid Lebrun 
16092d9cc60aSDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .parse = parse_nla_oif,
16102d9cc60aSDavid Lebrun 				    .put = put_nla_oif,
16112d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_oif },
1612004d4b27SMathieu Xhonneux 
1613004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .parse = parse_nla_bpf,
1614004d4b27SMathieu Xhonneux 				    .put = put_nla_bpf,
1615964adce5SAndrea Mayer 				    .cmp = cmp_nla_bpf,
1616964adce5SAndrea Mayer 				    .destroy = destroy_attr_bpf },
1617004d4b27SMathieu Xhonneux 
1618664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .parse = parse_nla_vrftable,
1619664d6f86SAndrea Mayer 				    .put = put_nla_vrftable,
1620664d6f86SAndrea Mayer 				    .cmp = cmp_nla_vrftable },
1621664d6f86SAndrea Mayer 
162294604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .parse = parse_nla_counters,
162394604548SAndrea Mayer 				    .put = put_nla_counters,
162494604548SAndrea Mayer 				    .cmp = cmp_nla_counters,
162594604548SAndrea Mayer 				    .destroy = destroy_attr_counters },
1626d1df6fd8SDavid Lebrun };
1627d1df6fd8SDavid Lebrun 
1628964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in
16290a3021f1SAndrea Mayer  * @parsed_attrs, starting from the first attribute up to the @max_parsed
16300a3021f1SAndrea Mayer  * (excluded) attribute.
1631964adce5SAndrea Mayer  */
16320a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed,
16330a3021f1SAndrea Mayer 			    struct seg6_local_lwt *slwt)
1634964adce5SAndrea Mayer {
1635964adce5SAndrea Mayer 	struct seg6_action_param *param;
1636964adce5SAndrea Mayer 	int i;
1637964adce5SAndrea Mayer 
1638964adce5SAndrea Mayer 	/* Every required seg6local attribute is identified by an ID which is
1639964adce5SAndrea Mayer 	 * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask;
1640964adce5SAndrea Mayer 	 *
16410a3021f1SAndrea Mayer 	 * We scan the 'parsed_attrs' bitmask, starting from the first attribute
1642964adce5SAndrea Mayer 	 * up to the @max_parsed (excluded) attribute.
1643964adce5SAndrea Mayer 	 * For each set attribute, we retrieve the corresponding destroy()
1644964adce5SAndrea Mayer 	 * callback. If the callback is not available, then we skip to the next
1645964adce5SAndrea Mayer 	 * attribute; otherwise, we call the destroy() callback.
1646964adce5SAndrea Mayer 	 */
1647964adce5SAndrea Mayer 	for (i = 0; i < max_parsed; ++i) {
1648300a0fd8SAndrea Mayer 		if (!(parsed_attrs & SEG6_F_ATTR(i)))
1649964adce5SAndrea Mayer 			continue;
1650964adce5SAndrea Mayer 
1651964adce5SAndrea Mayer 		param = &seg6_action_params[i];
1652964adce5SAndrea Mayer 
1653964adce5SAndrea Mayer 		if (param->destroy)
1654964adce5SAndrea Mayer 			param->destroy(slwt);
1655964adce5SAndrea Mayer 	}
1656964adce5SAndrea Mayer }
1657964adce5SAndrea Mayer 
1658964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing
1659964adce5SAndrea Mayer  * operations.
1660964adce5SAndrea Mayer  */
1661964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt)
1662964adce5SAndrea Mayer {
16630a3021f1SAndrea Mayer 	unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs;
16640a3021f1SAndrea Mayer 
16650a3021f1SAndrea Mayer 	__destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt);
16660a3021f1SAndrea Mayer }
16670a3021f1SAndrea Mayer 
16680a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs,
16690a3021f1SAndrea Mayer 				    struct seg6_local_lwt *slwt)
16700a3021f1SAndrea Mayer {
16710a3021f1SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
16720a3021f1SAndrea Mayer 	unsigned long parsed_optattrs = 0;
16730a3021f1SAndrea Mayer 	struct seg6_action_param *param;
16740a3021f1SAndrea Mayer 	int err, i;
16750a3021f1SAndrea Mayer 
16760a3021f1SAndrea Mayer 	for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) {
1677300a0fd8SAndrea Mayer 		if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i])
16780a3021f1SAndrea Mayer 			continue;
16790a3021f1SAndrea Mayer 
16800a3021f1SAndrea Mayer 		/* once here, the i-th attribute is provided by the
16810a3021f1SAndrea Mayer 		 * userspace AND it is identified optional as well.
16820a3021f1SAndrea Mayer 		 */
16830a3021f1SAndrea Mayer 		param = &seg6_action_params[i];
16840a3021f1SAndrea Mayer 
16850a3021f1SAndrea Mayer 		err = param->parse(attrs, slwt);
16860a3021f1SAndrea Mayer 		if (err < 0)
16870a3021f1SAndrea Mayer 			goto parse_optattrs_err;
16880a3021f1SAndrea Mayer 
16890a3021f1SAndrea Mayer 		/* current attribute has been correctly parsed */
1690300a0fd8SAndrea Mayer 		parsed_optattrs |= SEG6_F_ATTR(i);
16910a3021f1SAndrea Mayer 	}
16920a3021f1SAndrea Mayer 
16930a3021f1SAndrea Mayer 	/* store in the tunnel state all the optional attributed successfully
16940a3021f1SAndrea Mayer 	 * parsed.
16950a3021f1SAndrea Mayer 	 */
16960a3021f1SAndrea Mayer 	slwt->parsed_optattrs = parsed_optattrs;
16970a3021f1SAndrea Mayer 
16980a3021f1SAndrea Mayer 	return 0;
16990a3021f1SAndrea Mayer 
17000a3021f1SAndrea Mayer parse_optattrs_err:
17010a3021f1SAndrea Mayer 	__destroy_attrs(parsed_optattrs, i, slwt);
17020a3021f1SAndrea Mayer 
17030a3021f1SAndrea Mayer 	return err;
1704964adce5SAndrea Mayer }
1705964adce5SAndrea Mayer 
1706cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase
1707cfdf64a0SAndrea Mayer  * and after that all its attributes have been parsed successfully.
1708cfdf64a0SAndrea Mayer  */
1709cfdf64a0SAndrea Mayer static int
1710cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg,
1711cfdf64a0SAndrea Mayer 				struct netlink_ext_ack *extack)
1712cfdf64a0SAndrea Mayer {
1713cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1714cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1715cfdf64a0SAndrea Mayer 
1716cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1717cfdf64a0SAndrea Mayer 	if (!ops->build_state)
1718cfdf64a0SAndrea Mayer 		return 0;
1719cfdf64a0SAndrea Mayer 
1720cfdf64a0SAndrea Mayer 	return ops->build_state(slwt, cfg, extack);
1721cfdf64a0SAndrea Mayer }
1722cfdf64a0SAndrea Mayer 
1723cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the
1724cfdf64a0SAndrea Mayer  * tunnel is going to be destroyed.
1725cfdf64a0SAndrea Mayer  */
1726cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
1727cfdf64a0SAndrea Mayer {
1728cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1729cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1730cfdf64a0SAndrea Mayer 
1731cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1732cfdf64a0SAndrea Mayer 	if (!ops->destroy_state)
1733cfdf64a0SAndrea Mayer 		return;
1734cfdf64a0SAndrea Mayer 
1735cfdf64a0SAndrea Mayer 	ops->destroy_state(slwt);
1736cfdf64a0SAndrea Mayer }
1737cfdf64a0SAndrea Mayer 
1738d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1739d1df6fd8SDavid Lebrun {
1740d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
1741d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
17420a3021f1SAndrea Mayer 	unsigned long invalid_attrs;
1743d1df6fd8SDavid Lebrun 	int i, err;
1744d1df6fd8SDavid Lebrun 
1745d1df6fd8SDavid Lebrun 	desc = __get_action_desc(slwt->action);
1746d1df6fd8SDavid Lebrun 	if (!desc)
1747d1df6fd8SDavid Lebrun 		return -EINVAL;
1748d1df6fd8SDavid Lebrun 
1749d1df6fd8SDavid Lebrun 	if (!desc->input)
1750d1df6fd8SDavid Lebrun 		return -EOPNOTSUPP;
1751d1df6fd8SDavid Lebrun 
1752d1df6fd8SDavid Lebrun 	slwt->desc = desc;
1753d1df6fd8SDavid Lebrun 	slwt->headroom += desc->static_headroom;
1754d1df6fd8SDavid Lebrun 
17550a3021f1SAndrea Mayer 	/* Forcing the desc->optattrs *set* and the desc->attrs *set* to be
17560a3021f1SAndrea Mayer 	 * disjoined, this allow us to release acquired resources by optional
17570a3021f1SAndrea Mayer 	 * attributes and by required attributes independently from each other
17580d770360SAndrea Mayer 	 * without any interference.
17590a3021f1SAndrea Mayer 	 * In other terms, we are sure that we do not release some the acquired
17600a3021f1SAndrea Mayer 	 * resources twice.
17610a3021f1SAndrea Mayer 	 *
17620a3021f1SAndrea Mayer 	 * Note that if an attribute is configured both as required and as
17630a3021f1SAndrea Mayer 	 * optional, it means that the user has messed something up in the
17640a3021f1SAndrea Mayer 	 * seg6_action_table. Therefore, this check is required for SRv6
17650a3021f1SAndrea Mayer 	 * behaviors to work properly.
17660a3021f1SAndrea Mayer 	 */
17670a3021f1SAndrea Mayer 	invalid_attrs = desc->attrs & desc->optattrs;
17680a3021f1SAndrea Mayer 	if (invalid_attrs) {
17690a3021f1SAndrea Mayer 		WARN_ONCE(1,
17700a3021f1SAndrea Mayer 			  "An attribute cannot be both required AND optional");
17710a3021f1SAndrea Mayer 		return -EINVAL;
17720a3021f1SAndrea Mayer 	}
17730a3021f1SAndrea Mayer 
17740a3021f1SAndrea Mayer 	/* parse the required attributes */
1775d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1776300a0fd8SAndrea Mayer 		if (desc->attrs & SEG6_F_ATTR(i)) {
1777d1df6fd8SDavid Lebrun 			if (!attrs[i])
1778d1df6fd8SDavid Lebrun 				return -EINVAL;
1779d1df6fd8SDavid Lebrun 
1780d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1781d1df6fd8SDavid Lebrun 
1782d1df6fd8SDavid Lebrun 			err = param->parse(attrs, slwt);
1783d1df6fd8SDavid Lebrun 			if (err < 0)
17840a3021f1SAndrea Mayer 				goto parse_attrs_err;
1785d1df6fd8SDavid Lebrun 		}
1786d1df6fd8SDavid Lebrun 	}
1787d1df6fd8SDavid Lebrun 
17880a3021f1SAndrea Mayer 	/* parse the optional attributes, if any */
17890a3021f1SAndrea Mayer 	err = parse_nla_optional_attrs(attrs, slwt);
17900a3021f1SAndrea Mayer 	if (err < 0)
17910a3021f1SAndrea Mayer 		goto parse_attrs_err;
17920a3021f1SAndrea Mayer 
1793d1df6fd8SDavid Lebrun 	return 0;
1794964adce5SAndrea Mayer 
17950a3021f1SAndrea Mayer parse_attrs_err:
1796964adce5SAndrea Mayer 	/* release any resource that may have been acquired during the i-1
1797964adce5SAndrea Mayer 	 * parse() operations.
1798964adce5SAndrea Mayer 	 */
17990a3021f1SAndrea Mayer 	__destroy_attrs(desc->attrs, i, slwt);
1800964adce5SAndrea Mayer 
1801964adce5SAndrea Mayer 	return err;
1802d1df6fd8SDavid Lebrun }
1803d1df6fd8SDavid Lebrun 
1804faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla,
1805faee6769SAlexander Aring 				  unsigned int family, const void *cfg,
1806faee6769SAlexander Aring 				  struct lwtunnel_state **ts,
1807d1df6fd8SDavid Lebrun 				  struct netlink_ext_ack *extack)
1808d1df6fd8SDavid Lebrun {
1809d1df6fd8SDavid Lebrun 	struct nlattr *tb[SEG6_LOCAL_MAX + 1];
1810d1df6fd8SDavid Lebrun 	struct lwtunnel_state *newts;
1811d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
1812d1df6fd8SDavid Lebrun 	int err;
1813d1df6fd8SDavid Lebrun 
18146285217fSDavid Lebrun 	if (family != AF_INET6)
18156285217fSDavid Lebrun 		return -EINVAL;
18166285217fSDavid Lebrun 
18178cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla,
18188cb08174SJohannes Berg 					  seg6_local_policy, extack);
1819d1df6fd8SDavid Lebrun 
1820d1df6fd8SDavid Lebrun 	if (err < 0)
1821d1df6fd8SDavid Lebrun 		return err;
1822d1df6fd8SDavid Lebrun 
1823d1df6fd8SDavid Lebrun 	if (!tb[SEG6_LOCAL_ACTION])
1824d1df6fd8SDavid Lebrun 		return -EINVAL;
1825d1df6fd8SDavid Lebrun 
1826d1df6fd8SDavid Lebrun 	newts = lwtunnel_state_alloc(sizeof(*slwt));
1827d1df6fd8SDavid Lebrun 	if (!newts)
1828d1df6fd8SDavid Lebrun 		return -ENOMEM;
1829d1df6fd8SDavid Lebrun 
1830d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(newts);
1831d1df6fd8SDavid Lebrun 	slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
1832d1df6fd8SDavid Lebrun 
1833d1df6fd8SDavid Lebrun 	err = parse_nla_action(tb, slwt);
1834d1df6fd8SDavid Lebrun 	if (err < 0)
1835d1df6fd8SDavid Lebrun 		goto out_free;
1836d1df6fd8SDavid Lebrun 
1837cfdf64a0SAndrea Mayer 	err = seg6_local_lwtunnel_build_state(slwt, cfg, extack);
1838cfdf64a0SAndrea Mayer 	if (err < 0)
1839cfdf64a0SAndrea Mayer 		goto out_destroy_attrs;
1840cfdf64a0SAndrea Mayer 
1841d1df6fd8SDavid Lebrun 	newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
1842d1df6fd8SDavid Lebrun 	newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
1843d1df6fd8SDavid Lebrun 	newts->headroom = slwt->headroom;
1844d1df6fd8SDavid Lebrun 
1845d1df6fd8SDavid Lebrun 	*ts = newts;
1846d1df6fd8SDavid Lebrun 
1847d1df6fd8SDavid Lebrun 	return 0;
1848d1df6fd8SDavid Lebrun 
1849cfdf64a0SAndrea Mayer out_destroy_attrs:
1850cfdf64a0SAndrea Mayer 	destroy_attrs(slwt);
1851d1df6fd8SDavid Lebrun out_free:
1852d1df6fd8SDavid Lebrun 	kfree(newts);
1853d1df6fd8SDavid Lebrun 	return err;
1854d1df6fd8SDavid Lebrun }
1855d1df6fd8SDavid Lebrun 
1856d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
1857d1df6fd8SDavid Lebrun {
1858d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1859d1df6fd8SDavid Lebrun 
1860cfdf64a0SAndrea Mayer 	seg6_local_lwtunnel_destroy_state(slwt);
1861cfdf64a0SAndrea Mayer 
1862964adce5SAndrea Mayer 	destroy_attrs(slwt);
1863004d4b27SMathieu Xhonneux 
1864004d4b27SMathieu Xhonneux 	return;
1865d1df6fd8SDavid Lebrun }
1866d1df6fd8SDavid Lebrun 
1867d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb,
1868d1df6fd8SDavid Lebrun 				 struct lwtunnel_state *lwt)
1869d1df6fd8SDavid Lebrun {
1870d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1871d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
18720a3021f1SAndrea Mayer 	unsigned long attrs;
1873d1df6fd8SDavid Lebrun 	int i, err;
1874d1df6fd8SDavid Lebrun 
1875d1df6fd8SDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
1876d1df6fd8SDavid Lebrun 		return -EMSGSIZE;
1877d1df6fd8SDavid Lebrun 
18780a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
18790a3021f1SAndrea Mayer 
1880d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1881300a0fd8SAndrea Mayer 		if (attrs & SEG6_F_ATTR(i)) {
1882d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1883d1df6fd8SDavid Lebrun 			err = param->put(skb, slwt);
1884d1df6fd8SDavid Lebrun 			if (err < 0)
1885d1df6fd8SDavid Lebrun 				return err;
1886d1df6fd8SDavid Lebrun 		}
1887d1df6fd8SDavid Lebrun 	}
1888d1df6fd8SDavid Lebrun 
1889d1df6fd8SDavid Lebrun 	return 0;
1890d1df6fd8SDavid Lebrun }
1891d1df6fd8SDavid Lebrun 
1892d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
1893d1df6fd8SDavid Lebrun {
1894d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1895d1df6fd8SDavid Lebrun 	unsigned long attrs;
1896d1df6fd8SDavid Lebrun 	int nlsize;
1897d1df6fd8SDavid Lebrun 
1898d1df6fd8SDavid Lebrun 	nlsize = nla_total_size(4); /* action */
1899d1df6fd8SDavid Lebrun 
19000a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
1901d1df6fd8SDavid Lebrun 
1902300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH))
1903d1df6fd8SDavid Lebrun 		nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
1904d1df6fd8SDavid Lebrun 
1905300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE))
1906d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1907d1df6fd8SDavid Lebrun 
1908300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4))
1909d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1910d1df6fd8SDavid Lebrun 
1911300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6))
1912d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(16);
1913d1df6fd8SDavid Lebrun 
1914300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF))
1915d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1916d1df6fd8SDavid Lebrun 
1917300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF))
1918d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1919d1df6fd8SDavid Lebrun 
1920300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF))
1921004d4b27SMathieu Xhonneux 		nlsize += nla_total_size(sizeof(struct nlattr)) +
1922004d4b27SMathieu Xhonneux 		       nla_total_size(MAX_PROG_NAME) +
1923004d4b27SMathieu Xhonneux 		       nla_total_size(4);
1924004d4b27SMathieu Xhonneux 
1925300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE))
1926664d6f86SAndrea Mayer 		nlsize += nla_total_size(4);
1927664d6f86SAndrea Mayer 
192894604548SAndrea Mayer 	if (attrs & SEG6_F_LOCAL_COUNTERS)
192994604548SAndrea Mayer 		nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */
193094604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_PACKETS */
193194604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
193294604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_BYTES */
193394604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
193494604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_ERRORS */
193594604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64));
193694604548SAndrea Mayer 
1937d1df6fd8SDavid Lebrun 	return nlsize;
1938d1df6fd8SDavid Lebrun }
1939d1df6fd8SDavid Lebrun 
1940d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a,
1941d1df6fd8SDavid Lebrun 				struct lwtunnel_state *b)
1942d1df6fd8SDavid Lebrun {
1943d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt_a, *slwt_b;
1944d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
19450a3021f1SAndrea Mayer 	unsigned long attrs_a, attrs_b;
1946d1df6fd8SDavid Lebrun 	int i;
1947d1df6fd8SDavid Lebrun 
1948d1df6fd8SDavid Lebrun 	slwt_a = seg6_local_lwtunnel(a);
1949d1df6fd8SDavid Lebrun 	slwt_b = seg6_local_lwtunnel(b);
1950d1df6fd8SDavid Lebrun 
1951d1df6fd8SDavid Lebrun 	if (slwt_a->action != slwt_b->action)
1952d1df6fd8SDavid Lebrun 		return 1;
1953d1df6fd8SDavid Lebrun 
19540a3021f1SAndrea Mayer 	attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs;
19550a3021f1SAndrea Mayer 	attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs;
19560a3021f1SAndrea Mayer 
19570a3021f1SAndrea Mayer 	if (attrs_a != attrs_b)
1958d1df6fd8SDavid Lebrun 		return 1;
1959d1df6fd8SDavid Lebrun 
1960d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1961300a0fd8SAndrea Mayer 		if (attrs_a & SEG6_F_ATTR(i)) {
1962d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1963d1df6fd8SDavid Lebrun 			if (param->cmp(slwt_a, slwt_b))
1964d1df6fd8SDavid Lebrun 				return 1;
1965d1df6fd8SDavid Lebrun 		}
1966d1df6fd8SDavid Lebrun 	}
1967d1df6fd8SDavid Lebrun 
1968d1df6fd8SDavid Lebrun 	return 0;
1969d1df6fd8SDavid Lebrun }
1970d1df6fd8SDavid Lebrun 
1971d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = {
1972d1df6fd8SDavid Lebrun 	.build_state	= seg6_local_build_state,
1973d1df6fd8SDavid Lebrun 	.destroy_state	= seg6_local_destroy_state,
1974d1df6fd8SDavid Lebrun 	.input		= seg6_local_input,
1975d1df6fd8SDavid Lebrun 	.fill_encap	= seg6_local_fill_encap,
1976d1df6fd8SDavid Lebrun 	.get_encap_size	= seg6_local_get_encap_size,
1977d1df6fd8SDavid Lebrun 	.cmp_encap	= seg6_local_cmp_encap,
1978d1df6fd8SDavid Lebrun 	.owner		= THIS_MODULE,
1979d1df6fd8SDavid Lebrun };
1980d1df6fd8SDavid Lebrun 
1981d1df6fd8SDavid Lebrun int __init seg6_local_init(void)
1982d1df6fd8SDavid Lebrun {
1983300a0fd8SAndrea Mayer 	/* If the max total number of defined attributes is reached, then your
1984300a0fd8SAndrea Mayer 	 * kernel build stops here.
1985300a0fd8SAndrea Mayer 	 *
1986300a0fd8SAndrea Mayer 	 * This check is required to avoid arithmetic overflows when processing
1987300a0fd8SAndrea Mayer 	 * behavior attributes and the maximum number of defined attributes
1988300a0fd8SAndrea Mayer 	 * exceeds the allowed value.
1989300a0fd8SAndrea Mayer 	 */
1990300a0fd8SAndrea Mayer 	BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long));
1991300a0fd8SAndrea Mayer 
1992d1df6fd8SDavid Lebrun 	return lwtunnel_encap_add_ops(&seg6_local_ops,
1993d1df6fd8SDavid Lebrun 				      LWTUNNEL_ENCAP_SEG6_LOCAL);
1994d1df6fd8SDavid Lebrun }
1995d1df6fd8SDavid Lebrun 
1996d1df6fd8SDavid Lebrun void seg6_local_exit(void)
1997d1df6fd8SDavid Lebrun {
1998d1df6fd8SDavid Lebrun 	lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
1999d1df6fd8SDavid Lebrun }
2000