xref: /openbmc/linux/net/ipv6/seg6_local.c (revision 8b532109bf885b7b59b93487bc4672eb6d071b78)
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>
33d1df6fd8SDavid Lebrun 
34300a0fd8SAndrea Mayer #define SEG6_F_ATTR(i)		BIT(i)
35300a0fd8SAndrea Mayer 
36d1df6fd8SDavid Lebrun struct seg6_local_lwt;
37d1df6fd8SDavid Lebrun 
38cfdf64a0SAndrea Mayer /* callbacks used for customizing the creation and destruction of a behavior */
39cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops {
40cfdf64a0SAndrea Mayer 	int (*build_state)(struct seg6_local_lwt *slwt, const void *cfg,
41cfdf64a0SAndrea Mayer 			   struct netlink_ext_ack *extack);
42cfdf64a0SAndrea Mayer 	void (*destroy_state)(struct seg6_local_lwt *slwt);
43cfdf64a0SAndrea Mayer };
44cfdf64a0SAndrea Mayer 
45d1df6fd8SDavid Lebrun struct seg6_action_desc {
46d1df6fd8SDavid Lebrun 	int action;
47d1df6fd8SDavid Lebrun 	unsigned long attrs;
480a3021f1SAndrea Mayer 
490a3021f1SAndrea Mayer 	/* The optattrs field is used for specifying all the optional
500a3021f1SAndrea Mayer 	 * attributes supported by a specific behavior.
510a3021f1SAndrea Mayer 	 * It means that if one of these attributes is not provided in the
520a3021f1SAndrea Mayer 	 * netlink message during the behavior creation, no errors will be
530a3021f1SAndrea Mayer 	 * returned to the userspace.
540a3021f1SAndrea Mayer 	 *
550a3021f1SAndrea Mayer 	 * Each attribute can be only of two types (mutually exclusive):
560a3021f1SAndrea Mayer 	 * 1) required or 2) optional.
570a3021f1SAndrea Mayer 	 * Every user MUST obey to this rule! If you set an attribute as
580a3021f1SAndrea Mayer 	 * required the same attribute CANNOT be set as optional and vice
590a3021f1SAndrea Mayer 	 * versa.
600a3021f1SAndrea Mayer 	 */
610a3021f1SAndrea Mayer 	unsigned long optattrs;
620a3021f1SAndrea Mayer 
63d1df6fd8SDavid Lebrun 	int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
64d1df6fd8SDavid Lebrun 	int static_headroom;
65cfdf64a0SAndrea Mayer 
66cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops slwt_ops;
67d1df6fd8SDavid Lebrun };
68d1df6fd8SDavid Lebrun 
69004d4b27SMathieu Xhonneux struct bpf_lwt_prog {
70004d4b27SMathieu Xhonneux 	struct bpf_prog *prog;
71004d4b27SMathieu Xhonneux 	char *name;
72004d4b27SMathieu Xhonneux };
73004d4b27SMathieu Xhonneux 
74664d6f86SAndrea Mayer enum seg6_end_dt_mode {
75664d6f86SAndrea Mayer 	DT_INVALID_MODE	= -EINVAL,
76664d6f86SAndrea Mayer 	DT_LEGACY_MODE	= 0,
77664d6f86SAndrea Mayer 	DT_VRF_MODE	= 1,
78664d6f86SAndrea Mayer };
79664d6f86SAndrea Mayer 
80664d6f86SAndrea Mayer struct seg6_end_dt_info {
81664d6f86SAndrea Mayer 	enum seg6_end_dt_mode mode;
82664d6f86SAndrea Mayer 
83664d6f86SAndrea Mayer 	struct net *net;
84664d6f86SAndrea Mayer 	/* VRF device associated to the routing table used by the SRv6
85664d6f86SAndrea Mayer 	 * End.DT4/DT6 behavior for routing IPv4/IPv6 packets.
86664d6f86SAndrea Mayer 	 */
87664d6f86SAndrea Mayer 	int vrf_ifindex;
88664d6f86SAndrea Mayer 	int vrf_table;
89664d6f86SAndrea Mayer 
90*8b532109SAndrea Mayer 	/* tunneled packet family (IPv4 or IPv6).
91*8b532109SAndrea Mayer 	 * Protocol and header length are inferred from family.
92*8b532109SAndrea Mayer 	 */
93664d6f86SAndrea Mayer 	u16 family;
94664d6f86SAndrea Mayer };
95664d6f86SAndrea Mayer 
9694604548SAndrea Mayer struct pcpu_seg6_local_counters {
9794604548SAndrea Mayer 	u64_stats_t packets;
9894604548SAndrea Mayer 	u64_stats_t bytes;
9994604548SAndrea Mayer 	u64_stats_t errors;
10094604548SAndrea Mayer 
10194604548SAndrea Mayer 	struct u64_stats_sync syncp;
10294604548SAndrea Mayer };
10394604548SAndrea Mayer 
10494604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far.
10594604548SAndrea Mayer  *
10694604548SAndrea Mayer  * put_nla_counters() makes use of this data structure to collect all counter
10794604548SAndrea Mayer  * values after the per-CPU counter evaluation has been performed.
10894604548SAndrea Mayer  * Finally, each counter value (in seg6_local_counters) is stored in the
10994604548SAndrea Mayer  * corresponding netlink attribute and sent to user space.
11094604548SAndrea Mayer  *
11194604548SAndrea Mayer  * NB: we don't want to expose this structure to user space!
11294604548SAndrea Mayer  */
11394604548SAndrea Mayer struct seg6_local_counters {
11494604548SAndrea Mayer 	__u64 packets;
11594604548SAndrea Mayer 	__u64 bytes;
11694604548SAndrea Mayer 	__u64 errors;
11794604548SAndrea Mayer };
11894604548SAndrea Mayer 
11994604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp)				\
12094604548SAndrea Mayer 	__netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters,	\
12194604548SAndrea Mayer 				  ((__gfp) | __GFP_ZERO))
12294604548SAndrea Mayer 
12394604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS	SEG6_F_ATTR(SEG6_LOCAL_COUNTERS)
12494604548SAndrea Mayer 
125d1df6fd8SDavid Lebrun struct seg6_local_lwt {
126d1df6fd8SDavid Lebrun 	int action;
127d1df6fd8SDavid Lebrun 	struct ipv6_sr_hdr *srh;
128d1df6fd8SDavid Lebrun 	int table;
129d1df6fd8SDavid Lebrun 	struct in_addr nh4;
130d1df6fd8SDavid Lebrun 	struct in6_addr nh6;
131d1df6fd8SDavid Lebrun 	int iif;
132d1df6fd8SDavid Lebrun 	int oif;
133004d4b27SMathieu Xhonneux 	struct bpf_lwt_prog bpf;
134664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
135664d6f86SAndrea Mayer 	struct seg6_end_dt_info dt_info;
136664d6f86SAndrea Mayer #endif
13794604548SAndrea Mayer 	struct pcpu_seg6_local_counters __percpu *pcpu_counters;
138d1df6fd8SDavid Lebrun 
139d1df6fd8SDavid Lebrun 	int headroom;
140d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1410a3021f1SAndrea Mayer 	/* unlike the required attrs, we have to track the optional attributes
1420a3021f1SAndrea Mayer 	 * that have been effectively parsed.
1430a3021f1SAndrea Mayer 	 */
1440a3021f1SAndrea Mayer 	unsigned long parsed_optattrs;
145d1df6fd8SDavid Lebrun };
146d1df6fd8SDavid Lebrun 
147d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
148d1df6fd8SDavid Lebrun {
149d1df6fd8SDavid Lebrun 	return (struct seg6_local_lwt *)lwt->data;
150d1df6fd8SDavid Lebrun }
151d1df6fd8SDavid Lebrun 
152fbbc5bc2SJulien Massonneau static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb, int flags)
153140f04c3SDavid Lebrun {
154140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
1555829d70bSAhmed Abdelsalam 	int len, srhoff = 0;
156140f04c3SDavid Lebrun 
157fbbc5bc2SJulien Massonneau 	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0)
158140f04c3SDavid Lebrun 		return NULL;
159140f04c3SDavid Lebrun 
1605829d70bSAhmed Abdelsalam 	if (!pskb_may_pull(skb, srhoff + sizeof(*srh)))
1615829d70bSAhmed Abdelsalam 		return NULL;
1625829d70bSAhmed Abdelsalam 
1635829d70bSAhmed Abdelsalam 	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
1645829d70bSAhmed Abdelsalam 
165140f04c3SDavid Lebrun 	len = (srh->hdrlen + 1) << 3;
166140f04c3SDavid Lebrun 
1675829d70bSAhmed Abdelsalam 	if (!pskb_may_pull(skb, srhoff + len))
168140f04c3SDavid Lebrun 		return NULL;
169140f04c3SDavid Lebrun 
1707f91ed8cSAndrea Mayer 	/* note that pskb_may_pull may change pointers in header;
1717f91ed8cSAndrea Mayer 	 * for this reason it is necessary to reload them when needed.
1727f91ed8cSAndrea Mayer 	 */
1737f91ed8cSAndrea Mayer 	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
1747f91ed8cSAndrea Mayer 
175bb986a50SAhmed Abdelsalam 	if (!seg6_validate_srh(srh, len, true))
176140f04c3SDavid Lebrun 		return NULL;
177140f04c3SDavid Lebrun 
178140f04c3SDavid Lebrun 	return srh;
179140f04c3SDavid Lebrun }
180140f04c3SDavid Lebrun 
181140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
182140f04c3SDavid Lebrun {
183140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
184140f04c3SDavid Lebrun 
185fbbc5bc2SJulien Massonneau 	srh = get_srh(skb, IP6_FH_F_SKIP_RH);
186140f04c3SDavid Lebrun 	if (!srh)
187140f04c3SDavid Lebrun 		return NULL;
188140f04c3SDavid Lebrun 
189140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
190140f04c3SDavid Lebrun 	if (!seg6_hmac_validate_skb(skb))
191140f04c3SDavid Lebrun 		return NULL;
192140f04c3SDavid Lebrun #endif
193140f04c3SDavid Lebrun 
194140f04c3SDavid Lebrun 	return srh;
195140f04c3SDavid Lebrun }
196140f04c3SDavid Lebrun 
197d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto)
198d7a669ddSDavid Lebrun {
199d7a669ddSDavid Lebrun 	struct ipv6_sr_hdr *srh;
200d7a669ddSDavid Lebrun 	unsigned int off = 0;
201d7a669ddSDavid Lebrun 
202fbbc5bc2SJulien Massonneau 	srh = get_srh(skb, 0);
203d7a669ddSDavid Lebrun 	if (srh && srh->segments_left > 0)
204d7a669ddSDavid Lebrun 		return false;
205d7a669ddSDavid Lebrun 
206d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC
207d7a669ddSDavid Lebrun 	if (srh && !seg6_hmac_validate_skb(skb))
208d7a669ddSDavid Lebrun 		return false;
209d7a669ddSDavid Lebrun #endif
210d7a669ddSDavid Lebrun 
211d7a669ddSDavid Lebrun 	if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0)
212d7a669ddSDavid Lebrun 		return false;
213d7a669ddSDavid Lebrun 
214d7a669ddSDavid Lebrun 	if (!pskb_pull(skb, off))
215d7a669ddSDavid Lebrun 		return false;
216d7a669ddSDavid Lebrun 
217d7a669ddSDavid Lebrun 	skb_postpull_rcsum(skb, skb_network_header(skb), off);
218d7a669ddSDavid Lebrun 
219d7a669ddSDavid Lebrun 	skb_reset_network_header(skb);
220d7a669ddSDavid Lebrun 	skb_reset_transport_header(skb);
22162ebaeaeSYuki Taguchi 	if (iptunnel_pull_offloads(skb))
22262ebaeaeSYuki Taguchi 		return false;
223d7a669ddSDavid Lebrun 
224d7a669ddSDavid Lebrun 	return true;
225d7a669ddSDavid Lebrun }
226d7a669ddSDavid Lebrun 
227d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr)
228d7a669ddSDavid Lebrun {
229d7a669ddSDavid Lebrun 	struct in6_addr *addr;
230d7a669ddSDavid Lebrun 
231d7a669ddSDavid Lebrun 	srh->segments_left--;
232d7a669ddSDavid Lebrun 	addr = srh->segments + srh->segments_left;
233d7a669ddSDavid Lebrun 	*daddr = *addr;
234d7a669ddSDavid Lebrun }
235d7a669ddSDavid Lebrun 
236fd1fef0cSAndrea Mayer static int
237fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
238fd1fef0cSAndrea Mayer 			u32 tbl_id, bool local_delivery)
239d7a669ddSDavid Lebrun {
240d7a669ddSDavid Lebrun 	struct net *net = dev_net(skb->dev);
241d7a669ddSDavid Lebrun 	struct ipv6hdr *hdr = ipv6_hdr(skb);
242d7a669ddSDavid Lebrun 	int flags = RT6_LOOKUP_F_HAS_SADDR;
243d7a669ddSDavid Lebrun 	struct dst_entry *dst = NULL;
244d7a669ddSDavid Lebrun 	struct rt6_info *rt;
245d7a669ddSDavid Lebrun 	struct flowi6 fl6;
246fd1fef0cSAndrea Mayer 	int dev_flags = 0;
247d7a669ddSDavid Lebrun 
248d7a669ddSDavid Lebrun 	fl6.flowi6_iif = skb->dev->ifindex;
249d7a669ddSDavid Lebrun 	fl6.daddr = nhaddr ? *nhaddr : hdr->daddr;
250d7a669ddSDavid Lebrun 	fl6.saddr = hdr->saddr;
251d7a669ddSDavid Lebrun 	fl6.flowlabel = ip6_flowinfo(hdr);
252d7a669ddSDavid Lebrun 	fl6.flowi6_mark = skb->mark;
253d7a669ddSDavid Lebrun 	fl6.flowi6_proto = hdr->nexthdr;
254d7a669ddSDavid Lebrun 
255d7a669ddSDavid Lebrun 	if (nhaddr)
256d7a669ddSDavid Lebrun 		fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH;
257d7a669ddSDavid Lebrun 
258d7a669ddSDavid Lebrun 	if (!tbl_id) {
259b75cc8f9SDavid Ahern 		dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags);
260d7a669ddSDavid Lebrun 	} else {
261d7a669ddSDavid Lebrun 		struct fib6_table *table;
262d7a669ddSDavid Lebrun 
263d7a669ddSDavid Lebrun 		table = fib6_get_table(net, tbl_id);
264d7a669ddSDavid Lebrun 		if (!table)
265d7a669ddSDavid Lebrun 			goto out;
266d7a669ddSDavid Lebrun 
267b75cc8f9SDavid Ahern 		rt = ip6_pol_route(net, table, 0, &fl6, skb, flags);
268d7a669ddSDavid Lebrun 		dst = &rt->dst;
269d7a669ddSDavid Lebrun 	}
270d7a669ddSDavid Lebrun 
271fd1fef0cSAndrea Mayer 	/* we want to discard traffic destined for local packet processing,
272fd1fef0cSAndrea Mayer 	 * if @local_delivery is set to false.
273fd1fef0cSAndrea Mayer 	 */
274fd1fef0cSAndrea Mayer 	if (!local_delivery)
275fd1fef0cSAndrea Mayer 		dev_flags |= IFF_LOOPBACK;
276fd1fef0cSAndrea Mayer 
277fd1fef0cSAndrea Mayer 	if (dst && (dst->dev->flags & dev_flags) && !dst->error) {
278d7a669ddSDavid Lebrun 		dst_release(dst);
279d7a669ddSDavid Lebrun 		dst = NULL;
280d7a669ddSDavid Lebrun 	}
281d7a669ddSDavid Lebrun 
282d7a669ddSDavid Lebrun out:
283d7a669ddSDavid Lebrun 	if (!dst) {
284d7a669ddSDavid Lebrun 		rt = net->ipv6.ip6_blk_hole_entry;
285d7a669ddSDavid Lebrun 		dst = &rt->dst;
286d7a669ddSDavid Lebrun 		dst_hold(dst);
287d7a669ddSDavid Lebrun 	}
288d7a669ddSDavid Lebrun 
289d7a669ddSDavid Lebrun 	skb_dst_drop(skb);
290d7a669ddSDavid Lebrun 	skb_dst_set(skb, dst);
2911c1e761eSMathieu Xhonneux 	return dst->error;
292d7a669ddSDavid Lebrun }
293d7a669ddSDavid Lebrun 
294fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb,
295fd1fef0cSAndrea Mayer 			struct in6_addr *nhaddr, u32 tbl_id)
296fd1fef0cSAndrea Mayer {
297fd1fef0cSAndrea Mayer 	return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false);
298fd1fef0cSAndrea Mayer }
299fd1fef0cSAndrea Mayer 
300140f04c3SDavid Lebrun /* regular endpoint function */
301140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
302140f04c3SDavid Lebrun {
303140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
304140f04c3SDavid Lebrun 
305140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
306140f04c3SDavid Lebrun 	if (!srh)
307140f04c3SDavid Lebrun 		goto drop;
308140f04c3SDavid Lebrun 
309d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
310140f04c3SDavid Lebrun 
3111c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
312140f04c3SDavid Lebrun 
313140f04c3SDavid Lebrun 	return dst_input(skb);
314140f04c3SDavid Lebrun 
315140f04c3SDavid Lebrun drop:
316140f04c3SDavid Lebrun 	kfree_skb(skb);
317140f04c3SDavid Lebrun 	return -EINVAL;
318140f04c3SDavid Lebrun }
319140f04c3SDavid Lebrun 
320140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */
321140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
322140f04c3SDavid Lebrun {
323140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
324140f04c3SDavid Lebrun 
325140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
326140f04c3SDavid Lebrun 	if (!srh)
327140f04c3SDavid Lebrun 		goto drop;
328140f04c3SDavid Lebrun 
329d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
330140f04c3SDavid Lebrun 
3311c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, &slwt->nh6, 0);
332140f04c3SDavid Lebrun 
333140f04c3SDavid Lebrun 	return dst_input(skb);
334140f04c3SDavid Lebrun 
335140f04c3SDavid Lebrun drop:
336140f04c3SDavid Lebrun 	kfree_skb(skb);
337140f04c3SDavid Lebrun 	return -EINVAL;
338140f04c3SDavid Lebrun }
339140f04c3SDavid Lebrun 
340891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt)
341891ef8ddSDavid Lebrun {
342891ef8ddSDavid Lebrun 	struct ipv6_sr_hdr *srh;
343891ef8ddSDavid Lebrun 
344891ef8ddSDavid Lebrun 	srh = get_and_validate_srh(skb);
345891ef8ddSDavid Lebrun 	if (!srh)
346891ef8ddSDavid Lebrun 		goto drop;
347891ef8ddSDavid Lebrun 
348891ef8ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
349891ef8ddSDavid Lebrun 
3501c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, slwt->table);
351891ef8ddSDavid Lebrun 
352891ef8ddSDavid Lebrun 	return dst_input(skb);
353891ef8ddSDavid Lebrun 
354891ef8ddSDavid Lebrun drop:
355891ef8ddSDavid Lebrun 	kfree_skb(skb);
356891ef8ddSDavid Lebrun 	return -EINVAL;
357891ef8ddSDavid Lebrun }
358891ef8ddSDavid Lebrun 
359891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */
360891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb,
361891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
362891ef8ddSDavid Lebrun {
363891ef8ddSDavid Lebrun 	struct net *net = dev_net(skb->dev);
364891ef8ddSDavid Lebrun 	struct net_device *odev;
365891ef8ddSDavid Lebrun 	struct ethhdr *eth;
366891ef8ddSDavid Lebrun 
36726776253SPaolo Lungaroni 	if (!decap_and_validate(skb, IPPROTO_ETHERNET))
368891ef8ddSDavid Lebrun 		goto drop;
369891ef8ddSDavid Lebrun 
370891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, ETH_HLEN))
371891ef8ddSDavid Lebrun 		goto drop;
372891ef8ddSDavid Lebrun 
373891ef8ddSDavid Lebrun 	skb_reset_mac_header(skb);
374891ef8ddSDavid Lebrun 	eth = (struct ethhdr *)skb->data;
375891ef8ddSDavid Lebrun 
376891ef8ddSDavid Lebrun 	/* To determine the frame's protocol, we assume it is 802.3. This avoids
377891ef8ddSDavid Lebrun 	 * a call to eth_type_trans(), which is not really relevant for our
378891ef8ddSDavid Lebrun 	 * use case.
379891ef8ddSDavid Lebrun 	 */
380891ef8ddSDavid Lebrun 	if (!eth_proto_is_802_3(eth->h_proto))
381891ef8ddSDavid Lebrun 		goto drop;
382891ef8ddSDavid Lebrun 
383891ef8ddSDavid Lebrun 	odev = dev_get_by_index_rcu(net, slwt->oif);
384891ef8ddSDavid Lebrun 	if (!odev)
385891ef8ddSDavid Lebrun 		goto drop;
386891ef8ddSDavid Lebrun 
387891ef8ddSDavid Lebrun 	/* As we accept Ethernet frames, make sure the egress device is of
388891ef8ddSDavid Lebrun 	 * the correct type.
389891ef8ddSDavid Lebrun 	 */
390891ef8ddSDavid Lebrun 	if (odev->type != ARPHRD_ETHER)
391891ef8ddSDavid Lebrun 		goto drop;
392891ef8ddSDavid Lebrun 
393891ef8ddSDavid Lebrun 	if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev))
394891ef8ddSDavid Lebrun 		goto drop;
395891ef8ddSDavid Lebrun 
396891ef8ddSDavid Lebrun 	skb_orphan(skb);
397891ef8ddSDavid Lebrun 
398891ef8ddSDavid Lebrun 	if (skb_warn_if_lro(skb))
399891ef8ddSDavid Lebrun 		goto drop;
400891ef8ddSDavid Lebrun 
401891ef8ddSDavid Lebrun 	skb_forward_csum(skb);
402891ef8ddSDavid Lebrun 
403891ef8ddSDavid Lebrun 	if (skb->len - ETH_HLEN > odev->mtu)
404891ef8ddSDavid Lebrun 		goto drop;
405891ef8ddSDavid Lebrun 
406891ef8ddSDavid Lebrun 	skb->dev = odev;
407891ef8ddSDavid Lebrun 	skb->protocol = eth->h_proto;
408891ef8ddSDavid Lebrun 
409891ef8ddSDavid Lebrun 	return dev_queue_xmit(skb);
410891ef8ddSDavid Lebrun 
411891ef8ddSDavid Lebrun drop:
412891ef8ddSDavid Lebrun 	kfree_skb(skb);
413891ef8ddSDavid Lebrun 	return -EINVAL;
414891ef8ddSDavid Lebrun }
415891ef8ddSDavid Lebrun 
416140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */
417140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb,
418140f04c3SDavid Lebrun 				struct seg6_local_lwt *slwt)
419140f04c3SDavid Lebrun {
420d7a669ddSDavid Lebrun 	struct in6_addr *nhaddr = NULL;
421140f04c3SDavid Lebrun 
422140f04c3SDavid Lebrun 	/* this function accepts IPv6 encapsulated packets, with either
423140f04c3SDavid Lebrun 	 * an SRH with SL=0, or no SRH.
424140f04c3SDavid Lebrun 	 */
425140f04c3SDavid Lebrun 
426d7a669ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPV6))
427140f04c3SDavid Lebrun 		goto drop;
428140f04c3SDavid Lebrun 
429d7a669ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
430140f04c3SDavid Lebrun 		goto drop;
431140f04c3SDavid Lebrun 
432140f04c3SDavid Lebrun 	/* The inner packet is not associated to any local interface,
433140f04c3SDavid Lebrun 	 * so we do not call netif_rx().
434140f04c3SDavid Lebrun 	 *
435140f04c3SDavid Lebrun 	 * If slwt->nh6 is set to ::, then lookup the nexthop for the
436140f04c3SDavid Lebrun 	 * inner packet's DA. Otherwise, use the specified nexthop.
437140f04c3SDavid Lebrun 	 */
438140f04c3SDavid Lebrun 
439d7a669ddSDavid Lebrun 	if (!ipv6_addr_any(&slwt->nh6))
440d7a669ddSDavid Lebrun 		nhaddr = &slwt->nh6;
441140f04c3SDavid Lebrun 
442c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
443c71644d0SAndrea Mayer 
4441c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, nhaddr, 0);
445140f04c3SDavid Lebrun 
446140f04c3SDavid Lebrun 	return dst_input(skb);
447140f04c3SDavid Lebrun drop:
448140f04c3SDavid Lebrun 	kfree_skb(skb);
449140f04c3SDavid Lebrun 	return -EINVAL;
450140f04c3SDavid Lebrun }
451140f04c3SDavid Lebrun 
452891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb,
453891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
454891ef8ddSDavid Lebrun {
455891ef8ddSDavid Lebrun 	struct iphdr *iph;
456891ef8ddSDavid Lebrun 	__be32 nhaddr;
457891ef8ddSDavid Lebrun 	int err;
458891ef8ddSDavid Lebrun 
459891ef8ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPIP))
460891ef8ddSDavid Lebrun 		goto drop;
461891ef8ddSDavid Lebrun 
462891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
463891ef8ddSDavid Lebrun 		goto drop;
464891ef8ddSDavid Lebrun 
465891ef8ddSDavid Lebrun 	skb->protocol = htons(ETH_P_IP);
466891ef8ddSDavid Lebrun 
467891ef8ddSDavid Lebrun 	iph = ip_hdr(skb);
468891ef8ddSDavid Lebrun 
469891ef8ddSDavid Lebrun 	nhaddr = slwt->nh4.s_addr ?: iph->daddr;
470891ef8ddSDavid Lebrun 
471891ef8ddSDavid Lebrun 	skb_dst_drop(skb);
472891ef8ddSDavid Lebrun 
473c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct iphdr));
474c71644d0SAndrea Mayer 
475891ef8ddSDavid Lebrun 	err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
476891ef8ddSDavid Lebrun 	if (err)
477891ef8ddSDavid Lebrun 		goto drop;
478891ef8ddSDavid Lebrun 
479891ef8ddSDavid Lebrun 	return dst_input(skb);
480891ef8ddSDavid Lebrun 
481891ef8ddSDavid Lebrun drop:
482891ef8ddSDavid Lebrun 	kfree_skb(skb);
483891ef8ddSDavid Lebrun 	return -EINVAL;
484891ef8ddSDavid Lebrun }
485891ef8ddSDavid Lebrun 
486664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
487664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg)
488664d6f86SAndrea Mayer {
489664d6f86SAndrea Mayer 	const struct nl_info *nli = &fib6_cfg->fc_nlinfo;
490664d6f86SAndrea Mayer 
491664d6f86SAndrea Mayer 	return nli->nl_net;
492664d6f86SAndrea Mayer }
493664d6f86SAndrea Mayer 
494664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg,
495664d6f86SAndrea Mayer 				   u16 family, struct netlink_ext_ack *extack)
496664d6f86SAndrea Mayer {
497664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
498664d6f86SAndrea Mayer 	int vrf_ifindex;
499664d6f86SAndrea Mayer 	struct net *net;
500664d6f86SAndrea Mayer 
501664d6f86SAndrea Mayer 	net = fib6_config_get_net(cfg);
502664d6f86SAndrea Mayer 
503664d6f86SAndrea Mayer 	/* note that vrf_table was already set by parse_nla_vrftable() */
504664d6f86SAndrea Mayer 	vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net,
505664d6f86SAndrea Mayer 							info->vrf_table);
506664d6f86SAndrea Mayer 	if (vrf_ifindex < 0) {
507664d6f86SAndrea Mayer 		if (vrf_ifindex == -EPERM) {
508664d6f86SAndrea Mayer 			NL_SET_ERR_MSG(extack,
509664d6f86SAndrea Mayer 				       "Strict mode for VRF is disabled");
510664d6f86SAndrea Mayer 		} else if (vrf_ifindex == -ENODEV) {
511664d6f86SAndrea Mayer 			NL_SET_ERR_MSG(extack,
512664d6f86SAndrea Mayer 				       "Table has no associated VRF device");
513664d6f86SAndrea Mayer 		} else {
514664d6f86SAndrea Mayer 			pr_debug("seg6local: SRv6 End.DT* creation error=%d\n",
515664d6f86SAndrea Mayer 				 vrf_ifindex);
516664d6f86SAndrea Mayer 		}
517664d6f86SAndrea Mayer 
518664d6f86SAndrea Mayer 		return vrf_ifindex;
519664d6f86SAndrea Mayer 	}
520664d6f86SAndrea Mayer 
521664d6f86SAndrea Mayer 	info->net = net;
522664d6f86SAndrea Mayer 	info->vrf_ifindex = vrf_ifindex;
523664d6f86SAndrea Mayer 
524664d6f86SAndrea Mayer 	info->family = family;
525664d6f86SAndrea Mayer 	info->mode = DT_VRF_MODE;
526664d6f86SAndrea Mayer 
527664d6f86SAndrea Mayer 	return 0;
528664d6f86SAndrea Mayer }
529664d6f86SAndrea Mayer 
530664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and
531664d6f86SAndrea Mayer  * routes the IPv4/IPv6 packet by looking at the configured routing table.
532664d6f86SAndrea Mayer  *
533664d6f86SAndrea Mayer  * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment
534664d6f86SAndrea Mayer  * Routing Header packets) from several interfaces and the outer IPv6
535664d6f86SAndrea Mayer  * destination address (DA) is used for retrieving the specific instance of the
536664d6f86SAndrea Mayer  * End.DT4/DT6 behavior that should process the packets.
537664d6f86SAndrea Mayer  *
538664d6f86SAndrea Mayer  * However, the inner IPv4/IPv6 packet is not really bound to any receiving
539664d6f86SAndrea Mayer  * interface and thus the End.DT4/DT6 sets the VRF (associated with the
540664d6f86SAndrea Mayer  * corresponding routing table) as the *receiving* interface.
541664d6f86SAndrea Mayer  * In other words, the End.DT4/DT6 processes a packet as if it has been received
542664d6f86SAndrea Mayer  * directly by the VRF (and not by one of its slave devices, if any).
543664d6f86SAndrea Mayer  * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in
544664d6f86SAndrea Mayer  * according to the routing table configured by the End.DT4/DT6 instance.
545664d6f86SAndrea Mayer  *
546664d6f86SAndrea Mayer  * This design allows you to get some interesting features like:
547664d6f86SAndrea Mayer  *  1) the statistics on rx packets;
548664d6f86SAndrea Mayer  *  2) the possibility to install a packet sniffer on the receiving interface
549664d6f86SAndrea Mayer  *     (the VRF one) for looking at the incoming packets;
550664d6f86SAndrea Mayer  *  3) the possibility to leverage the netfilter prerouting hook for the inner
551664d6f86SAndrea Mayer  *     IPv4 packet.
552664d6f86SAndrea Mayer  *
553664d6f86SAndrea Mayer  * This function returns:
554664d6f86SAndrea Mayer  *  - the sk_buff* when the VRF rcv handler has processed the packet correctly;
555664d6f86SAndrea Mayer  *  - NULL when the skb is consumed by the VRF rcv handler;
556664d6f86SAndrea Mayer  *  - a pointer which encodes a negative error number in case of error.
557664d6f86SAndrea Mayer  *    Note that in this case, the function takes care of freeing the skb.
558664d6f86SAndrea Mayer  */
559664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family,
560664d6f86SAndrea Mayer 				      struct net_device *dev)
561664d6f86SAndrea Mayer {
562664d6f86SAndrea Mayer 	/* based on l3mdev_ip_rcv; we are only interested in the master */
563664d6f86SAndrea Mayer 	if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev)))
564664d6f86SAndrea Mayer 		goto drop;
565664d6f86SAndrea Mayer 
566664d6f86SAndrea Mayer 	if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv))
567664d6f86SAndrea Mayer 		goto drop;
568664d6f86SAndrea Mayer 
569664d6f86SAndrea Mayer 	/* the decap packet IPv4/IPv6 does not come with any mac header info.
570664d6f86SAndrea Mayer 	 * We must unset the mac header to allow the VRF device to rebuild it,
571664d6f86SAndrea Mayer 	 * just in case there is a sniffer attached on the device.
572664d6f86SAndrea Mayer 	 */
573664d6f86SAndrea Mayer 	skb_unset_mac_header(skb);
574664d6f86SAndrea Mayer 
575664d6f86SAndrea Mayer 	skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family);
576664d6f86SAndrea Mayer 	if (!skb)
577664d6f86SAndrea Mayer 		/* the skb buffer was consumed by the handler */
578664d6f86SAndrea Mayer 		return NULL;
579664d6f86SAndrea Mayer 
580664d6f86SAndrea Mayer 	/* when a packet is received by a VRF or by one of its slaves, the
581664d6f86SAndrea Mayer 	 * master device reference is set into the skb.
582664d6f86SAndrea Mayer 	 */
583664d6f86SAndrea Mayer 	if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex))
584664d6f86SAndrea Mayer 		goto drop;
585664d6f86SAndrea Mayer 
586664d6f86SAndrea Mayer 	return skb;
587664d6f86SAndrea Mayer 
588664d6f86SAndrea Mayer drop:
589664d6f86SAndrea Mayer 	kfree_skb(skb);
590664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
591664d6f86SAndrea Mayer }
592664d6f86SAndrea Mayer 
593664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb,
594664d6f86SAndrea Mayer 					     struct seg6_end_dt_info *info)
595664d6f86SAndrea Mayer {
596664d6f86SAndrea Mayer 	int vrf_ifindex = info->vrf_ifindex;
597664d6f86SAndrea Mayer 	struct net *net = info->net;
598664d6f86SAndrea Mayer 
599664d6f86SAndrea Mayer 	if (unlikely(vrf_ifindex < 0))
600664d6f86SAndrea Mayer 		goto error;
601664d6f86SAndrea Mayer 
602664d6f86SAndrea Mayer 	if (unlikely(!net_eq(dev_net(skb->dev), net)))
603664d6f86SAndrea Mayer 		goto error;
604664d6f86SAndrea Mayer 
605664d6f86SAndrea Mayer 	return dev_get_by_index_rcu(net, vrf_ifindex);
606664d6f86SAndrea Mayer 
607664d6f86SAndrea Mayer error:
608664d6f86SAndrea Mayer 	return NULL;
609664d6f86SAndrea Mayer }
610664d6f86SAndrea Mayer 
611664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
612*8b532109SAndrea Mayer 				       struct seg6_local_lwt *slwt, u16 family)
613664d6f86SAndrea Mayer {
614664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
615664d6f86SAndrea Mayer 	struct net_device *vrf;
616*8b532109SAndrea Mayer 	__be16 protocol;
617*8b532109SAndrea Mayer 	int hdrlen;
618664d6f86SAndrea Mayer 
619664d6f86SAndrea Mayer 	vrf = end_dt_get_vrf_rcu(skb, info);
620664d6f86SAndrea Mayer 	if (unlikely(!vrf))
621664d6f86SAndrea Mayer 		goto drop;
622664d6f86SAndrea Mayer 
623*8b532109SAndrea Mayer 	switch (family) {
624*8b532109SAndrea Mayer 	case AF_INET:
625*8b532109SAndrea Mayer 		protocol = htons(ETH_P_IP);
626*8b532109SAndrea Mayer 		hdrlen = sizeof(struct iphdr);
627*8b532109SAndrea Mayer 		break;
628*8b532109SAndrea Mayer 	case AF_INET6:
629*8b532109SAndrea Mayer 		protocol = htons(ETH_P_IPV6);
630*8b532109SAndrea Mayer 		hdrlen = sizeof(struct ipv6hdr);
631*8b532109SAndrea Mayer 		break;
632*8b532109SAndrea Mayer 	case AF_UNSPEC:
633*8b532109SAndrea Mayer 		fallthrough;
634*8b532109SAndrea Mayer 	default:
635*8b532109SAndrea Mayer 		goto drop;
636*8b532109SAndrea Mayer 	}
637*8b532109SAndrea Mayer 
638*8b532109SAndrea Mayer 	if (unlikely(info->family != AF_UNSPEC && info->family != family)) {
639*8b532109SAndrea Mayer 		pr_warn_once("seg6local: SRv6 End.DT* family mismatch");
640*8b532109SAndrea Mayer 		goto drop;
641*8b532109SAndrea Mayer 	}
642*8b532109SAndrea Mayer 
643*8b532109SAndrea Mayer 	skb->protocol = protocol;
644664d6f86SAndrea Mayer 
645664d6f86SAndrea Mayer 	skb_dst_drop(skb);
646664d6f86SAndrea Mayer 
647*8b532109SAndrea Mayer 	skb_set_transport_header(skb, hdrlen);
648664d6f86SAndrea Mayer 
649*8b532109SAndrea Mayer 	return end_dt_vrf_rcv(skb, family, vrf);
650664d6f86SAndrea Mayer 
651664d6f86SAndrea Mayer drop:
652664d6f86SAndrea Mayer 	kfree_skb(skb);
653664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
654664d6f86SAndrea Mayer }
655664d6f86SAndrea Mayer 
656664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb,
657664d6f86SAndrea Mayer 				struct seg6_local_lwt *slwt)
658664d6f86SAndrea Mayer {
659664d6f86SAndrea Mayer 	struct iphdr *iph;
660664d6f86SAndrea Mayer 	int err;
661664d6f86SAndrea Mayer 
662664d6f86SAndrea Mayer 	if (!decap_and_validate(skb, IPPROTO_IPIP))
663664d6f86SAndrea Mayer 		goto drop;
664664d6f86SAndrea Mayer 
665664d6f86SAndrea Mayer 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
666664d6f86SAndrea Mayer 		goto drop;
667664d6f86SAndrea Mayer 
668*8b532109SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt, AF_INET);
669664d6f86SAndrea Mayer 	if (!skb)
670664d6f86SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
671664d6f86SAndrea Mayer 		return 0;
672664d6f86SAndrea Mayer 
673664d6f86SAndrea Mayer 	if (IS_ERR(skb))
674664d6f86SAndrea Mayer 		return PTR_ERR(skb);
675664d6f86SAndrea Mayer 
676664d6f86SAndrea Mayer 	iph = ip_hdr(skb);
677664d6f86SAndrea Mayer 
678664d6f86SAndrea Mayer 	err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev);
679664d6f86SAndrea Mayer 	if (unlikely(err))
680664d6f86SAndrea Mayer 		goto drop;
681664d6f86SAndrea Mayer 
682664d6f86SAndrea Mayer 	return dst_input(skb);
683664d6f86SAndrea Mayer 
684664d6f86SAndrea Mayer drop:
685664d6f86SAndrea Mayer 	kfree_skb(skb);
686664d6f86SAndrea Mayer 	return -EINVAL;
687664d6f86SAndrea Mayer }
688664d6f86SAndrea Mayer 
689664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg,
690664d6f86SAndrea Mayer 			      struct netlink_ext_ack *extack)
691664d6f86SAndrea Mayer {
692664d6f86SAndrea Mayer 	return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack);
693664d6f86SAndrea Mayer }
69420a081b7SAndrea Mayer 
69520a081b7SAndrea Mayer static enum
69620a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt)
69720a081b7SAndrea Mayer {
69820a081b7SAndrea Mayer 	unsigned long parsed_optattrs = slwt->parsed_optattrs;
69920a081b7SAndrea Mayer 	bool legacy, vrfmode;
70020a081b7SAndrea Mayer 
701300a0fd8SAndrea Mayer 	legacy	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE));
702300a0fd8SAndrea Mayer 	vrfmode	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE));
70320a081b7SAndrea Mayer 
70420a081b7SAndrea Mayer 	if (!(legacy ^ vrfmode))
70520a081b7SAndrea Mayer 		/* both are absent or present: invalid DT6 mode */
70620a081b7SAndrea Mayer 		return DT_INVALID_MODE;
70720a081b7SAndrea Mayer 
70820a081b7SAndrea Mayer 	return legacy ? DT_LEGACY_MODE : DT_VRF_MODE;
70920a081b7SAndrea Mayer }
71020a081b7SAndrea Mayer 
71120a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt)
71220a081b7SAndrea Mayer {
71320a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
71420a081b7SAndrea Mayer 
71520a081b7SAndrea Mayer 	return info->mode;
71620a081b7SAndrea Mayer }
71720a081b7SAndrea Mayer 
71820a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg,
71920a081b7SAndrea Mayer 			      struct netlink_ext_ack *extack)
72020a081b7SAndrea Mayer {
72120a081b7SAndrea Mayer 	enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt);
72220a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
72320a081b7SAndrea Mayer 
72420a081b7SAndrea Mayer 	switch (mode) {
72520a081b7SAndrea Mayer 	case DT_LEGACY_MODE:
72620a081b7SAndrea Mayer 		info->mode = DT_LEGACY_MODE;
72720a081b7SAndrea Mayer 		return 0;
72820a081b7SAndrea Mayer 	case DT_VRF_MODE:
72920a081b7SAndrea Mayer 		return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack);
73020a081b7SAndrea Mayer 	default:
73120a081b7SAndrea Mayer 		NL_SET_ERR_MSG(extack, "table or vrftable must be specified");
73220a081b7SAndrea Mayer 		return -EINVAL;
73320a081b7SAndrea Mayer 	}
73420a081b7SAndrea Mayer }
735664d6f86SAndrea Mayer #endif
736664d6f86SAndrea Mayer 
737891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb,
738891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
739891ef8ddSDavid Lebrun {
740891ef8ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPV6))
741891ef8ddSDavid Lebrun 		goto drop;
742891ef8ddSDavid Lebrun 
743891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
744891ef8ddSDavid Lebrun 		goto drop;
745891ef8ddSDavid Lebrun 
74620a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
74720a081b7SAndrea Mayer 	if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE)
74820a081b7SAndrea Mayer 		goto legacy_mode;
74920a081b7SAndrea Mayer 
75020a081b7SAndrea Mayer 	/* DT6_VRF_MODE */
751*8b532109SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt, AF_INET6);
75220a081b7SAndrea Mayer 	if (!skb)
75320a081b7SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
75420a081b7SAndrea Mayer 		return 0;
75520a081b7SAndrea Mayer 
75620a081b7SAndrea Mayer 	if (IS_ERR(skb))
75720a081b7SAndrea Mayer 		return PTR_ERR(skb);
75820a081b7SAndrea Mayer 
75920a081b7SAndrea Mayer 	/* note: this time we do not need to specify the table because the VRF
76020a081b7SAndrea Mayer 	 * takes care of selecting the correct table.
76120a081b7SAndrea Mayer 	 */
76220a081b7SAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, 0, true);
76320a081b7SAndrea Mayer 
76420a081b7SAndrea Mayer 	return dst_input(skb);
76520a081b7SAndrea Mayer 
76620a081b7SAndrea Mayer legacy_mode:
76720a081b7SAndrea Mayer #endif
768c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
769c71644d0SAndrea Mayer 
770fd1fef0cSAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, slwt->table, true);
771891ef8ddSDavid Lebrun 
772891ef8ddSDavid Lebrun 	return dst_input(skb);
773891ef8ddSDavid Lebrun 
774891ef8ddSDavid Lebrun drop:
775891ef8ddSDavid Lebrun 	kfree_skb(skb);
776891ef8ddSDavid Lebrun 	return -EINVAL;
777891ef8ddSDavid Lebrun }
778891ef8ddSDavid Lebrun 
779*8b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
780*8b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg,
781*8b532109SAndrea Mayer 			       struct netlink_ext_ack *extack)
782*8b532109SAndrea Mayer {
783*8b532109SAndrea Mayer 	return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack);
784*8b532109SAndrea Mayer }
785*8b532109SAndrea Mayer 
786*8b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb,
787*8b532109SAndrea Mayer 				 struct seg6_local_lwt *slwt)
788*8b532109SAndrea Mayer {
789*8b532109SAndrea Mayer 	unsigned int off = 0;
790*8b532109SAndrea Mayer 	int nexthdr;
791*8b532109SAndrea Mayer 
792*8b532109SAndrea Mayer 	nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL);
793*8b532109SAndrea Mayer 	if (unlikely(nexthdr < 0))
794*8b532109SAndrea Mayer 		goto drop;
795*8b532109SAndrea Mayer 
796*8b532109SAndrea Mayer 	switch (nexthdr) {
797*8b532109SAndrea Mayer 	case IPPROTO_IPIP:
798*8b532109SAndrea Mayer 		return input_action_end_dt4(skb, slwt);
799*8b532109SAndrea Mayer 	case IPPROTO_IPV6:
800*8b532109SAndrea Mayer 		return input_action_end_dt6(skb, slwt);
801*8b532109SAndrea Mayer 	}
802*8b532109SAndrea Mayer 
803*8b532109SAndrea Mayer drop:
804*8b532109SAndrea Mayer 	kfree_skb(skb);
805*8b532109SAndrea Mayer 	return -EINVAL;
806*8b532109SAndrea Mayer }
807*8b532109SAndrea Mayer #endif
808*8b532109SAndrea Mayer 
809140f04c3SDavid Lebrun /* push an SRH on top of the current one */
810140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
811140f04c3SDavid Lebrun {
812140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
813140f04c3SDavid Lebrun 	int err = -EINVAL;
814140f04c3SDavid Lebrun 
815140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
816140f04c3SDavid Lebrun 	if (!srh)
817140f04c3SDavid Lebrun 		goto drop;
818140f04c3SDavid Lebrun 
819140f04c3SDavid Lebrun 	err = seg6_do_srh_inline(skb, slwt->srh);
820140f04c3SDavid Lebrun 	if (err)
821140f04c3SDavid Lebrun 		goto drop;
822140f04c3SDavid Lebrun 
823140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
824140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
825140f04c3SDavid Lebrun 
8261c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
827140f04c3SDavid Lebrun 
828140f04c3SDavid Lebrun 	return dst_input(skb);
829140f04c3SDavid Lebrun 
830140f04c3SDavid Lebrun drop:
831140f04c3SDavid Lebrun 	kfree_skb(skb);
832140f04c3SDavid Lebrun 	return err;
833140f04c3SDavid Lebrun }
834140f04c3SDavid Lebrun 
835140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */
836140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb,
837140f04c3SDavid Lebrun 				     struct seg6_local_lwt *slwt)
838140f04c3SDavid Lebrun {
839140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
840140f04c3SDavid Lebrun 	int err = -EINVAL;
841140f04c3SDavid Lebrun 
842140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
843140f04c3SDavid Lebrun 	if (!srh)
844140f04c3SDavid Lebrun 		goto drop;
845140f04c3SDavid Lebrun 
846d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
847140f04c3SDavid Lebrun 
848140f04c3SDavid Lebrun 	skb_reset_inner_headers(skb);
849140f04c3SDavid Lebrun 	skb->encapsulation = 1;
850140f04c3SDavid Lebrun 
85132d99d0bSDavid Lebrun 	err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6);
852140f04c3SDavid Lebrun 	if (err)
853140f04c3SDavid Lebrun 		goto drop;
854140f04c3SDavid Lebrun 
855140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
856140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
857140f04c3SDavid Lebrun 
8581c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
859140f04c3SDavid Lebrun 
860140f04c3SDavid Lebrun 	return dst_input(skb);
861140f04c3SDavid Lebrun 
862140f04c3SDavid Lebrun drop:
863140f04c3SDavid Lebrun 	kfree_skb(skb);
864140f04c3SDavid Lebrun 	return err;
865140f04c3SDavid Lebrun }
866140f04c3SDavid Lebrun 
867fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
868fe94cc29SMathieu Xhonneux 
869486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
870486cdf21SMathieu Xhonneux {
871486cdf21SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
872486cdf21SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
873486cdf21SMathieu Xhonneux 	struct ipv6_sr_hdr *srh = srh_state->srh;
874486cdf21SMathieu Xhonneux 
875486cdf21SMathieu Xhonneux 	if (unlikely(srh == NULL))
876486cdf21SMathieu Xhonneux 		return false;
877486cdf21SMathieu Xhonneux 
878486cdf21SMathieu Xhonneux 	if (unlikely(!srh_state->valid)) {
879486cdf21SMathieu Xhonneux 		if ((srh_state->hdrlen & 7) != 0)
880486cdf21SMathieu Xhonneux 			return false;
881486cdf21SMathieu Xhonneux 
882486cdf21SMathieu Xhonneux 		srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
883bb986a50SAhmed Abdelsalam 		if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
884486cdf21SMathieu Xhonneux 			return false;
885486cdf21SMathieu Xhonneux 
886486cdf21SMathieu Xhonneux 		srh_state->valid = true;
887486cdf21SMathieu Xhonneux 	}
888486cdf21SMathieu Xhonneux 
889486cdf21SMathieu Xhonneux 	return true;
890486cdf21SMathieu Xhonneux }
891486cdf21SMathieu Xhonneux 
892004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb,
893004d4b27SMathieu Xhonneux 				struct seg6_local_lwt *slwt)
894004d4b27SMathieu Xhonneux {
895004d4b27SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
896004d4b27SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
897004d4b27SMathieu Xhonneux 	struct ipv6_sr_hdr *srh;
898004d4b27SMathieu Xhonneux 	int ret;
899004d4b27SMathieu Xhonneux 
900004d4b27SMathieu Xhonneux 	srh = get_and_validate_srh(skb);
901486cdf21SMathieu Xhonneux 	if (!srh) {
902486cdf21SMathieu Xhonneux 		kfree_skb(skb);
903486cdf21SMathieu Xhonneux 		return -EINVAL;
904486cdf21SMathieu Xhonneux 	}
905004d4b27SMathieu Xhonneux 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
906004d4b27SMathieu Xhonneux 
907004d4b27SMathieu Xhonneux 	/* preempt_disable is needed to protect the per-CPU buffer srh_state,
908004d4b27SMathieu Xhonneux 	 * which is also accessed by the bpf_lwt_seg6_* helpers
909004d4b27SMathieu Xhonneux 	 */
910004d4b27SMathieu Xhonneux 	preempt_disable();
911486cdf21SMathieu Xhonneux 	srh_state->srh = srh;
912004d4b27SMathieu Xhonneux 	srh_state->hdrlen = srh->hdrlen << 3;
913486cdf21SMathieu Xhonneux 	srh_state->valid = true;
914004d4b27SMathieu Xhonneux 
915004d4b27SMathieu Xhonneux 	rcu_read_lock();
916004d4b27SMathieu Xhonneux 	bpf_compute_data_pointers(skb);
917004d4b27SMathieu Xhonneux 	ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb);
918004d4b27SMathieu Xhonneux 	rcu_read_unlock();
919004d4b27SMathieu Xhonneux 
920004d4b27SMathieu Xhonneux 	switch (ret) {
921004d4b27SMathieu Xhonneux 	case BPF_OK:
922004d4b27SMathieu Xhonneux 	case BPF_REDIRECT:
923004d4b27SMathieu Xhonneux 		break;
924004d4b27SMathieu Xhonneux 	case BPF_DROP:
925004d4b27SMathieu Xhonneux 		goto drop;
926004d4b27SMathieu Xhonneux 	default:
927004d4b27SMathieu Xhonneux 		pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret);
928004d4b27SMathieu Xhonneux 		goto drop;
929004d4b27SMathieu Xhonneux 	}
930004d4b27SMathieu Xhonneux 
931486cdf21SMathieu Xhonneux 	if (srh_state->srh && !seg6_bpf_has_valid_srh(skb))
932004d4b27SMathieu Xhonneux 		goto drop;
933004d4b27SMathieu Xhonneux 
934486cdf21SMathieu Xhonneux 	preempt_enable();
935004d4b27SMathieu Xhonneux 	if (ret != BPF_REDIRECT)
936004d4b27SMathieu Xhonneux 		seg6_lookup_nexthop(skb, NULL, 0);
937004d4b27SMathieu Xhonneux 
938004d4b27SMathieu Xhonneux 	return dst_input(skb);
939004d4b27SMathieu Xhonneux 
940004d4b27SMathieu Xhonneux drop:
941486cdf21SMathieu Xhonneux 	preempt_enable();
942004d4b27SMathieu Xhonneux 	kfree_skb(skb);
943004d4b27SMathieu Xhonneux 	return -EINVAL;
944004d4b27SMathieu Xhonneux }
945004d4b27SMathieu Xhonneux 
946d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = {
947d1df6fd8SDavid Lebrun 	{
948d1df6fd8SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END,
949d1df6fd8SDavid Lebrun 		.attrs		= 0,
95094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
951140f04c3SDavid Lebrun 		.input		= input_action_end,
952d1df6fd8SDavid Lebrun 	},
953140f04c3SDavid Lebrun 	{
954140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_X,
955300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
95694604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
957140f04c3SDavid Lebrun 		.input		= input_action_end_x,
958140f04c3SDavid Lebrun 	},
959140f04c3SDavid Lebrun 	{
960891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_T,
961300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
96294604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
963891ef8ddSDavid Lebrun 		.input		= input_action_end_t,
964891ef8ddSDavid Lebrun 	},
965891ef8ddSDavid Lebrun 	{
966891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX2,
967300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_OIF),
96894604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
969891ef8ddSDavid Lebrun 		.input		= input_action_end_dx2,
970891ef8ddSDavid Lebrun 	},
971891ef8ddSDavid Lebrun 	{
972140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX6,
973300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
97494604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
975140f04c3SDavid Lebrun 		.input		= input_action_end_dx6,
976140f04c3SDavid Lebrun 	},
977140f04c3SDavid Lebrun 	{
978891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX4,
979300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH4),
98094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
981891ef8ddSDavid Lebrun 		.input		= input_action_end_dx4,
982891ef8ddSDavid Lebrun 	},
983891ef8ddSDavid Lebrun 	{
984664d6f86SAndrea Mayer 		.action		= SEG6_LOCAL_ACTION_END_DT4,
985300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
98694604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
987664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
988664d6f86SAndrea Mayer 		.input		= input_action_end_dt4,
989664d6f86SAndrea Mayer 		.slwt_ops	= {
990664d6f86SAndrea Mayer 					.build_state = seg6_end_dt4_build,
991664d6f86SAndrea Mayer 				  },
992664d6f86SAndrea Mayer #endif
993664d6f86SAndrea Mayer 	},
994664d6f86SAndrea Mayer 	{
995891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DT6,
99620a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
99720a081b7SAndrea Mayer 		.attrs		= 0,
99894604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS		|
99994604548SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_TABLE) |
1000300a0fd8SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
100120a081b7SAndrea Mayer 		.slwt_ops	= {
100220a081b7SAndrea Mayer 					.build_state = seg6_end_dt6_build,
100320a081b7SAndrea Mayer 				  },
100420a081b7SAndrea Mayer #else
1005300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
100694604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
100720a081b7SAndrea Mayer #endif
1008891ef8ddSDavid Lebrun 		.input		= input_action_end_dt6,
1009891ef8ddSDavid Lebrun 	},
1010891ef8ddSDavid Lebrun 	{
1011*8b532109SAndrea Mayer 		.action		= SEG6_LOCAL_ACTION_END_DT46,
1012*8b532109SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
1013*8b532109SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1014*8b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
1015*8b532109SAndrea Mayer 		.input		= input_action_end_dt46,
1016*8b532109SAndrea Mayer 		.slwt_ops	= {
1017*8b532109SAndrea Mayer 					.build_state = seg6_end_dt46_build,
1018*8b532109SAndrea Mayer 				  },
1019*8b532109SAndrea Mayer #endif
1020*8b532109SAndrea Mayer 	},
1021*8b532109SAndrea Mayer 	{
1022140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6,
1023300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
102494604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1025140f04c3SDavid Lebrun 		.input		= input_action_end_b6,
1026140f04c3SDavid Lebrun 	},
1027140f04c3SDavid Lebrun 	{
1028140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6_ENCAP,
1029300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
103094604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1031140f04c3SDavid Lebrun 		.input		= input_action_end_b6_encap,
1032140f04c3SDavid Lebrun 		.static_headroom	= sizeof(struct ipv6hdr),
1033004d4b27SMathieu Xhonneux 	},
1034004d4b27SMathieu Xhonneux 	{
1035004d4b27SMathieu Xhonneux 		.action		= SEG6_LOCAL_ACTION_END_BPF,
1036300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_BPF),
103794604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
1038004d4b27SMathieu Xhonneux 		.input		= input_action_end_bpf,
1039004d4b27SMathieu Xhonneux 	},
1040004d4b27SMathieu Xhonneux 
1041d1df6fd8SDavid Lebrun };
1042d1df6fd8SDavid Lebrun 
1043d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action)
1044d1df6fd8SDavid Lebrun {
1045d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1046d1df6fd8SDavid Lebrun 	int i, count;
1047d1df6fd8SDavid Lebrun 
1048709af180SColin Ian King 	count = ARRAY_SIZE(seg6_action_table);
1049d1df6fd8SDavid Lebrun 	for (i = 0; i < count; i++) {
1050d1df6fd8SDavid Lebrun 		desc = &seg6_action_table[i];
1051d1df6fd8SDavid Lebrun 		if (desc->action == action)
1052d1df6fd8SDavid Lebrun 			return desc;
1053d1df6fd8SDavid Lebrun 	}
1054d1df6fd8SDavid Lebrun 
1055d1df6fd8SDavid Lebrun 	return NULL;
1056d1df6fd8SDavid Lebrun }
1057d1df6fd8SDavid Lebrun 
105894604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt)
105994604548SAndrea Mayer {
106094604548SAndrea Mayer 	return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS;
106194604548SAndrea Mayer }
106294604548SAndrea Mayer 
106394604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt,
106494604548SAndrea Mayer 				       unsigned int len, int err)
106594604548SAndrea Mayer {
106694604548SAndrea Mayer 	struct pcpu_seg6_local_counters *pcounters;
106794604548SAndrea Mayer 
106894604548SAndrea Mayer 	pcounters = this_cpu_ptr(slwt->pcpu_counters);
106994604548SAndrea Mayer 	u64_stats_update_begin(&pcounters->syncp);
107094604548SAndrea Mayer 
107194604548SAndrea Mayer 	if (likely(!err)) {
107294604548SAndrea Mayer 		u64_stats_inc(&pcounters->packets);
107394604548SAndrea Mayer 		u64_stats_add(&pcounters->bytes, len);
107494604548SAndrea Mayer 	} else {
107594604548SAndrea Mayer 		u64_stats_inc(&pcounters->errors);
107694604548SAndrea Mayer 	}
107794604548SAndrea Mayer 
107894604548SAndrea Mayer 	u64_stats_update_end(&pcounters->syncp);
107994604548SAndrea Mayer }
108094604548SAndrea Mayer 
1081d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb)
1082d1df6fd8SDavid Lebrun {
1083d1df6fd8SDavid Lebrun 	struct dst_entry *orig_dst = skb_dst(skb);
1084d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1085d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
108694604548SAndrea Mayer 	unsigned int len = skb->len;
108794604548SAndrea Mayer 	int rc;
1088d1df6fd8SDavid Lebrun 
10896285217fSDavid Lebrun 	if (skb->protocol != htons(ETH_P_IPV6)) {
10906285217fSDavid Lebrun 		kfree_skb(skb);
10916285217fSDavid Lebrun 		return -EINVAL;
10926285217fSDavid Lebrun 	}
10936285217fSDavid Lebrun 
1094d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
1095d1df6fd8SDavid Lebrun 	desc = slwt->desc;
1096d1df6fd8SDavid Lebrun 
109794604548SAndrea Mayer 	rc = desc->input(skb, slwt);
109894604548SAndrea Mayer 
109994604548SAndrea Mayer 	if (!seg6_lwtunnel_counters_enabled(slwt))
110094604548SAndrea Mayer 		return rc;
110194604548SAndrea Mayer 
110294604548SAndrea Mayer 	seg6_local_update_counters(slwt, len, rc);
110394604548SAndrea Mayer 
110494604548SAndrea Mayer 	return rc;
1105d1df6fd8SDavid Lebrun }
1106d1df6fd8SDavid Lebrun 
1107d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
1108d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_ACTION]	= { .type = NLA_U32 },
1109d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .type = NLA_BINARY },
1110d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .type = NLA_U32 },
1111664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .type = NLA_U32 },
1112d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .type = NLA_BINARY,
1113d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in_addr) },
1114d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .type = NLA_BINARY,
1115d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in6_addr) },
1116d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .type = NLA_U32 },
1117d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .type = NLA_U32 },
1118004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .type = NLA_NESTED },
111994604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .type = NLA_NESTED },
1120d1df6fd8SDavid Lebrun };
1121d1df6fd8SDavid Lebrun 
11222d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
11232d9cc60aSDavid Lebrun {
11242d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
11252d9cc60aSDavid Lebrun 	int len;
11262d9cc60aSDavid Lebrun 
11272d9cc60aSDavid Lebrun 	srh = nla_data(attrs[SEG6_LOCAL_SRH]);
11282d9cc60aSDavid Lebrun 	len = nla_len(attrs[SEG6_LOCAL_SRH]);
11292d9cc60aSDavid Lebrun 
11302d9cc60aSDavid Lebrun 	/* SRH must contain at least one segment */
11312d9cc60aSDavid Lebrun 	if (len < sizeof(*srh) + sizeof(struct in6_addr))
11322d9cc60aSDavid Lebrun 		return -EINVAL;
11332d9cc60aSDavid Lebrun 
1134bb986a50SAhmed Abdelsalam 	if (!seg6_validate_srh(srh, len, false))
11352d9cc60aSDavid Lebrun 		return -EINVAL;
11362d9cc60aSDavid Lebrun 
11377fa41efaSYueHaibing 	slwt->srh = kmemdup(srh, len, GFP_KERNEL);
11382d9cc60aSDavid Lebrun 	if (!slwt->srh)
11392d9cc60aSDavid Lebrun 		return -ENOMEM;
11402d9cc60aSDavid Lebrun 
11412d9cc60aSDavid Lebrun 	slwt->headroom += len;
11422d9cc60aSDavid Lebrun 
11432d9cc60aSDavid Lebrun 	return 0;
11442d9cc60aSDavid Lebrun }
11452d9cc60aSDavid Lebrun 
11462d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt)
11472d9cc60aSDavid Lebrun {
11482d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
11492d9cc60aSDavid Lebrun 	struct nlattr *nla;
11502d9cc60aSDavid Lebrun 	int len;
11512d9cc60aSDavid Lebrun 
11522d9cc60aSDavid Lebrun 	srh = slwt->srh;
11532d9cc60aSDavid Lebrun 	len = (srh->hdrlen + 1) << 3;
11542d9cc60aSDavid Lebrun 
11552d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_SRH, len);
11562d9cc60aSDavid Lebrun 	if (!nla)
11572d9cc60aSDavid Lebrun 		return -EMSGSIZE;
11582d9cc60aSDavid Lebrun 
11592d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), srh, len);
11602d9cc60aSDavid Lebrun 
11612d9cc60aSDavid Lebrun 	return 0;
11622d9cc60aSDavid Lebrun }
11632d9cc60aSDavid Lebrun 
11642d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
11652d9cc60aSDavid Lebrun {
11662d9cc60aSDavid Lebrun 	int len = (a->srh->hdrlen + 1) << 3;
11672d9cc60aSDavid Lebrun 
11682d9cc60aSDavid Lebrun 	if (len != ((b->srh->hdrlen + 1) << 3))
11692d9cc60aSDavid Lebrun 		return 1;
11702d9cc60aSDavid Lebrun 
11712d9cc60aSDavid Lebrun 	return memcmp(a->srh, b->srh, len);
11722d9cc60aSDavid Lebrun }
11732d9cc60aSDavid Lebrun 
1174964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt)
1175964adce5SAndrea Mayer {
1176964adce5SAndrea Mayer 	kfree(slwt->srh);
1177964adce5SAndrea Mayer }
1178964adce5SAndrea Mayer 
11792d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt)
11802d9cc60aSDavid Lebrun {
11812d9cc60aSDavid Lebrun 	slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]);
11822d9cc60aSDavid Lebrun 
11832d9cc60aSDavid Lebrun 	return 0;
11842d9cc60aSDavid Lebrun }
11852d9cc60aSDavid Lebrun 
11862d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt)
11872d9cc60aSDavid Lebrun {
11882d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table))
11892d9cc60aSDavid Lebrun 		return -EMSGSIZE;
11902d9cc60aSDavid Lebrun 
11912d9cc60aSDavid Lebrun 	return 0;
11922d9cc60aSDavid Lebrun }
11932d9cc60aSDavid Lebrun 
11942d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
11952d9cc60aSDavid Lebrun {
11962d9cc60aSDavid Lebrun 	if (a->table != b->table)
11972d9cc60aSDavid Lebrun 		return 1;
11982d9cc60aSDavid Lebrun 
11992d9cc60aSDavid Lebrun 	return 0;
12002d9cc60aSDavid Lebrun }
12012d9cc60aSDavid Lebrun 
1202664d6f86SAndrea Mayer static struct
1203664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt)
1204664d6f86SAndrea Mayer {
1205664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
1206664d6f86SAndrea Mayer 	return &slwt->dt_info;
1207664d6f86SAndrea Mayer #else
1208664d6f86SAndrea Mayer 	return ERR_PTR(-EOPNOTSUPP);
1209664d6f86SAndrea Mayer #endif
1210664d6f86SAndrea Mayer }
1211664d6f86SAndrea Mayer 
1212664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs,
1213664d6f86SAndrea Mayer 			      struct seg6_local_lwt *slwt)
1214664d6f86SAndrea Mayer {
1215664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1216664d6f86SAndrea Mayer 
1217664d6f86SAndrea Mayer 	if (IS_ERR(info))
1218664d6f86SAndrea Mayer 		return PTR_ERR(info);
1219664d6f86SAndrea Mayer 
1220664d6f86SAndrea Mayer 	info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]);
1221664d6f86SAndrea Mayer 
1222664d6f86SAndrea Mayer 	return 0;
1223664d6f86SAndrea Mayer }
1224664d6f86SAndrea Mayer 
1225664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1226664d6f86SAndrea Mayer {
1227664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1228664d6f86SAndrea Mayer 
1229664d6f86SAndrea Mayer 	if (IS_ERR(info))
1230664d6f86SAndrea Mayer 		return PTR_ERR(info);
1231664d6f86SAndrea Mayer 
1232664d6f86SAndrea Mayer 	if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table))
1233664d6f86SAndrea Mayer 		return -EMSGSIZE;
1234664d6f86SAndrea Mayer 
1235664d6f86SAndrea Mayer 	return 0;
1236664d6f86SAndrea Mayer }
1237664d6f86SAndrea Mayer 
1238664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1239664d6f86SAndrea Mayer {
1240664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a);
1241664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b);
1242664d6f86SAndrea Mayer 
1243664d6f86SAndrea Mayer 	if (info_a->vrf_table != info_b->vrf_table)
1244664d6f86SAndrea Mayer 		return 1;
1245664d6f86SAndrea Mayer 
1246664d6f86SAndrea Mayer 	return 0;
1247664d6f86SAndrea Mayer }
1248664d6f86SAndrea Mayer 
12492d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12502d9cc60aSDavid Lebrun {
12512d9cc60aSDavid Lebrun 	memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]),
12522d9cc60aSDavid Lebrun 	       sizeof(struct in_addr));
12532d9cc60aSDavid Lebrun 
12542d9cc60aSDavid Lebrun 	return 0;
12552d9cc60aSDavid Lebrun }
12562d9cc60aSDavid Lebrun 
12572d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12582d9cc60aSDavid Lebrun {
12592d9cc60aSDavid Lebrun 	struct nlattr *nla;
12602d9cc60aSDavid Lebrun 
12612d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr));
12622d9cc60aSDavid Lebrun 	if (!nla)
12632d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12642d9cc60aSDavid Lebrun 
12652d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr));
12662d9cc60aSDavid Lebrun 
12672d9cc60aSDavid Lebrun 	return 0;
12682d9cc60aSDavid Lebrun }
12692d9cc60aSDavid Lebrun 
12702d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12712d9cc60aSDavid Lebrun {
12722d9cc60aSDavid Lebrun 	return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr));
12732d9cc60aSDavid Lebrun }
12742d9cc60aSDavid Lebrun 
12752d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12762d9cc60aSDavid Lebrun {
12772d9cc60aSDavid Lebrun 	memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]),
12782d9cc60aSDavid Lebrun 	       sizeof(struct in6_addr));
12792d9cc60aSDavid Lebrun 
12802d9cc60aSDavid Lebrun 	return 0;
12812d9cc60aSDavid Lebrun }
12822d9cc60aSDavid Lebrun 
12832d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12842d9cc60aSDavid Lebrun {
12852d9cc60aSDavid Lebrun 	struct nlattr *nla;
12862d9cc60aSDavid Lebrun 
12872d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr));
12882d9cc60aSDavid Lebrun 	if (!nla)
12892d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12902d9cc60aSDavid Lebrun 
12912d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr));
12922d9cc60aSDavid Lebrun 
12932d9cc60aSDavid Lebrun 	return 0;
12942d9cc60aSDavid Lebrun }
12952d9cc60aSDavid Lebrun 
12962d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12972d9cc60aSDavid Lebrun {
12982d9cc60aSDavid Lebrun 	return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr));
12992d9cc60aSDavid Lebrun }
13002d9cc60aSDavid Lebrun 
13012d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
13022d9cc60aSDavid Lebrun {
13032d9cc60aSDavid Lebrun 	slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]);
13042d9cc60aSDavid Lebrun 
13052d9cc60aSDavid Lebrun 	return 0;
13062d9cc60aSDavid Lebrun }
13072d9cc60aSDavid Lebrun 
13082d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13092d9cc60aSDavid Lebrun {
13102d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif))
13112d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13122d9cc60aSDavid Lebrun 
13132d9cc60aSDavid Lebrun 	return 0;
13142d9cc60aSDavid Lebrun }
13152d9cc60aSDavid Lebrun 
13162d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13172d9cc60aSDavid Lebrun {
13182d9cc60aSDavid Lebrun 	if (a->iif != b->iif)
13192d9cc60aSDavid Lebrun 		return 1;
13202d9cc60aSDavid Lebrun 
13212d9cc60aSDavid Lebrun 	return 0;
13222d9cc60aSDavid Lebrun }
13232d9cc60aSDavid Lebrun 
13242d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
13252d9cc60aSDavid Lebrun {
13262d9cc60aSDavid Lebrun 	slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]);
13272d9cc60aSDavid Lebrun 
13282d9cc60aSDavid Lebrun 	return 0;
13292d9cc60aSDavid Lebrun }
13302d9cc60aSDavid Lebrun 
13312d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
13322d9cc60aSDavid Lebrun {
13332d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif))
13342d9cc60aSDavid Lebrun 		return -EMSGSIZE;
13352d9cc60aSDavid Lebrun 
13362d9cc60aSDavid Lebrun 	return 0;
13372d9cc60aSDavid Lebrun }
13382d9cc60aSDavid Lebrun 
13392d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
13402d9cc60aSDavid Lebrun {
13412d9cc60aSDavid Lebrun 	if (a->oif != b->oif)
13422d9cc60aSDavid Lebrun 		return 1;
13432d9cc60aSDavid Lebrun 
13442d9cc60aSDavid Lebrun 	return 0;
13452d9cc60aSDavid Lebrun }
13462d9cc60aSDavid Lebrun 
1347004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256
1348004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
1349004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG]	   = { .type = NLA_U32, },
1350004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
1351004d4b27SMathieu Xhonneux 				       .len = MAX_PROG_NAME },
1352004d4b27SMathieu Xhonneux };
1353004d4b27SMathieu Xhonneux 
1354004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1355004d4b27SMathieu Xhonneux {
1356004d4b27SMathieu Xhonneux 	struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
1357004d4b27SMathieu Xhonneux 	struct bpf_prog *p;
1358004d4b27SMathieu Xhonneux 	int ret;
1359004d4b27SMathieu Xhonneux 	u32 fd;
1360004d4b27SMathieu Xhonneux 
13618cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX,
13628cb08174SJohannes Berg 					  attrs[SEG6_LOCAL_BPF],
13638cb08174SJohannes Berg 					  bpf_prog_policy, NULL);
1364004d4b27SMathieu Xhonneux 	if (ret < 0)
1365004d4b27SMathieu Xhonneux 		return ret;
1366004d4b27SMathieu Xhonneux 
1367004d4b27SMathieu Xhonneux 	if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME])
1368004d4b27SMathieu Xhonneux 		return -EINVAL;
1369004d4b27SMathieu Xhonneux 
1370004d4b27SMathieu Xhonneux 	slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL);
1371004d4b27SMathieu Xhonneux 	if (!slwt->bpf.name)
1372004d4b27SMathieu Xhonneux 		return -ENOMEM;
1373004d4b27SMathieu Xhonneux 
1374004d4b27SMathieu Xhonneux 	fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]);
1375004d4b27SMathieu Xhonneux 	p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL);
1376004d4b27SMathieu Xhonneux 	if (IS_ERR(p)) {
1377004d4b27SMathieu Xhonneux 		kfree(slwt->bpf.name);
1378004d4b27SMathieu Xhonneux 		return PTR_ERR(p);
1379004d4b27SMathieu Xhonneux 	}
1380004d4b27SMathieu Xhonneux 
1381004d4b27SMathieu Xhonneux 	slwt->bpf.prog = p;
1382004d4b27SMathieu Xhonneux 	return 0;
1383004d4b27SMathieu Xhonneux }
1384004d4b27SMathieu Xhonneux 
1385004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1386004d4b27SMathieu Xhonneux {
1387004d4b27SMathieu Xhonneux 	struct nlattr *nest;
1388004d4b27SMathieu Xhonneux 
1389004d4b27SMathieu Xhonneux 	if (!slwt->bpf.prog)
1390004d4b27SMathieu Xhonneux 		return 0;
1391004d4b27SMathieu Xhonneux 
1392ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF);
1393004d4b27SMathieu Xhonneux 	if (!nest)
1394004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1395004d4b27SMathieu Xhonneux 
1396004d4b27SMathieu Xhonneux 	if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id))
1397004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1398004d4b27SMathieu Xhonneux 
1399004d4b27SMathieu Xhonneux 	if (slwt->bpf.name &&
1400004d4b27SMathieu Xhonneux 	    nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name))
1401004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1402004d4b27SMathieu Xhonneux 
1403004d4b27SMathieu Xhonneux 	return nla_nest_end(skb, nest);
1404004d4b27SMathieu Xhonneux }
1405004d4b27SMathieu Xhonneux 
1406004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1407004d4b27SMathieu Xhonneux {
1408004d4b27SMathieu Xhonneux 	if (!a->bpf.name && !b->bpf.name)
1409004d4b27SMathieu Xhonneux 		return 0;
1410004d4b27SMathieu Xhonneux 
1411004d4b27SMathieu Xhonneux 	if (!a->bpf.name || !b->bpf.name)
1412004d4b27SMathieu Xhonneux 		return 1;
1413004d4b27SMathieu Xhonneux 
1414004d4b27SMathieu Xhonneux 	return strcmp(a->bpf.name, b->bpf.name);
1415004d4b27SMathieu Xhonneux }
1416004d4b27SMathieu Xhonneux 
1417964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt)
1418964adce5SAndrea Mayer {
1419964adce5SAndrea Mayer 	kfree(slwt->bpf.name);
1420964adce5SAndrea Mayer 	if (slwt->bpf.prog)
1421964adce5SAndrea Mayer 		bpf_prog_put(slwt->bpf.prog);
1422964adce5SAndrea Mayer }
1423964adce5SAndrea Mayer 
142494604548SAndrea Mayer static const struct
142594604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = {
142694604548SAndrea Mayer 	[SEG6_LOCAL_CNT_PACKETS]	= { .type = NLA_U64 },
142794604548SAndrea Mayer 	[SEG6_LOCAL_CNT_BYTES]		= { .type = NLA_U64 },
142894604548SAndrea Mayer 	[SEG6_LOCAL_CNT_ERRORS]		= { .type = NLA_U64 },
142994604548SAndrea Mayer };
143094604548SAndrea Mayer 
143194604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs,
143294604548SAndrea Mayer 			      struct seg6_local_lwt *slwt)
143394604548SAndrea Mayer {
143494604548SAndrea Mayer 	struct pcpu_seg6_local_counters __percpu *pcounters;
143594604548SAndrea Mayer 	struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1];
143694604548SAndrea Mayer 	int ret;
143794604548SAndrea Mayer 
143894604548SAndrea Mayer 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX,
143994604548SAndrea Mayer 					  attrs[SEG6_LOCAL_COUNTERS],
144094604548SAndrea Mayer 					  seg6_local_counters_policy, NULL);
144194604548SAndrea Mayer 	if (ret < 0)
144294604548SAndrea Mayer 		return ret;
144394604548SAndrea Mayer 
144494604548SAndrea Mayer 	/* basic support for SRv6 Behavior counters requires at least:
144594604548SAndrea Mayer 	 * packets, bytes and errors.
144694604548SAndrea Mayer 	 */
144794604548SAndrea Mayer 	if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] ||
144894604548SAndrea Mayer 	    !tb[SEG6_LOCAL_CNT_ERRORS])
144994604548SAndrea Mayer 		return -EINVAL;
145094604548SAndrea Mayer 
145194604548SAndrea Mayer 	/* counters are always zero initialized */
145294604548SAndrea Mayer 	pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL);
145394604548SAndrea Mayer 	if (!pcounters)
145494604548SAndrea Mayer 		return -ENOMEM;
145594604548SAndrea Mayer 
145694604548SAndrea Mayer 	slwt->pcpu_counters = pcounters;
145794604548SAndrea Mayer 
145894604548SAndrea Mayer 	return 0;
145994604548SAndrea Mayer }
146094604548SAndrea Mayer 
146194604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb,
146294604548SAndrea Mayer 					struct seg6_local_counters *counters)
146394604548SAndrea Mayer {
146494604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets,
146594604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
146694604548SAndrea Mayer 		return -EMSGSIZE;
146794604548SAndrea Mayer 
146894604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes,
146994604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
147094604548SAndrea Mayer 		return -EMSGSIZE;
147194604548SAndrea Mayer 
147294604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors,
147394604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
147494604548SAndrea Mayer 		return -EMSGSIZE;
147594604548SAndrea Mayer 
147694604548SAndrea Mayer 	return 0;
147794604548SAndrea Mayer }
147894604548SAndrea Mayer 
147994604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt)
148094604548SAndrea Mayer {
148194604548SAndrea Mayer 	struct seg6_local_counters counters = { 0, 0, 0 };
148294604548SAndrea Mayer 	struct nlattr *nest;
148394604548SAndrea Mayer 	int rc, i;
148494604548SAndrea Mayer 
148594604548SAndrea Mayer 	nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS);
148694604548SAndrea Mayer 	if (!nest)
148794604548SAndrea Mayer 		return -EMSGSIZE;
148894604548SAndrea Mayer 
148994604548SAndrea Mayer 	for_each_possible_cpu(i) {
149094604548SAndrea Mayer 		struct pcpu_seg6_local_counters *pcounters;
149194604548SAndrea Mayer 		u64 packets, bytes, errors;
149294604548SAndrea Mayer 		unsigned int start;
149394604548SAndrea Mayer 
149494604548SAndrea Mayer 		pcounters = per_cpu_ptr(slwt->pcpu_counters, i);
149594604548SAndrea Mayer 		do {
149694604548SAndrea Mayer 			start = u64_stats_fetch_begin_irq(&pcounters->syncp);
149794604548SAndrea Mayer 
149894604548SAndrea Mayer 			packets = u64_stats_read(&pcounters->packets);
149994604548SAndrea Mayer 			bytes = u64_stats_read(&pcounters->bytes);
150094604548SAndrea Mayer 			errors = u64_stats_read(&pcounters->errors);
150194604548SAndrea Mayer 
150294604548SAndrea Mayer 		} while (u64_stats_fetch_retry_irq(&pcounters->syncp, start));
150394604548SAndrea Mayer 
150494604548SAndrea Mayer 		counters.packets += packets;
150594604548SAndrea Mayer 		counters.bytes += bytes;
150694604548SAndrea Mayer 		counters.errors += errors;
150794604548SAndrea Mayer 	}
150894604548SAndrea Mayer 
150994604548SAndrea Mayer 	rc = seg6_local_fill_nla_counters(skb, &counters);
151094604548SAndrea Mayer 	if (rc < 0) {
151194604548SAndrea Mayer 		nla_nest_cancel(skb, nest);
151294604548SAndrea Mayer 		return rc;
151394604548SAndrea Mayer 	}
151494604548SAndrea Mayer 
151594604548SAndrea Mayer 	return nla_nest_end(skb, nest);
151694604548SAndrea Mayer }
151794604548SAndrea Mayer 
151894604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
151994604548SAndrea Mayer {
152094604548SAndrea Mayer 	/* a and b are equal if both have pcpu_counters set or not */
152194604548SAndrea Mayer 	return (!!((unsigned long)a->pcpu_counters)) ^
152294604548SAndrea Mayer 		(!!((unsigned long)b->pcpu_counters));
152394604548SAndrea Mayer }
152494604548SAndrea Mayer 
152594604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt)
152694604548SAndrea Mayer {
152794604548SAndrea Mayer 	free_percpu(slwt->pcpu_counters);
152894604548SAndrea Mayer }
152994604548SAndrea Mayer 
1530d1df6fd8SDavid Lebrun struct seg6_action_param {
1531d1df6fd8SDavid Lebrun 	int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
1532d1df6fd8SDavid Lebrun 	int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
1533d1df6fd8SDavid Lebrun 	int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
1534964adce5SAndrea Mayer 
1535964adce5SAndrea Mayer 	/* optional destroy() callback useful for releasing resources which
1536964adce5SAndrea Mayer 	 * have been previously acquired in the corresponding parse()
1537964adce5SAndrea Mayer 	 * function.
1538964adce5SAndrea Mayer 	 */
1539964adce5SAndrea Mayer 	void (*destroy)(struct seg6_local_lwt *slwt);
1540d1df6fd8SDavid Lebrun };
1541d1df6fd8SDavid Lebrun 
1542d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
15432d9cc60aSDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .parse = parse_nla_srh,
15442d9cc60aSDavid Lebrun 				    .put = put_nla_srh,
1545964adce5SAndrea Mayer 				    .cmp = cmp_nla_srh,
1546964adce5SAndrea Mayer 				    .destroy = destroy_attr_srh },
1547d1df6fd8SDavid Lebrun 
15482d9cc60aSDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .parse = parse_nla_table,
15492d9cc60aSDavid Lebrun 				    .put = put_nla_table,
15502d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_table },
1551d1df6fd8SDavid Lebrun 
15522d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .parse = parse_nla_nh4,
15532d9cc60aSDavid Lebrun 				    .put = put_nla_nh4,
15542d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh4 },
1555d1df6fd8SDavid Lebrun 
15562d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .parse = parse_nla_nh6,
15572d9cc60aSDavid Lebrun 				    .put = put_nla_nh6,
15582d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh6 },
1559d1df6fd8SDavid Lebrun 
15602d9cc60aSDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .parse = parse_nla_iif,
15612d9cc60aSDavid Lebrun 				    .put = put_nla_iif,
15622d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_iif },
1563d1df6fd8SDavid Lebrun 
15642d9cc60aSDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .parse = parse_nla_oif,
15652d9cc60aSDavid Lebrun 				    .put = put_nla_oif,
15662d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_oif },
1567004d4b27SMathieu Xhonneux 
1568004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .parse = parse_nla_bpf,
1569004d4b27SMathieu Xhonneux 				    .put = put_nla_bpf,
1570964adce5SAndrea Mayer 				    .cmp = cmp_nla_bpf,
1571964adce5SAndrea Mayer 				    .destroy = destroy_attr_bpf },
1572004d4b27SMathieu Xhonneux 
1573664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .parse = parse_nla_vrftable,
1574664d6f86SAndrea Mayer 				    .put = put_nla_vrftable,
1575664d6f86SAndrea Mayer 				    .cmp = cmp_nla_vrftable },
1576664d6f86SAndrea Mayer 
157794604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .parse = parse_nla_counters,
157894604548SAndrea Mayer 				    .put = put_nla_counters,
157994604548SAndrea Mayer 				    .cmp = cmp_nla_counters,
158094604548SAndrea Mayer 				    .destroy = destroy_attr_counters },
1581d1df6fd8SDavid Lebrun };
1582d1df6fd8SDavid Lebrun 
1583964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in
15840a3021f1SAndrea Mayer  * @parsed_attrs, starting from the first attribute up to the @max_parsed
15850a3021f1SAndrea Mayer  * (excluded) attribute.
1586964adce5SAndrea Mayer  */
15870a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed,
15880a3021f1SAndrea Mayer 			    struct seg6_local_lwt *slwt)
1589964adce5SAndrea Mayer {
1590964adce5SAndrea Mayer 	struct seg6_action_param *param;
1591964adce5SAndrea Mayer 	int i;
1592964adce5SAndrea Mayer 
1593964adce5SAndrea Mayer 	/* Every required seg6local attribute is identified by an ID which is
1594964adce5SAndrea Mayer 	 * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask;
1595964adce5SAndrea Mayer 	 *
15960a3021f1SAndrea Mayer 	 * We scan the 'parsed_attrs' bitmask, starting from the first attribute
1597964adce5SAndrea Mayer 	 * up to the @max_parsed (excluded) attribute.
1598964adce5SAndrea Mayer 	 * For each set attribute, we retrieve the corresponding destroy()
1599964adce5SAndrea Mayer 	 * callback. If the callback is not available, then we skip to the next
1600964adce5SAndrea Mayer 	 * attribute; otherwise, we call the destroy() callback.
1601964adce5SAndrea Mayer 	 */
1602964adce5SAndrea Mayer 	for (i = 0; i < max_parsed; ++i) {
1603300a0fd8SAndrea Mayer 		if (!(parsed_attrs & SEG6_F_ATTR(i)))
1604964adce5SAndrea Mayer 			continue;
1605964adce5SAndrea Mayer 
1606964adce5SAndrea Mayer 		param = &seg6_action_params[i];
1607964adce5SAndrea Mayer 
1608964adce5SAndrea Mayer 		if (param->destroy)
1609964adce5SAndrea Mayer 			param->destroy(slwt);
1610964adce5SAndrea Mayer 	}
1611964adce5SAndrea Mayer }
1612964adce5SAndrea Mayer 
1613964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing
1614964adce5SAndrea Mayer  * operations.
1615964adce5SAndrea Mayer  */
1616964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt)
1617964adce5SAndrea Mayer {
16180a3021f1SAndrea Mayer 	unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs;
16190a3021f1SAndrea Mayer 
16200a3021f1SAndrea Mayer 	__destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt);
16210a3021f1SAndrea Mayer }
16220a3021f1SAndrea Mayer 
16230a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs,
16240a3021f1SAndrea Mayer 				    struct seg6_local_lwt *slwt)
16250a3021f1SAndrea Mayer {
16260a3021f1SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
16270a3021f1SAndrea Mayer 	unsigned long parsed_optattrs = 0;
16280a3021f1SAndrea Mayer 	struct seg6_action_param *param;
16290a3021f1SAndrea Mayer 	int err, i;
16300a3021f1SAndrea Mayer 
16310a3021f1SAndrea Mayer 	for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) {
1632300a0fd8SAndrea Mayer 		if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i])
16330a3021f1SAndrea Mayer 			continue;
16340a3021f1SAndrea Mayer 
16350a3021f1SAndrea Mayer 		/* once here, the i-th attribute is provided by the
16360a3021f1SAndrea Mayer 		 * userspace AND it is identified optional as well.
16370a3021f1SAndrea Mayer 		 */
16380a3021f1SAndrea Mayer 		param = &seg6_action_params[i];
16390a3021f1SAndrea Mayer 
16400a3021f1SAndrea Mayer 		err = param->parse(attrs, slwt);
16410a3021f1SAndrea Mayer 		if (err < 0)
16420a3021f1SAndrea Mayer 			goto parse_optattrs_err;
16430a3021f1SAndrea Mayer 
16440a3021f1SAndrea Mayer 		/* current attribute has been correctly parsed */
1645300a0fd8SAndrea Mayer 		parsed_optattrs |= SEG6_F_ATTR(i);
16460a3021f1SAndrea Mayer 	}
16470a3021f1SAndrea Mayer 
16480a3021f1SAndrea Mayer 	/* store in the tunnel state all the optional attributed successfully
16490a3021f1SAndrea Mayer 	 * parsed.
16500a3021f1SAndrea Mayer 	 */
16510a3021f1SAndrea Mayer 	slwt->parsed_optattrs = parsed_optattrs;
16520a3021f1SAndrea Mayer 
16530a3021f1SAndrea Mayer 	return 0;
16540a3021f1SAndrea Mayer 
16550a3021f1SAndrea Mayer parse_optattrs_err:
16560a3021f1SAndrea Mayer 	__destroy_attrs(parsed_optattrs, i, slwt);
16570a3021f1SAndrea Mayer 
16580a3021f1SAndrea Mayer 	return err;
1659964adce5SAndrea Mayer }
1660964adce5SAndrea Mayer 
1661cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase
1662cfdf64a0SAndrea Mayer  * and after that all its attributes have been parsed successfully.
1663cfdf64a0SAndrea Mayer  */
1664cfdf64a0SAndrea Mayer static int
1665cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg,
1666cfdf64a0SAndrea Mayer 				struct netlink_ext_ack *extack)
1667cfdf64a0SAndrea Mayer {
1668cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1669cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1670cfdf64a0SAndrea Mayer 
1671cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1672cfdf64a0SAndrea Mayer 	if (!ops->build_state)
1673cfdf64a0SAndrea Mayer 		return 0;
1674cfdf64a0SAndrea Mayer 
1675cfdf64a0SAndrea Mayer 	return ops->build_state(slwt, cfg, extack);
1676cfdf64a0SAndrea Mayer }
1677cfdf64a0SAndrea Mayer 
1678cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the
1679cfdf64a0SAndrea Mayer  * tunnel is going to be destroyed.
1680cfdf64a0SAndrea Mayer  */
1681cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
1682cfdf64a0SAndrea Mayer {
1683cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1684cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1685cfdf64a0SAndrea Mayer 
1686cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1687cfdf64a0SAndrea Mayer 	if (!ops->destroy_state)
1688cfdf64a0SAndrea Mayer 		return;
1689cfdf64a0SAndrea Mayer 
1690cfdf64a0SAndrea Mayer 	ops->destroy_state(slwt);
1691cfdf64a0SAndrea Mayer }
1692cfdf64a0SAndrea Mayer 
1693d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1694d1df6fd8SDavid Lebrun {
1695d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
1696d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
16970a3021f1SAndrea Mayer 	unsigned long invalid_attrs;
1698d1df6fd8SDavid Lebrun 	int i, err;
1699d1df6fd8SDavid Lebrun 
1700d1df6fd8SDavid Lebrun 	desc = __get_action_desc(slwt->action);
1701d1df6fd8SDavid Lebrun 	if (!desc)
1702d1df6fd8SDavid Lebrun 		return -EINVAL;
1703d1df6fd8SDavid Lebrun 
1704d1df6fd8SDavid Lebrun 	if (!desc->input)
1705d1df6fd8SDavid Lebrun 		return -EOPNOTSUPP;
1706d1df6fd8SDavid Lebrun 
1707d1df6fd8SDavid Lebrun 	slwt->desc = desc;
1708d1df6fd8SDavid Lebrun 	slwt->headroom += desc->static_headroom;
1709d1df6fd8SDavid Lebrun 
17100a3021f1SAndrea Mayer 	/* Forcing the desc->optattrs *set* and the desc->attrs *set* to be
17110a3021f1SAndrea Mayer 	 * disjoined, this allow us to release acquired resources by optional
17120a3021f1SAndrea Mayer 	 * attributes and by required attributes independently from each other
17130d770360SAndrea Mayer 	 * without any interference.
17140a3021f1SAndrea Mayer 	 * In other terms, we are sure that we do not release some the acquired
17150a3021f1SAndrea Mayer 	 * resources twice.
17160a3021f1SAndrea Mayer 	 *
17170a3021f1SAndrea Mayer 	 * Note that if an attribute is configured both as required and as
17180a3021f1SAndrea Mayer 	 * optional, it means that the user has messed something up in the
17190a3021f1SAndrea Mayer 	 * seg6_action_table. Therefore, this check is required for SRv6
17200a3021f1SAndrea Mayer 	 * behaviors to work properly.
17210a3021f1SAndrea Mayer 	 */
17220a3021f1SAndrea Mayer 	invalid_attrs = desc->attrs & desc->optattrs;
17230a3021f1SAndrea Mayer 	if (invalid_attrs) {
17240a3021f1SAndrea Mayer 		WARN_ONCE(1,
17250a3021f1SAndrea Mayer 			  "An attribute cannot be both required AND optional");
17260a3021f1SAndrea Mayer 		return -EINVAL;
17270a3021f1SAndrea Mayer 	}
17280a3021f1SAndrea Mayer 
17290a3021f1SAndrea Mayer 	/* parse the required attributes */
1730d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1731300a0fd8SAndrea Mayer 		if (desc->attrs & SEG6_F_ATTR(i)) {
1732d1df6fd8SDavid Lebrun 			if (!attrs[i])
1733d1df6fd8SDavid Lebrun 				return -EINVAL;
1734d1df6fd8SDavid Lebrun 
1735d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1736d1df6fd8SDavid Lebrun 
1737d1df6fd8SDavid Lebrun 			err = param->parse(attrs, slwt);
1738d1df6fd8SDavid Lebrun 			if (err < 0)
17390a3021f1SAndrea Mayer 				goto parse_attrs_err;
1740d1df6fd8SDavid Lebrun 		}
1741d1df6fd8SDavid Lebrun 	}
1742d1df6fd8SDavid Lebrun 
17430a3021f1SAndrea Mayer 	/* parse the optional attributes, if any */
17440a3021f1SAndrea Mayer 	err = parse_nla_optional_attrs(attrs, slwt);
17450a3021f1SAndrea Mayer 	if (err < 0)
17460a3021f1SAndrea Mayer 		goto parse_attrs_err;
17470a3021f1SAndrea Mayer 
1748d1df6fd8SDavid Lebrun 	return 0;
1749964adce5SAndrea Mayer 
17500a3021f1SAndrea Mayer parse_attrs_err:
1751964adce5SAndrea Mayer 	/* release any resource that may have been acquired during the i-1
1752964adce5SAndrea Mayer 	 * parse() operations.
1753964adce5SAndrea Mayer 	 */
17540a3021f1SAndrea Mayer 	__destroy_attrs(desc->attrs, i, slwt);
1755964adce5SAndrea Mayer 
1756964adce5SAndrea Mayer 	return err;
1757d1df6fd8SDavid Lebrun }
1758d1df6fd8SDavid Lebrun 
1759faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla,
1760faee6769SAlexander Aring 				  unsigned int family, const void *cfg,
1761faee6769SAlexander Aring 				  struct lwtunnel_state **ts,
1762d1df6fd8SDavid Lebrun 				  struct netlink_ext_ack *extack)
1763d1df6fd8SDavid Lebrun {
1764d1df6fd8SDavid Lebrun 	struct nlattr *tb[SEG6_LOCAL_MAX + 1];
1765d1df6fd8SDavid Lebrun 	struct lwtunnel_state *newts;
1766d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
1767d1df6fd8SDavid Lebrun 	int err;
1768d1df6fd8SDavid Lebrun 
17696285217fSDavid Lebrun 	if (family != AF_INET6)
17706285217fSDavid Lebrun 		return -EINVAL;
17716285217fSDavid Lebrun 
17728cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla,
17738cb08174SJohannes Berg 					  seg6_local_policy, extack);
1774d1df6fd8SDavid Lebrun 
1775d1df6fd8SDavid Lebrun 	if (err < 0)
1776d1df6fd8SDavid Lebrun 		return err;
1777d1df6fd8SDavid Lebrun 
1778d1df6fd8SDavid Lebrun 	if (!tb[SEG6_LOCAL_ACTION])
1779d1df6fd8SDavid Lebrun 		return -EINVAL;
1780d1df6fd8SDavid Lebrun 
1781d1df6fd8SDavid Lebrun 	newts = lwtunnel_state_alloc(sizeof(*slwt));
1782d1df6fd8SDavid Lebrun 	if (!newts)
1783d1df6fd8SDavid Lebrun 		return -ENOMEM;
1784d1df6fd8SDavid Lebrun 
1785d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(newts);
1786d1df6fd8SDavid Lebrun 	slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
1787d1df6fd8SDavid Lebrun 
1788d1df6fd8SDavid Lebrun 	err = parse_nla_action(tb, slwt);
1789d1df6fd8SDavid Lebrun 	if (err < 0)
1790d1df6fd8SDavid Lebrun 		goto out_free;
1791d1df6fd8SDavid Lebrun 
1792cfdf64a0SAndrea Mayer 	err = seg6_local_lwtunnel_build_state(slwt, cfg, extack);
1793cfdf64a0SAndrea Mayer 	if (err < 0)
1794cfdf64a0SAndrea Mayer 		goto out_destroy_attrs;
1795cfdf64a0SAndrea Mayer 
1796d1df6fd8SDavid Lebrun 	newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
1797d1df6fd8SDavid Lebrun 	newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
1798d1df6fd8SDavid Lebrun 	newts->headroom = slwt->headroom;
1799d1df6fd8SDavid Lebrun 
1800d1df6fd8SDavid Lebrun 	*ts = newts;
1801d1df6fd8SDavid Lebrun 
1802d1df6fd8SDavid Lebrun 	return 0;
1803d1df6fd8SDavid Lebrun 
1804cfdf64a0SAndrea Mayer out_destroy_attrs:
1805cfdf64a0SAndrea Mayer 	destroy_attrs(slwt);
1806d1df6fd8SDavid Lebrun out_free:
1807d1df6fd8SDavid Lebrun 	kfree(newts);
1808d1df6fd8SDavid Lebrun 	return err;
1809d1df6fd8SDavid Lebrun }
1810d1df6fd8SDavid Lebrun 
1811d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
1812d1df6fd8SDavid Lebrun {
1813d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1814d1df6fd8SDavid Lebrun 
1815cfdf64a0SAndrea Mayer 	seg6_local_lwtunnel_destroy_state(slwt);
1816cfdf64a0SAndrea Mayer 
1817964adce5SAndrea Mayer 	destroy_attrs(slwt);
1818004d4b27SMathieu Xhonneux 
1819004d4b27SMathieu Xhonneux 	return;
1820d1df6fd8SDavid Lebrun }
1821d1df6fd8SDavid Lebrun 
1822d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb,
1823d1df6fd8SDavid Lebrun 				 struct lwtunnel_state *lwt)
1824d1df6fd8SDavid Lebrun {
1825d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1826d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
18270a3021f1SAndrea Mayer 	unsigned long attrs;
1828d1df6fd8SDavid Lebrun 	int i, err;
1829d1df6fd8SDavid Lebrun 
1830d1df6fd8SDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
1831d1df6fd8SDavid Lebrun 		return -EMSGSIZE;
1832d1df6fd8SDavid Lebrun 
18330a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
18340a3021f1SAndrea Mayer 
1835d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1836300a0fd8SAndrea Mayer 		if (attrs & SEG6_F_ATTR(i)) {
1837d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1838d1df6fd8SDavid Lebrun 			err = param->put(skb, slwt);
1839d1df6fd8SDavid Lebrun 			if (err < 0)
1840d1df6fd8SDavid Lebrun 				return err;
1841d1df6fd8SDavid Lebrun 		}
1842d1df6fd8SDavid Lebrun 	}
1843d1df6fd8SDavid Lebrun 
1844d1df6fd8SDavid Lebrun 	return 0;
1845d1df6fd8SDavid Lebrun }
1846d1df6fd8SDavid Lebrun 
1847d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
1848d1df6fd8SDavid Lebrun {
1849d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1850d1df6fd8SDavid Lebrun 	unsigned long attrs;
1851d1df6fd8SDavid Lebrun 	int nlsize;
1852d1df6fd8SDavid Lebrun 
1853d1df6fd8SDavid Lebrun 	nlsize = nla_total_size(4); /* action */
1854d1df6fd8SDavid Lebrun 
18550a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
1856d1df6fd8SDavid Lebrun 
1857300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH))
1858d1df6fd8SDavid Lebrun 		nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
1859d1df6fd8SDavid Lebrun 
1860300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE))
1861d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1862d1df6fd8SDavid Lebrun 
1863300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4))
1864d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1865d1df6fd8SDavid Lebrun 
1866300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6))
1867d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(16);
1868d1df6fd8SDavid Lebrun 
1869300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF))
1870d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1871d1df6fd8SDavid Lebrun 
1872300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF))
1873d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1874d1df6fd8SDavid Lebrun 
1875300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF))
1876004d4b27SMathieu Xhonneux 		nlsize += nla_total_size(sizeof(struct nlattr)) +
1877004d4b27SMathieu Xhonneux 		       nla_total_size(MAX_PROG_NAME) +
1878004d4b27SMathieu Xhonneux 		       nla_total_size(4);
1879004d4b27SMathieu Xhonneux 
1880300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE))
1881664d6f86SAndrea Mayer 		nlsize += nla_total_size(4);
1882664d6f86SAndrea Mayer 
188394604548SAndrea Mayer 	if (attrs & SEG6_F_LOCAL_COUNTERS)
188494604548SAndrea Mayer 		nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */
188594604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_PACKETS */
188694604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
188794604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_BYTES */
188894604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
188994604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_ERRORS */
189094604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64));
189194604548SAndrea Mayer 
1892d1df6fd8SDavid Lebrun 	return nlsize;
1893d1df6fd8SDavid Lebrun }
1894d1df6fd8SDavid Lebrun 
1895d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a,
1896d1df6fd8SDavid Lebrun 				struct lwtunnel_state *b)
1897d1df6fd8SDavid Lebrun {
1898d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt_a, *slwt_b;
1899d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
19000a3021f1SAndrea Mayer 	unsigned long attrs_a, attrs_b;
1901d1df6fd8SDavid Lebrun 	int i;
1902d1df6fd8SDavid Lebrun 
1903d1df6fd8SDavid Lebrun 	slwt_a = seg6_local_lwtunnel(a);
1904d1df6fd8SDavid Lebrun 	slwt_b = seg6_local_lwtunnel(b);
1905d1df6fd8SDavid Lebrun 
1906d1df6fd8SDavid Lebrun 	if (slwt_a->action != slwt_b->action)
1907d1df6fd8SDavid Lebrun 		return 1;
1908d1df6fd8SDavid Lebrun 
19090a3021f1SAndrea Mayer 	attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs;
19100a3021f1SAndrea Mayer 	attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs;
19110a3021f1SAndrea Mayer 
19120a3021f1SAndrea Mayer 	if (attrs_a != attrs_b)
1913d1df6fd8SDavid Lebrun 		return 1;
1914d1df6fd8SDavid Lebrun 
1915d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1916300a0fd8SAndrea Mayer 		if (attrs_a & SEG6_F_ATTR(i)) {
1917d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1918d1df6fd8SDavid Lebrun 			if (param->cmp(slwt_a, slwt_b))
1919d1df6fd8SDavid Lebrun 				return 1;
1920d1df6fd8SDavid Lebrun 		}
1921d1df6fd8SDavid Lebrun 	}
1922d1df6fd8SDavid Lebrun 
1923d1df6fd8SDavid Lebrun 	return 0;
1924d1df6fd8SDavid Lebrun }
1925d1df6fd8SDavid Lebrun 
1926d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = {
1927d1df6fd8SDavid Lebrun 	.build_state	= seg6_local_build_state,
1928d1df6fd8SDavid Lebrun 	.destroy_state	= seg6_local_destroy_state,
1929d1df6fd8SDavid Lebrun 	.input		= seg6_local_input,
1930d1df6fd8SDavid Lebrun 	.fill_encap	= seg6_local_fill_encap,
1931d1df6fd8SDavid Lebrun 	.get_encap_size	= seg6_local_get_encap_size,
1932d1df6fd8SDavid Lebrun 	.cmp_encap	= seg6_local_cmp_encap,
1933d1df6fd8SDavid Lebrun 	.owner		= THIS_MODULE,
1934d1df6fd8SDavid Lebrun };
1935d1df6fd8SDavid Lebrun 
1936d1df6fd8SDavid Lebrun int __init seg6_local_init(void)
1937d1df6fd8SDavid Lebrun {
1938300a0fd8SAndrea Mayer 	/* If the max total number of defined attributes is reached, then your
1939300a0fd8SAndrea Mayer 	 * kernel build stops here.
1940300a0fd8SAndrea Mayer 	 *
1941300a0fd8SAndrea Mayer 	 * This check is required to avoid arithmetic overflows when processing
1942300a0fd8SAndrea Mayer 	 * behavior attributes and the maximum number of defined attributes
1943300a0fd8SAndrea Mayer 	 * exceeds the allowed value.
1944300a0fd8SAndrea Mayer 	 */
1945300a0fd8SAndrea Mayer 	BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long));
1946300a0fd8SAndrea Mayer 
1947d1df6fd8SDavid Lebrun 	return lwtunnel_encap_add_ops(&seg6_local_ops,
1948d1df6fd8SDavid Lebrun 				      LWTUNNEL_ENCAP_SEG6_LOCAL);
1949d1df6fd8SDavid Lebrun }
1950d1df6fd8SDavid Lebrun 
1951d1df6fd8SDavid Lebrun void seg6_local_exit(void)
1952d1df6fd8SDavid Lebrun {
1953d1df6fd8SDavid Lebrun 	lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
1954d1df6fd8SDavid Lebrun }
1955