xref: /openbmc/linux/net/ipv6/seg6_local.c (revision 94604548aa7163fa14b837149bb0cb708bc613bc)
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 
90664d6f86SAndrea Mayer 	/* tunneled packet proto and family (IPv4 or IPv6) */
91664d6f86SAndrea Mayer 	__be16 proto;
92664d6f86SAndrea Mayer 	u16 family;
93664d6f86SAndrea Mayer 	int hdrlen;
94664d6f86SAndrea Mayer };
95664d6f86SAndrea Mayer 
96*94604548SAndrea Mayer struct pcpu_seg6_local_counters {
97*94604548SAndrea Mayer 	u64_stats_t packets;
98*94604548SAndrea Mayer 	u64_stats_t bytes;
99*94604548SAndrea Mayer 	u64_stats_t errors;
100*94604548SAndrea Mayer 
101*94604548SAndrea Mayer 	struct u64_stats_sync syncp;
102*94604548SAndrea Mayer };
103*94604548SAndrea Mayer 
104*94604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far.
105*94604548SAndrea Mayer  *
106*94604548SAndrea Mayer  * put_nla_counters() makes use of this data structure to collect all counter
107*94604548SAndrea Mayer  * values after the per-CPU counter evaluation has been performed.
108*94604548SAndrea Mayer  * Finally, each counter value (in seg6_local_counters) is stored in the
109*94604548SAndrea Mayer  * corresponding netlink attribute and sent to user space.
110*94604548SAndrea Mayer  *
111*94604548SAndrea Mayer  * NB: we don't want to expose this structure to user space!
112*94604548SAndrea Mayer  */
113*94604548SAndrea Mayer struct seg6_local_counters {
114*94604548SAndrea Mayer 	__u64 packets;
115*94604548SAndrea Mayer 	__u64 bytes;
116*94604548SAndrea Mayer 	__u64 errors;
117*94604548SAndrea Mayer };
118*94604548SAndrea Mayer 
119*94604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp)				\
120*94604548SAndrea Mayer 	__netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters,	\
121*94604548SAndrea Mayer 				  ((__gfp) | __GFP_ZERO))
122*94604548SAndrea Mayer 
123*94604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS	SEG6_F_ATTR(SEG6_LOCAL_COUNTERS)
124*94604548SAndrea 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
137*94604548SAndrea 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 	switch (family) {
525664d6f86SAndrea Mayer 	case AF_INET:
526664d6f86SAndrea Mayer 		info->proto = htons(ETH_P_IP);
527664d6f86SAndrea Mayer 		info->hdrlen = sizeof(struct iphdr);
528664d6f86SAndrea Mayer 		break;
52920a081b7SAndrea Mayer 	case AF_INET6:
53020a081b7SAndrea Mayer 		info->proto = htons(ETH_P_IPV6);
53120a081b7SAndrea Mayer 		info->hdrlen = sizeof(struct ipv6hdr);
53220a081b7SAndrea Mayer 		break;
533664d6f86SAndrea Mayer 	default:
534664d6f86SAndrea Mayer 		return -EINVAL;
535664d6f86SAndrea Mayer 	}
536664d6f86SAndrea Mayer 
537664d6f86SAndrea Mayer 	info->family = family;
538664d6f86SAndrea Mayer 	info->mode = DT_VRF_MODE;
539664d6f86SAndrea Mayer 
540664d6f86SAndrea Mayer 	return 0;
541664d6f86SAndrea Mayer }
542664d6f86SAndrea Mayer 
543664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and
544664d6f86SAndrea Mayer  * routes the IPv4/IPv6 packet by looking at the configured routing table.
545664d6f86SAndrea Mayer  *
546664d6f86SAndrea Mayer  * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment
547664d6f86SAndrea Mayer  * Routing Header packets) from several interfaces and the outer IPv6
548664d6f86SAndrea Mayer  * destination address (DA) is used for retrieving the specific instance of the
549664d6f86SAndrea Mayer  * End.DT4/DT6 behavior that should process the packets.
550664d6f86SAndrea Mayer  *
551664d6f86SAndrea Mayer  * However, the inner IPv4/IPv6 packet is not really bound to any receiving
552664d6f86SAndrea Mayer  * interface and thus the End.DT4/DT6 sets the VRF (associated with the
553664d6f86SAndrea Mayer  * corresponding routing table) as the *receiving* interface.
554664d6f86SAndrea Mayer  * In other words, the End.DT4/DT6 processes a packet as if it has been received
555664d6f86SAndrea Mayer  * directly by the VRF (and not by one of its slave devices, if any).
556664d6f86SAndrea Mayer  * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in
557664d6f86SAndrea Mayer  * according to the routing table configured by the End.DT4/DT6 instance.
558664d6f86SAndrea Mayer  *
559664d6f86SAndrea Mayer  * This design allows you to get some interesting features like:
560664d6f86SAndrea Mayer  *  1) the statistics on rx packets;
561664d6f86SAndrea Mayer  *  2) the possibility to install a packet sniffer on the receiving interface
562664d6f86SAndrea Mayer  *     (the VRF one) for looking at the incoming packets;
563664d6f86SAndrea Mayer  *  3) the possibility to leverage the netfilter prerouting hook for the inner
564664d6f86SAndrea Mayer  *     IPv4 packet.
565664d6f86SAndrea Mayer  *
566664d6f86SAndrea Mayer  * This function returns:
567664d6f86SAndrea Mayer  *  - the sk_buff* when the VRF rcv handler has processed the packet correctly;
568664d6f86SAndrea Mayer  *  - NULL when the skb is consumed by the VRF rcv handler;
569664d6f86SAndrea Mayer  *  - a pointer which encodes a negative error number in case of error.
570664d6f86SAndrea Mayer  *    Note that in this case, the function takes care of freeing the skb.
571664d6f86SAndrea Mayer  */
572664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family,
573664d6f86SAndrea Mayer 				      struct net_device *dev)
574664d6f86SAndrea Mayer {
575664d6f86SAndrea Mayer 	/* based on l3mdev_ip_rcv; we are only interested in the master */
576664d6f86SAndrea Mayer 	if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev)))
577664d6f86SAndrea Mayer 		goto drop;
578664d6f86SAndrea Mayer 
579664d6f86SAndrea Mayer 	if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv))
580664d6f86SAndrea Mayer 		goto drop;
581664d6f86SAndrea Mayer 
582664d6f86SAndrea Mayer 	/* the decap packet IPv4/IPv6 does not come with any mac header info.
583664d6f86SAndrea Mayer 	 * We must unset the mac header to allow the VRF device to rebuild it,
584664d6f86SAndrea Mayer 	 * just in case there is a sniffer attached on the device.
585664d6f86SAndrea Mayer 	 */
586664d6f86SAndrea Mayer 	skb_unset_mac_header(skb);
587664d6f86SAndrea Mayer 
588664d6f86SAndrea Mayer 	skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family);
589664d6f86SAndrea Mayer 	if (!skb)
590664d6f86SAndrea Mayer 		/* the skb buffer was consumed by the handler */
591664d6f86SAndrea Mayer 		return NULL;
592664d6f86SAndrea Mayer 
593664d6f86SAndrea Mayer 	/* when a packet is received by a VRF or by one of its slaves, the
594664d6f86SAndrea Mayer 	 * master device reference is set into the skb.
595664d6f86SAndrea Mayer 	 */
596664d6f86SAndrea Mayer 	if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex))
597664d6f86SAndrea Mayer 		goto drop;
598664d6f86SAndrea Mayer 
599664d6f86SAndrea Mayer 	return skb;
600664d6f86SAndrea Mayer 
601664d6f86SAndrea Mayer drop:
602664d6f86SAndrea Mayer 	kfree_skb(skb);
603664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
604664d6f86SAndrea Mayer }
605664d6f86SAndrea Mayer 
606664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb,
607664d6f86SAndrea Mayer 					     struct seg6_end_dt_info *info)
608664d6f86SAndrea Mayer {
609664d6f86SAndrea Mayer 	int vrf_ifindex = info->vrf_ifindex;
610664d6f86SAndrea Mayer 	struct net *net = info->net;
611664d6f86SAndrea Mayer 
612664d6f86SAndrea Mayer 	if (unlikely(vrf_ifindex < 0))
613664d6f86SAndrea Mayer 		goto error;
614664d6f86SAndrea Mayer 
615664d6f86SAndrea Mayer 	if (unlikely(!net_eq(dev_net(skb->dev), net)))
616664d6f86SAndrea Mayer 		goto error;
617664d6f86SAndrea Mayer 
618664d6f86SAndrea Mayer 	return dev_get_by_index_rcu(net, vrf_ifindex);
619664d6f86SAndrea Mayer 
620664d6f86SAndrea Mayer error:
621664d6f86SAndrea Mayer 	return NULL;
622664d6f86SAndrea Mayer }
623664d6f86SAndrea Mayer 
624664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
625664d6f86SAndrea Mayer 				       struct seg6_local_lwt *slwt)
626664d6f86SAndrea Mayer {
627664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
628664d6f86SAndrea Mayer 	struct net_device *vrf;
629664d6f86SAndrea Mayer 
630664d6f86SAndrea Mayer 	vrf = end_dt_get_vrf_rcu(skb, info);
631664d6f86SAndrea Mayer 	if (unlikely(!vrf))
632664d6f86SAndrea Mayer 		goto drop;
633664d6f86SAndrea Mayer 
634664d6f86SAndrea Mayer 	skb->protocol = info->proto;
635664d6f86SAndrea Mayer 
636664d6f86SAndrea Mayer 	skb_dst_drop(skb);
637664d6f86SAndrea Mayer 
638664d6f86SAndrea Mayer 	skb_set_transport_header(skb, info->hdrlen);
639664d6f86SAndrea Mayer 
640664d6f86SAndrea Mayer 	return end_dt_vrf_rcv(skb, info->family, vrf);
641664d6f86SAndrea Mayer 
642664d6f86SAndrea Mayer drop:
643664d6f86SAndrea Mayer 	kfree_skb(skb);
644664d6f86SAndrea Mayer 	return ERR_PTR(-EINVAL);
645664d6f86SAndrea Mayer }
646664d6f86SAndrea Mayer 
647664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb,
648664d6f86SAndrea Mayer 				struct seg6_local_lwt *slwt)
649664d6f86SAndrea Mayer {
650664d6f86SAndrea Mayer 	struct iphdr *iph;
651664d6f86SAndrea Mayer 	int err;
652664d6f86SAndrea Mayer 
653664d6f86SAndrea Mayer 	if (!decap_and_validate(skb, IPPROTO_IPIP))
654664d6f86SAndrea Mayer 		goto drop;
655664d6f86SAndrea Mayer 
656664d6f86SAndrea Mayer 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
657664d6f86SAndrea Mayer 		goto drop;
658664d6f86SAndrea Mayer 
659664d6f86SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt);
660664d6f86SAndrea Mayer 	if (!skb)
661664d6f86SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
662664d6f86SAndrea Mayer 		return 0;
663664d6f86SAndrea Mayer 
664664d6f86SAndrea Mayer 	if (IS_ERR(skb))
665664d6f86SAndrea Mayer 		return PTR_ERR(skb);
666664d6f86SAndrea Mayer 
667664d6f86SAndrea Mayer 	iph = ip_hdr(skb);
668664d6f86SAndrea Mayer 
669664d6f86SAndrea Mayer 	err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev);
670664d6f86SAndrea Mayer 	if (unlikely(err))
671664d6f86SAndrea Mayer 		goto drop;
672664d6f86SAndrea Mayer 
673664d6f86SAndrea Mayer 	return dst_input(skb);
674664d6f86SAndrea Mayer 
675664d6f86SAndrea Mayer drop:
676664d6f86SAndrea Mayer 	kfree_skb(skb);
677664d6f86SAndrea Mayer 	return -EINVAL;
678664d6f86SAndrea Mayer }
679664d6f86SAndrea Mayer 
680664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg,
681664d6f86SAndrea Mayer 			      struct netlink_ext_ack *extack)
682664d6f86SAndrea Mayer {
683664d6f86SAndrea Mayer 	return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack);
684664d6f86SAndrea Mayer }
68520a081b7SAndrea Mayer 
68620a081b7SAndrea Mayer static enum
68720a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt)
68820a081b7SAndrea Mayer {
68920a081b7SAndrea Mayer 	unsigned long parsed_optattrs = slwt->parsed_optattrs;
69020a081b7SAndrea Mayer 	bool legacy, vrfmode;
69120a081b7SAndrea Mayer 
692300a0fd8SAndrea Mayer 	legacy	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE));
693300a0fd8SAndrea Mayer 	vrfmode	= !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE));
69420a081b7SAndrea Mayer 
69520a081b7SAndrea Mayer 	if (!(legacy ^ vrfmode))
69620a081b7SAndrea Mayer 		/* both are absent or present: invalid DT6 mode */
69720a081b7SAndrea Mayer 		return DT_INVALID_MODE;
69820a081b7SAndrea Mayer 
69920a081b7SAndrea Mayer 	return legacy ? DT_LEGACY_MODE : DT_VRF_MODE;
70020a081b7SAndrea Mayer }
70120a081b7SAndrea Mayer 
70220a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt)
70320a081b7SAndrea Mayer {
70420a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
70520a081b7SAndrea Mayer 
70620a081b7SAndrea Mayer 	return info->mode;
70720a081b7SAndrea Mayer }
70820a081b7SAndrea Mayer 
70920a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg,
71020a081b7SAndrea Mayer 			      struct netlink_ext_ack *extack)
71120a081b7SAndrea Mayer {
71220a081b7SAndrea Mayer 	enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt);
71320a081b7SAndrea Mayer 	struct seg6_end_dt_info *info = &slwt->dt_info;
71420a081b7SAndrea Mayer 
71520a081b7SAndrea Mayer 	switch (mode) {
71620a081b7SAndrea Mayer 	case DT_LEGACY_MODE:
71720a081b7SAndrea Mayer 		info->mode = DT_LEGACY_MODE;
71820a081b7SAndrea Mayer 		return 0;
71920a081b7SAndrea Mayer 	case DT_VRF_MODE:
72020a081b7SAndrea Mayer 		return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack);
72120a081b7SAndrea Mayer 	default:
72220a081b7SAndrea Mayer 		NL_SET_ERR_MSG(extack, "table or vrftable must be specified");
72320a081b7SAndrea Mayer 		return -EINVAL;
72420a081b7SAndrea Mayer 	}
72520a081b7SAndrea Mayer }
726664d6f86SAndrea Mayer #endif
727664d6f86SAndrea Mayer 
728891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb,
729891ef8ddSDavid Lebrun 				struct seg6_local_lwt *slwt)
730891ef8ddSDavid Lebrun {
731891ef8ddSDavid Lebrun 	if (!decap_and_validate(skb, IPPROTO_IPV6))
732891ef8ddSDavid Lebrun 		goto drop;
733891ef8ddSDavid Lebrun 
734891ef8ddSDavid Lebrun 	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
735891ef8ddSDavid Lebrun 		goto drop;
736891ef8ddSDavid Lebrun 
73720a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
73820a081b7SAndrea Mayer 	if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE)
73920a081b7SAndrea Mayer 		goto legacy_mode;
74020a081b7SAndrea Mayer 
74120a081b7SAndrea Mayer 	/* DT6_VRF_MODE */
74220a081b7SAndrea Mayer 	skb = end_dt_vrf_core(skb, slwt);
74320a081b7SAndrea Mayer 	if (!skb)
74420a081b7SAndrea Mayer 		/* packet has been processed and consumed by the VRF */
74520a081b7SAndrea Mayer 		return 0;
74620a081b7SAndrea Mayer 
74720a081b7SAndrea Mayer 	if (IS_ERR(skb))
74820a081b7SAndrea Mayer 		return PTR_ERR(skb);
74920a081b7SAndrea Mayer 
75020a081b7SAndrea Mayer 	/* note: this time we do not need to specify the table because the VRF
75120a081b7SAndrea Mayer 	 * takes care of selecting the correct table.
75220a081b7SAndrea Mayer 	 */
75320a081b7SAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, 0, true);
75420a081b7SAndrea Mayer 
75520a081b7SAndrea Mayer 	return dst_input(skb);
75620a081b7SAndrea Mayer 
75720a081b7SAndrea Mayer legacy_mode:
75820a081b7SAndrea Mayer #endif
759c71644d0SAndrea Mayer 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
760c71644d0SAndrea Mayer 
761fd1fef0cSAndrea Mayer 	seg6_lookup_any_nexthop(skb, NULL, slwt->table, true);
762891ef8ddSDavid Lebrun 
763891ef8ddSDavid Lebrun 	return dst_input(skb);
764891ef8ddSDavid Lebrun 
765891ef8ddSDavid Lebrun drop:
766891ef8ddSDavid Lebrun 	kfree_skb(skb);
767891ef8ddSDavid Lebrun 	return -EINVAL;
768891ef8ddSDavid Lebrun }
769891ef8ddSDavid Lebrun 
770140f04c3SDavid Lebrun /* push an SRH on top of the current one */
771140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
772140f04c3SDavid Lebrun {
773140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
774140f04c3SDavid Lebrun 	int err = -EINVAL;
775140f04c3SDavid Lebrun 
776140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
777140f04c3SDavid Lebrun 	if (!srh)
778140f04c3SDavid Lebrun 		goto drop;
779140f04c3SDavid Lebrun 
780140f04c3SDavid Lebrun 	err = seg6_do_srh_inline(skb, slwt->srh);
781140f04c3SDavid Lebrun 	if (err)
782140f04c3SDavid Lebrun 		goto drop;
783140f04c3SDavid Lebrun 
784140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
785140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
786140f04c3SDavid Lebrun 
7871c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
788140f04c3SDavid Lebrun 
789140f04c3SDavid Lebrun 	return dst_input(skb);
790140f04c3SDavid Lebrun 
791140f04c3SDavid Lebrun drop:
792140f04c3SDavid Lebrun 	kfree_skb(skb);
793140f04c3SDavid Lebrun 	return err;
794140f04c3SDavid Lebrun }
795140f04c3SDavid Lebrun 
796140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */
797140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb,
798140f04c3SDavid Lebrun 				     struct seg6_local_lwt *slwt)
799140f04c3SDavid Lebrun {
800140f04c3SDavid Lebrun 	struct ipv6_sr_hdr *srh;
801140f04c3SDavid Lebrun 	int err = -EINVAL;
802140f04c3SDavid Lebrun 
803140f04c3SDavid Lebrun 	srh = get_and_validate_srh(skb);
804140f04c3SDavid Lebrun 	if (!srh)
805140f04c3SDavid Lebrun 		goto drop;
806140f04c3SDavid Lebrun 
807d7a669ddSDavid Lebrun 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
808140f04c3SDavid Lebrun 
809140f04c3SDavid Lebrun 	skb_reset_inner_headers(skb);
810140f04c3SDavid Lebrun 	skb->encapsulation = 1;
811140f04c3SDavid Lebrun 
81232d99d0bSDavid Lebrun 	err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6);
813140f04c3SDavid Lebrun 	if (err)
814140f04c3SDavid Lebrun 		goto drop;
815140f04c3SDavid Lebrun 
816140f04c3SDavid Lebrun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
817140f04c3SDavid Lebrun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
818140f04c3SDavid Lebrun 
8191c1e761eSMathieu Xhonneux 	seg6_lookup_nexthop(skb, NULL, 0);
820140f04c3SDavid Lebrun 
821140f04c3SDavid Lebrun 	return dst_input(skb);
822140f04c3SDavid Lebrun 
823140f04c3SDavid Lebrun drop:
824140f04c3SDavid Lebrun 	kfree_skb(skb);
825140f04c3SDavid Lebrun 	return err;
826140f04c3SDavid Lebrun }
827140f04c3SDavid Lebrun 
828fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
829fe94cc29SMathieu Xhonneux 
830486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
831486cdf21SMathieu Xhonneux {
832486cdf21SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
833486cdf21SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
834486cdf21SMathieu Xhonneux 	struct ipv6_sr_hdr *srh = srh_state->srh;
835486cdf21SMathieu Xhonneux 
836486cdf21SMathieu Xhonneux 	if (unlikely(srh == NULL))
837486cdf21SMathieu Xhonneux 		return false;
838486cdf21SMathieu Xhonneux 
839486cdf21SMathieu Xhonneux 	if (unlikely(!srh_state->valid)) {
840486cdf21SMathieu Xhonneux 		if ((srh_state->hdrlen & 7) != 0)
841486cdf21SMathieu Xhonneux 			return false;
842486cdf21SMathieu Xhonneux 
843486cdf21SMathieu Xhonneux 		srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
844bb986a50SAhmed Abdelsalam 		if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
845486cdf21SMathieu Xhonneux 			return false;
846486cdf21SMathieu Xhonneux 
847486cdf21SMathieu Xhonneux 		srh_state->valid = true;
848486cdf21SMathieu Xhonneux 	}
849486cdf21SMathieu Xhonneux 
850486cdf21SMathieu Xhonneux 	return true;
851486cdf21SMathieu Xhonneux }
852486cdf21SMathieu Xhonneux 
853004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb,
854004d4b27SMathieu Xhonneux 				struct seg6_local_lwt *slwt)
855004d4b27SMathieu Xhonneux {
856004d4b27SMathieu Xhonneux 	struct seg6_bpf_srh_state *srh_state =
857004d4b27SMathieu Xhonneux 		this_cpu_ptr(&seg6_bpf_srh_states);
858004d4b27SMathieu Xhonneux 	struct ipv6_sr_hdr *srh;
859004d4b27SMathieu Xhonneux 	int ret;
860004d4b27SMathieu Xhonneux 
861004d4b27SMathieu Xhonneux 	srh = get_and_validate_srh(skb);
862486cdf21SMathieu Xhonneux 	if (!srh) {
863486cdf21SMathieu Xhonneux 		kfree_skb(skb);
864486cdf21SMathieu Xhonneux 		return -EINVAL;
865486cdf21SMathieu Xhonneux 	}
866004d4b27SMathieu Xhonneux 	advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
867004d4b27SMathieu Xhonneux 
868004d4b27SMathieu Xhonneux 	/* preempt_disable is needed to protect the per-CPU buffer srh_state,
869004d4b27SMathieu Xhonneux 	 * which is also accessed by the bpf_lwt_seg6_* helpers
870004d4b27SMathieu Xhonneux 	 */
871004d4b27SMathieu Xhonneux 	preempt_disable();
872486cdf21SMathieu Xhonneux 	srh_state->srh = srh;
873004d4b27SMathieu Xhonneux 	srh_state->hdrlen = srh->hdrlen << 3;
874486cdf21SMathieu Xhonneux 	srh_state->valid = true;
875004d4b27SMathieu Xhonneux 
876004d4b27SMathieu Xhonneux 	rcu_read_lock();
877004d4b27SMathieu Xhonneux 	bpf_compute_data_pointers(skb);
878004d4b27SMathieu Xhonneux 	ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb);
879004d4b27SMathieu Xhonneux 	rcu_read_unlock();
880004d4b27SMathieu Xhonneux 
881004d4b27SMathieu Xhonneux 	switch (ret) {
882004d4b27SMathieu Xhonneux 	case BPF_OK:
883004d4b27SMathieu Xhonneux 	case BPF_REDIRECT:
884004d4b27SMathieu Xhonneux 		break;
885004d4b27SMathieu Xhonneux 	case BPF_DROP:
886004d4b27SMathieu Xhonneux 		goto drop;
887004d4b27SMathieu Xhonneux 	default:
888004d4b27SMathieu Xhonneux 		pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret);
889004d4b27SMathieu Xhonneux 		goto drop;
890004d4b27SMathieu Xhonneux 	}
891004d4b27SMathieu Xhonneux 
892486cdf21SMathieu Xhonneux 	if (srh_state->srh && !seg6_bpf_has_valid_srh(skb))
893004d4b27SMathieu Xhonneux 		goto drop;
894004d4b27SMathieu Xhonneux 
895486cdf21SMathieu Xhonneux 	preempt_enable();
896004d4b27SMathieu Xhonneux 	if (ret != BPF_REDIRECT)
897004d4b27SMathieu Xhonneux 		seg6_lookup_nexthop(skb, NULL, 0);
898004d4b27SMathieu Xhonneux 
899004d4b27SMathieu Xhonneux 	return dst_input(skb);
900004d4b27SMathieu Xhonneux 
901004d4b27SMathieu Xhonneux drop:
902486cdf21SMathieu Xhonneux 	preempt_enable();
903004d4b27SMathieu Xhonneux 	kfree_skb(skb);
904004d4b27SMathieu Xhonneux 	return -EINVAL;
905004d4b27SMathieu Xhonneux }
906004d4b27SMathieu Xhonneux 
907d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = {
908d1df6fd8SDavid Lebrun 	{
909d1df6fd8SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END,
910d1df6fd8SDavid Lebrun 		.attrs		= 0,
911*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
912140f04c3SDavid Lebrun 		.input		= input_action_end,
913d1df6fd8SDavid Lebrun 	},
914140f04c3SDavid Lebrun 	{
915140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_X,
916300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
917*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
918140f04c3SDavid Lebrun 		.input		= input_action_end_x,
919140f04c3SDavid Lebrun 	},
920140f04c3SDavid Lebrun 	{
921891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_T,
922300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
923*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
924891ef8ddSDavid Lebrun 		.input		= input_action_end_t,
925891ef8ddSDavid Lebrun 	},
926891ef8ddSDavid Lebrun 	{
927891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX2,
928300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_OIF),
929*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
930891ef8ddSDavid Lebrun 		.input		= input_action_end_dx2,
931891ef8ddSDavid Lebrun 	},
932891ef8ddSDavid Lebrun 	{
933140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX6,
934300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH6),
935*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
936140f04c3SDavid Lebrun 		.input		= input_action_end_dx6,
937140f04c3SDavid Lebrun 	},
938140f04c3SDavid Lebrun 	{
939891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DX4,
940300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_NH4),
941*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
942891ef8ddSDavid Lebrun 		.input		= input_action_end_dx4,
943891ef8ddSDavid Lebrun 	},
944891ef8ddSDavid Lebrun 	{
945664d6f86SAndrea Mayer 		.action		= SEG6_LOCAL_ACTION_END_DT4,
946300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
947*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
948664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
949664d6f86SAndrea Mayer 		.input		= input_action_end_dt4,
950664d6f86SAndrea Mayer 		.slwt_ops	= {
951664d6f86SAndrea Mayer 					.build_state = seg6_end_dt4_build,
952664d6f86SAndrea Mayer 				  },
953664d6f86SAndrea Mayer #endif
954664d6f86SAndrea Mayer 	},
955664d6f86SAndrea Mayer 	{
956891ef8ddSDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_DT6,
95720a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
95820a081b7SAndrea Mayer 		.attrs		= 0,
959*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS		|
960*94604548SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_TABLE) |
961300a0fd8SAndrea Mayer 				  SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
96220a081b7SAndrea Mayer 		.slwt_ops	= {
96320a081b7SAndrea Mayer 					.build_state = seg6_end_dt6_build,
96420a081b7SAndrea Mayer 				  },
96520a081b7SAndrea Mayer #else
966300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_TABLE),
967*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
96820a081b7SAndrea Mayer #endif
969891ef8ddSDavid Lebrun 		.input		= input_action_end_dt6,
970891ef8ddSDavid Lebrun 	},
971891ef8ddSDavid Lebrun 	{
972140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6,
973300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
974*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
975140f04c3SDavid Lebrun 		.input		= input_action_end_b6,
976140f04c3SDavid Lebrun 	},
977140f04c3SDavid Lebrun 	{
978140f04c3SDavid Lebrun 		.action		= SEG6_LOCAL_ACTION_END_B6_ENCAP,
979300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_SRH),
980*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
981140f04c3SDavid Lebrun 		.input		= input_action_end_b6_encap,
982140f04c3SDavid Lebrun 		.static_headroom	= sizeof(struct ipv6hdr),
983004d4b27SMathieu Xhonneux 	},
984004d4b27SMathieu Xhonneux 	{
985004d4b27SMathieu Xhonneux 		.action		= SEG6_LOCAL_ACTION_END_BPF,
986300a0fd8SAndrea Mayer 		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_BPF),
987*94604548SAndrea Mayer 		.optattrs	= SEG6_F_LOCAL_COUNTERS,
988004d4b27SMathieu Xhonneux 		.input		= input_action_end_bpf,
989004d4b27SMathieu Xhonneux 	},
990004d4b27SMathieu Xhonneux 
991d1df6fd8SDavid Lebrun };
992d1df6fd8SDavid Lebrun 
993d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action)
994d1df6fd8SDavid Lebrun {
995d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
996d1df6fd8SDavid Lebrun 	int i, count;
997d1df6fd8SDavid Lebrun 
998709af180SColin Ian King 	count = ARRAY_SIZE(seg6_action_table);
999d1df6fd8SDavid Lebrun 	for (i = 0; i < count; i++) {
1000d1df6fd8SDavid Lebrun 		desc = &seg6_action_table[i];
1001d1df6fd8SDavid Lebrun 		if (desc->action == action)
1002d1df6fd8SDavid Lebrun 			return desc;
1003d1df6fd8SDavid Lebrun 	}
1004d1df6fd8SDavid Lebrun 
1005d1df6fd8SDavid Lebrun 	return NULL;
1006d1df6fd8SDavid Lebrun }
1007d1df6fd8SDavid Lebrun 
1008*94604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt)
1009*94604548SAndrea Mayer {
1010*94604548SAndrea Mayer 	return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS;
1011*94604548SAndrea Mayer }
1012*94604548SAndrea Mayer 
1013*94604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt,
1014*94604548SAndrea Mayer 				       unsigned int len, int err)
1015*94604548SAndrea Mayer {
1016*94604548SAndrea Mayer 	struct pcpu_seg6_local_counters *pcounters;
1017*94604548SAndrea Mayer 
1018*94604548SAndrea Mayer 	pcounters = this_cpu_ptr(slwt->pcpu_counters);
1019*94604548SAndrea Mayer 	u64_stats_update_begin(&pcounters->syncp);
1020*94604548SAndrea Mayer 
1021*94604548SAndrea Mayer 	if (likely(!err)) {
1022*94604548SAndrea Mayer 		u64_stats_inc(&pcounters->packets);
1023*94604548SAndrea Mayer 		u64_stats_add(&pcounters->bytes, len);
1024*94604548SAndrea Mayer 	} else {
1025*94604548SAndrea Mayer 		u64_stats_inc(&pcounters->errors);
1026*94604548SAndrea Mayer 	}
1027*94604548SAndrea Mayer 
1028*94604548SAndrea Mayer 	u64_stats_update_end(&pcounters->syncp);
1029*94604548SAndrea Mayer }
1030*94604548SAndrea Mayer 
1031d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb)
1032d1df6fd8SDavid Lebrun {
1033d1df6fd8SDavid Lebrun 	struct dst_entry *orig_dst = skb_dst(skb);
1034d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
1035d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
1036*94604548SAndrea Mayer 	unsigned int len = skb->len;
1037*94604548SAndrea Mayer 	int rc;
1038d1df6fd8SDavid Lebrun 
10396285217fSDavid Lebrun 	if (skb->protocol != htons(ETH_P_IPV6)) {
10406285217fSDavid Lebrun 		kfree_skb(skb);
10416285217fSDavid Lebrun 		return -EINVAL;
10426285217fSDavid Lebrun 	}
10436285217fSDavid Lebrun 
1044d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
1045d1df6fd8SDavid Lebrun 	desc = slwt->desc;
1046d1df6fd8SDavid Lebrun 
1047*94604548SAndrea Mayer 	rc = desc->input(skb, slwt);
1048*94604548SAndrea Mayer 
1049*94604548SAndrea Mayer 	if (!seg6_lwtunnel_counters_enabled(slwt))
1050*94604548SAndrea Mayer 		return rc;
1051*94604548SAndrea Mayer 
1052*94604548SAndrea Mayer 	seg6_local_update_counters(slwt, len, rc);
1053*94604548SAndrea Mayer 
1054*94604548SAndrea Mayer 	return rc;
1055d1df6fd8SDavid Lebrun }
1056d1df6fd8SDavid Lebrun 
1057d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
1058d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_ACTION]	= { .type = NLA_U32 },
1059d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .type = NLA_BINARY },
1060d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .type = NLA_U32 },
1061664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .type = NLA_U32 },
1062d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .type = NLA_BINARY,
1063d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in_addr) },
1064d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .type = NLA_BINARY,
1065d1df6fd8SDavid Lebrun 				    .len = sizeof(struct in6_addr) },
1066d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .type = NLA_U32 },
1067d1df6fd8SDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .type = NLA_U32 },
1068004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .type = NLA_NESTED },
1069*94604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .type = NLA_NESTED },
1070d1df6fd8SDavid Lebrun };
1071d1df6fd8SDavid Lebrun 
10722d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
10732d9cc60aSDavid Lebrun {
10742d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
10752d9cc60aSDavid Lebrun 	int len;
10762d9cc60aSDavid Lebrun 
10772d9cc60aSDavid Lebrun 	srh = nla_data(attrs[SEG6_LOCAL_SRH]);
10782d9cc60aSDavid Lebrun 	len = nla_len(attrs[SEG6_LOCAL_SRH]);
10792d9cc60aSDavid Lebrun 
10802d9cc60aSDavid Lebrun 	/* SRH must contain at least one segment */
10812d9cc60aSDavid Lebrun 	if (len < sizeof(*srh) + sizeof(struct in6_addr))
10822d9cc60aSDavid Lebrun 		return -EINVAL;
10832d9cc60aSDavid Lebrun 
1084bb986a50SAhmed Abdelsalam 	if (!seg6_validate_srh(srh, len, false))
10852d9cc60aSDavid Lebrun 		return -EINVAL;
10862d9cc60aSDavid Lebrun 
10877fa41efaSYueHaibing 	slwt->srh = kmemdup(srh, len, GFP_KERNEL);
10882d9cc60aSDavid Lebrun 	if (!slwt->srh)
10892d9cc60aSDavid Lebrun 		return -ENOMEM;
10902d9cc60aSDavid Lebrun 
10912d9cc60aSDavid Lebrun 	slwt->headroom += len;
10922d9cc60aSDavid Lebrun 
10932d9cc60aSDavid Lebrun 	return 0;
10942d9cc60aSDavid Lebrun }
10952d9cc60aSDavid Lebrun 
10962d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt)
10972d9cc60aSDavid Lebrun {
10982d9cc60aSDavid Lebrun 	struct ipv6_sr_hdr *srh;
10992d9cc60aSDavid Lebrun 	struct nlattr *nla;
11002d9cc60aSDavid Lebrun 	int len;
11012d9cc60aSDavid Lebrun 
11022d9cc60aSDavid Lebrun 	srh = slwt->srh;
11032d9cc60aSDavid Lebrun 	len = (srh->hdrlen + 1) << 3;
11042d9cc60aSDavid Lebrun 
11052d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_SRH, len);
11062d9cc60aSDavid Lebrun 	if (!nla)
11072d9cc60aSDavid Lebrun 		return -EMSGSIZE;
11082d9cc60aSDavid Lebrun 
11092d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), srh, len);
11102d9cc60aSDavid Lebrun 
11112d9cc60aSDavid Lebrun 	return 0;
11122d9cc60aSDavid Lebrun }
11132d9cc60aSDavid Lebrun 
11142d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
11152d9cc60aSDavid Lebrun {
11162d9cc60aSDavid Lebrun 	int len = (a->srh->hdrlen + 1) << 3;
11172d9cc60aSDavid Lebrun 
11182d9cc60aSDavid Lebrun 	if (len != ((b->srh->hdrlen + 1) << 3))
11192d9cc60aSDavid Lebrun 		return 1;
11202d9cc60aSDavid Lebrun 
11212d9cc60aSDavid Lebrun 	return memcmp(a->srh, b->srh, len);
11222d9cc60aSDavid Lebrun }
11232d9cc60aSDavid Lebrun 
1124964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt)
1125964adce5SAndrea Mayer {
1126964adce5SAndrea Mayer 	kfree(slwt->srh);
1127964adce5SAndrea Mayer }
1128964adce5SAndrea Mayer 
11292d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt)
11302d9cc60aSDavid Lebrun {
11312d9cc60aSDavid Lebrun 	slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]);
11322d9cc60aSDavid Lebrun 
11332d9cc60aSDavid Lebrun 	return 0;
11342d9cc60aSDavid Lebrun }
11352d9cc60aSDavid Lebrun 
11362d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt)
11372d9cc60aSDavid Lebrun {
11382d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table))
11392d9cc60aSDavid Lebrun 		return -EMSGSIZE;
11402d9cc60aSDavid Lebrun 
11412d9cc60aSDavid Lebrun 	return 0;
11422d9cc60aSDavid Lebrun }
11432d9cc60aSDavid Lebrun 
11442d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
11452d9cc60aSDavid Lebrun {
11462d9cc60aSDavid Lebrun 	if (a->table != b->table)
11472d9cc60aSDavid Lebrun 		return 1;
11482d9cc60aSDavid Lebrun 
11492d9cc60aSDavid Lebrun 	return 0;
11502d9cc60aSDavid Lebrun }
11512d9cc60aSDavid Lebrun 
1152664d6f86SAndrea Mayer static struct
1153664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt)
1154664d6f86SAndrea Mayer {
1155664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV
1156664d6f86SAndrea Mayer 	return &slwt->dt_info;
1157664d6f86SAndrea Mayer #else
1158664d6f86SAndrea Mayer 	return ERR_PTR(-EOPNOTSUPP);
1159664d6f86SAndrea Mayer #endif
1160664d6f86SAndrea Mayer }
1161664d6f86SAndrea Mayer 
1162664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs,
1163664d6f86SAndrea Mayer 			      struct seg6_local_lwt *slwt)
1164664d6f86SAndrea Mayer {
1165664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1166664d6f86SAndrea Mayer 
1167664d6f86SAndrea Mayer 	if (IS_ERR(info))
1168664d6f86SAndrea Mayer 		return PTR_ERR(info);
1169664d6f86SAndrea Mayer 
1170664d6f86SAndrea Mayer 	info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]);
1171664d6f86SAndrea Mayer 
1172664d6f86SAndrea Mayer 	return 0;
1173664d6f86SAndrea Mayer }
1174664d6f86SAndrea Mayer 
1175664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1176664d6f86SAndrea Mayer {
1177664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
1178664d6f86SAndrea Mayer 
1179664d6f86SAndrea Mayer 	if (IS_ERR(info))
1180664d6f86SAndrea Mayer 		return PTR_ERR(info);
1181664d6f86SAndrea Mayer 
1182664d6f86SAndrea Mayer 	if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table))
1183664d6f86SAndrea Mayer 		return -EMSGSIZE;
1184664d6f86SAndrea Mayer 
1185664d6f86SAndrea Mayer 	return 0;
1186664d6f86SAndrea Mayer }
1187664d6f86SAndrea Mayer 
1188664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1189664d6f86SAndrea Mayer {
1190664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a);
1191664d6f86SAndrea Mayer 	struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b);
1192664d6f86SAndrea Mayer 
1193664d6f86SAndrea Mayer 	if (info_a->vrf_table != info_b->vrf_table)
1194664d6f86SAndrea Mayer 		return 1;
1195664d6f86SAndrea Mayer 
1196664d6f86SAndrea Mayer 	return 0;
1197664d6f86SAndrea Mayer }
1198664d6f86SAndrea Mayer 
11992d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12002d9cc60aSDavid Lebrun {
12012d9cc60aSDavid Lebrun 	memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]),
12022d9cc60aSDavid Lebrun 	       sizeof(struct in_addr));
12032d9cc60aSDavid Lebrun 
12042d9cc60aSDavid Lebrun 	return 0;
12052d9cc60aSDavid Lebrun }
12062d9cc60aSDavid Lebrun 
12072d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12082d9cc60aSDavid Lebrun {
12092d9cc60aSDavid Lebrun 	struct nlattr *nla;
12102d9cc60aSDavid Lebrun 
12112d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr));
12122d9cc60aSDavid Lebrun 	if (!nla)
12132d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12142d9cc60aSDavid Lebrun 
12152d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr));
12162d9cc60aSDavid Lebrun 
12172d9cc60aSDavid Lebrun 	return 0;
12182d9cc60aSDavid Lebrun }
12192d9cc60aSDavid Lebrun 
12202d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12212d9cc60aSDavid Lebrun {
12222d9cc60aSDavid Lebrun 	return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr));
12232d9cc60aSDavid Lebrun }
12242d9cc60aSDavid Lebrun 
12252d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12262d9cc60aSDavid Lebrun {
12272d9cc60aSDavid Lebrun 	memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]),
12282d9cc60aSDavid Lebrun 	       sizeof(struct in6_addr));
12292d9cc60aSDavid Lebrun 
12302d9cc60aSDavid Lebrun 	return 0;
12312d9cc60aSDavid Lebrun }
12322d9cc60aSDavid Lebrun 
12332d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12342d9cc60aSDavid Lebrun {
12352d9cc60aSDavid Lebrun 	struct nlattr *nla;
12362d9cc60aSDavid Lebrun 
12372d9cc60aSDavid Lebrun 	nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr));
12382d9cc60aSDavid Lebrun 	if (!nla)
12392d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12402d9cc60aSDavid Lebrun 
12412d9cc60aSDavid Lebrun 	memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr));
12422d9cc60aSDavid Lebrun 
12432d9cc60aSDavid Lebrun 	return 0;
12442d9cc60aSDavid Lebrun }
12452d9cc60aSDavid Lebrun 
12462d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12472d9cc60aSDavid Lebrun {
12482d9cc60aSDavid Lebrun 	return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr));
12492d9cc60aSDavid Lebrun }
12502d9cc60aSDavid Lebrun 
12512d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12522d9cc60aSDavid Lebrun {
12532d9cc60aSDavid Lebrun 	slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]);
12542d9cc60aSDavid Lebrun 
12552d9cc60aSDavid Lebrun 	return 0;
12562d9cc60aSDavid Lebrun }
12572d9cc60aSDavid Lebrun 
12582d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12592d9cc60aSDavid Lebrun {
12602d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif))
12612d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12622d9cc60aSDavid Lebrun 
12632d9cc60aSDavid Lebrun 	return 0;
12642d9cc60aSDavid Lebrun }
12652d9cc60aSDavid Lebrun 
12662d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12672d9cc60aSDavid Lebrun {
12682d9cc60aSDavid Lebrun 	if (a->iif != b->iif)
12692d9cc60aSDavid Lebrun 		return 1;
12702d9cc60aSDavid Lebrun 
12712d9cc60aSDavid Lebrun 	return 0;
12722d9cc60aSDavid Lebrun }
12732d9cc60aSDavid Lebrun 
12742d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
12752d9cc60aSDavid Lebrun {
12762d9cc60aSDavid Lebrun 	slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]);
12772d9cc60aSDavid Lebrun 
12782d9cc60aSDavid Lebrun 	return 0;
12792d9cc60aSDavid Lebrun }
12802d9cc60aSDavid Lebrun 
12812d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
12822d9cc60aSDavid Lebrun {
12832d9cc60aSDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif))
12842d9cc60aSDavid Lebrun 		return -EMSGSIZE;
12852d9cc60aSDavid Lebrun 
12862d9cc60aSDavid Lebrun 	return 0;
12872d9cc60aSDavid Lebrun }
12882d9cc60aSDavid Lebrun 
12892d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
12902d9cc60aSDavid Lebrun {
12912d9cc60aSDavid Lebrun 	if (a->oif != b->oif)
12922d9cc60aSDavid Lebrun 		return 1;
12932d9cc60aSDavid Lebrun 
12942d9cc60aSDavid Lebrun 	return 0;
12952d9cc60aSDavid Lebrun }
12962d9cc60aSDavid Lebrun 
1297004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256
1298004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
1299004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG]	   = { .type = NLA_U32, },
1300004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
1301004d4b27SMathieu Xhonneux 				       .len = MAX_PROG_NAME },
1302004d4b27SMathieu Xhonneux };
1303004d4b27SMathieu Xhonneux 
1304004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1305004d4b27SMathieu Xhonneux {
1306004d4b27SMathieu Xhonneux 	struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
1307004d4b27SMathieu Xhonneux 	struct bpf_prog *p;
1308004d4b27SMathieu Xhonneux 	int ret;
1309004d4b27SMathieu Xhonneux 	u32 fd;
1310004d4b27SMathieu Xhonneux 
13118cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX,
13128cb08174SJohannes Berg 					  attrs[SEG6_LOCAL_BPF],
13138cb08174SJohannes Berg 					  bpf_prog_policy, NULL);
1314004d4b27SMathieu Xhonneux 	if (ret < 0)
1315004d4b27SMathieu Xhonneux 		return ret;
1316004d4b27SMathieu Xhonneux 
1317004d4b27SMathieu Xhonneux 	if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME])
1318004d4b27SMathieu Xhonneux 		return -EINVAL;
1319004d4b27SMathieu Xhonneux 
1320004d4b27SMathieu Xhonneux 	slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL);
1321004d4b27SMathieu Xhonneux 	if (!slwt->bpf.name)
1322004d4b27SMathieu Xhonneux 		return -ENOMEM;
1323004d4b27SMathieu Xhonneux 
1324004d4b27SMathieu Xhonneux 	fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]);
1325004d4b27SMathieu Xhonneux 	p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL);
1326004d4b27SMathieu Xhonneux 	if (IS_ERR(p)) {
1327004d4b27SMathieu Xhonneux 		kfree(slwt->bpf.name);
1328004d4b27SMathieu Xhonneux 		return PTR_ERR(p);
1329004d4b27SMathieu Xhonneux 	}
1330004d4b27SMathieu Xhonneux 
1331004d4b27SMathieu Xhonneux 	slwt->bpf.prog = p;
1332004d4b27SMathieu Xhonneux 	return 0;
1333004d4b27SMathieu Xhonneux }
1334004d4b27SMathieu Xhonneux 
1335004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1336004d4b27SMathieu Xhonneux {
1337004d4b27SMathieu Xhonneux 	struct nlattr *nest;
1338004d4b27SMathieu Xhonneux 
1339004d4b27SMathieu Xhonneux 	if (!slwt->bpf.prog)
1340004d4b27SMathieu Xhonneux 		return 0;
1341004d4b27SMathieu Xhonneux 
1342ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF);
1343004d4b27SMathieu Xhonneux 	if (!nest)
1344004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1345004d4b27SMathieu Xhonneux 
1346004d4b27SMathieu Xhonneux 	if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id))
1347004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1348004d4b27SMathieu Xhonneux 
1349004d4b27SMathieu Xhonneux 	if (slwt->bpf.name &&
1350004d4b27SMathieu Xhonneux 	    nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name))
1351004d4b27SMathieu Xhonneux 		return -EMSGSIZE;
1352004d4b27SMathieu Xhonneux 
1353004d4b27SMathieu Xhonneux 	return nla_nest_end(skb, nest);
1354004d4b27SMathieu Xhonneux }
1355004d4b27SMathieu Xhonneux 
1356004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1357004d4b27SMathieu Xhonneux {
1358004d4b27SMathieu Xhonneux 	if (!a->bpf.name && !b->bpf.name)
1359004d4b27SMathieu Xhonneux 		return 0;
1360004d4b27SMathieu Xhonneux 
1361004d4b27SMathieu Xhonneux 	if (!a->bpf.name || !b->bpf.name)
1362004d4b27SMathieu Xhonneux 		return 1;
1363004d4b27SMathieu Xhonneux 
1364004d4b27SMathieu Xhonneux 	return strcmp(a->bpf.name, b->bpf.name);
1365004d4b27SMathieu Xhonneux }
1366004d4b27SMathieu Xhonneux 
1367964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt)
1368964adce5SAndrea Mayer {
1369964adce5SAndrea Mayer 	kfree(slwt->bpf.name);
1370964adce5SAndrea Mayer 	if (slwt->bpf.prog)
1371964adce5SAndrea Mayer 		bpf_prog_put(slwt->bpf.prog);
1372964adce5SAndrea Mayer }
1373964adce5SAndrea Mayer 
1374*94604548SAndrea Mayer static const struct
1375*94604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = {
1376*94604548SAndrea Mayer 	[SEG6_LOCAL_CNT_PACKETS]	= { .type = NLA_U64 },
1377*94604548SAndrea Mayer 	[SEG6_LOCAL_CNT_BYTES]		= { .type = NLA_U64 },
1378*94604548SAndrea Mayer 	[SEG6_LOCAL_CNT_ERRORS]		= { .type = NLA_U64 },
1379*94604548SAndrea Mayer };
1380*94604548SAndrea Mayer 
1381*94604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs,
1382*94604548SAndrea Mayer 			      struct seg6_local_lwt *slwt)
1383*94604548SAndrea Mayer {
1384*94604548SAndrea Mayer 	struct pcpu_seg6_local_counters __percpu *pcounters;
1385*94604548SAndrea Mayer 	struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1];
1386*94604548SAndrea Mayer 	int ret;
1387*94604548SAndrea Mayer 
1388*94604548SAndrea Mayer 	ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX,
1389*94604548SAndrea Mayer 					  attrs[SEG6_LOCAL_COUNTERS],
1390*94604548SAndrea Mayer 					  seg6_local_counters_policy, NULL);
1391*94604548SAndrea Mayer 	if (ret < 0)
1392*94604548SAndrea Mayer 		return ret;
1393*94604548SAndrea Mayer 
1394*94604548SAndrea Mayer 	/* basic support for SRv6 Behavior counters requires at least:
1395*94604548SAndrea Mayer 	 * packets, bytes and errors.
1396*94604548SAndrea Mayer 	 */
1397*94604548SAndrea Mayer 	if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] ||
1398*94604548SAndrea Mayer 	    !tb[SEG6_LOCAL_CNT_ERRORS])
1399*94604548SAndrea Mayer 		return -EINVAL;
1400*94604548SAndrea Mayer 
1401*94604548SAndrea Mayer 	/* counters are always zero initialized */
1402*94604548SAndrea Mayer 	pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL);
1403*94604548SAndrea Mayer 	if (!pcounters)
1404*94604548SAndrea Mayer 		return -ENOMEM;
1405*94604548SAndrea Mayer 
1406*94604548SAndrea Mayer 	slwt->pcpu_counters = pcounters;
1407*94604548SAndrea Mayer 
1408*94604548SAndrea Mayer 	return 0;
1409*94604548SAndrea Mayer }
1410*94604548SAndrea Mayer 
1411*94604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb,
1412*94604548SAndrea Mayer 					struct seg6_local_counters *counters)
1413*94604548SAndrea Mayer {
1414*94604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets,
1415*94604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
1416*94604548SAndrea Mayer 		return -EMSGSIZE;
1417*94604548SAndrea Mayer 
1418*94604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes,
1419*94604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
1420*94604548SAndrea Mayer 		return -EMSGSIZE;
1421*94604548SAndrea Mayer 
1422*94604548SAndrea Mayer 	if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors,
1423*94604548SAndrea Mayer 			      SEG6_LOCAL_CNT_PAD))
1424*94604548SAndrea Mayer 		return -EMSGSIZE;
1425*94604548SAndrea Mayer 
1426*94604548SAndrea Mayer 	return 0;
1427*94604548SAndrea Mayer }
1428*94604548SAndrea Mayer 
1429*94604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt)
1430*94604548SAndrea Mayer {
1431*94604548SAndrea Mayer 	struct seg6_local_counters counters = { 0, 0, 0 };
1432*94604548SAndrea Mayer 	struct nlattr *nest;
1433*94604548SAndrea Mayer 	int rc, i;
1434*94604548SAndrea Mayer 
1435*94604548SAndrea Mayer 	nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS);
1436*94604548SAndrea Mayer 	if (!nest)
1437*94604548SAndrea Mayer 		return -EMSGSIZE;
1438*94604548SAndrea Mayer 
1439*94604548SAndrea Mayer 	for_each_possible_cpu(i) {
1440*94604548SAndrea Mayer 		struct pcpu_seg6_local_counters *pcounters;
1441*94604548SAndrea Mayer 		u64 packets, bytes, errors;
1442*94604548SAndrea Mayer 		unsigned int start;
1443*94604548SAndrea Mayer 
1444*94604548SAndrea Mayer 		pcounters = per_cpu_ptr(slwt->pcpu_counters, i);
1445*94604548SAndrea Mayer 		do {
1446*94604548SAndrea Mayer 			start = u64_stats_fetch_begin_irq(&pcounters->syncp);
1447*94604548SAndrea Mayer 
1448*94604548SAndrea Mayer 			packets = u64_stats_read(&pcounters->packets);
1449*94604548SAndrea Mayer 			bytes = u64_stats_read(&pcounters->bytes);
1450*94604548SAndrea Mayer 			errors = u64_stats_read(&pcounters->errors);
1451*94604548SAndrea Mayer 
1452*94604548SAndrea Mayer 		} while (u64_stats_fetch_retry_irq(&pcounters->syncp, start));
1453*94604548SAndrea Mayer 
1454*94604548SAndrea Mayer 		counters.packets += packets;
1455*94604548SAndrea Mayer 		counters.bytes += bytes;
1456*94604548SAndrea Mayer 		counters.errors += errors;
1457*94604548SAndrea Mayer 	}
1458*94604548SAndrea Mayer 
1459*94604548SAndrea Mayer 	rc = seg6_local_fill_nla_counters(skb, &counters);
1460*94604548SAndrea Mayer 	if (rc < 0) {
1461*94604548SAndrea Mayer 		nla_nest_cancel(skb, nest);
1462*94604548SAndrea Mayer 		return rc;
1463*94604548SAndrea Mayer 	}
1464*94604548SAndrea Mayer 
1465*94604548SAndrea Mayer 	return nla_nest_end(skb, nest);
1466*94604548SAndrea Mayer }
1467*94604548SAndrea Mayer 
1468*94604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
1469*94604548SAndrea Mayer {
1470*94604548SAndrea Mayer 	/* a and b are equal if both have pcpu_counters set or not */
1471*94604548SAndrea Mayer 	return (!!((unsigned long)a->pcpu_counters)) ^
1472*94604548SAndrea Mayer 		(!!((unsigned long)b->pcpu_counters));
1473*94604548SAndrea Mayer }
1474*94604548SAndrea Mayer 
1475*94604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt)
1476*94604548SAndrea Mayer {
1477*94604548SAndrea Mayer 	free_percpu(slwt->pcpu_counters);
1478*94604548SAndrea Mayer }
1479*94604548SAndrea Mayer 
1480d1df6fd8SDavid Lebrun struct seg6_action_param {
1481d1df6fd8SDavid Lebrun 	int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
1482d1df6fd8SDavid Lebrun 	int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
1483d1df6fd8SDavid Lebrun 	int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
1484964adce5SAndrea Mayer 
1485964adce5SAndrea Mayer 	/* optional destroy() callback useful for releasing resources which
1486964adce5SAndrea Mayer 	 * have been previously acquired in the corresponding parse()
1487964adce5SAndrea Mayer 	 * function.
1488964adce5SAndrea Mayer 	 */
1489964adce5SAndrea Mayer 	void (*destroy)(struct seg6_local_lwt *slwt);
1490d1df6fd8SDavid Lebrun };
1491d1df6fd8SDavid Lebrun 
1492d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
14932d9cc60aSDavid Lebrun 	[SEG6_LOCAL_SRH]	= { .parse = parse_nla_srh,
14942d9cc60aSDavid Lebrun 				    .put = put_nla_srh,
1495964adce5SAndrea Mayer 				    .cmp = cmp_nla_srh,
1496964adce5SAndrea Mayer 				    .destroy = destroy_attr_srh },
1497d1df6fd8SDavid Lebrun 
14982d9cc60aSDavid Lebrun 	[SEG6_LOCAL_TABLE]	= { .parse = parse_nla_table,
14992d9cc60aSDavid Lebrun 				    .put = put_nla_table,
15002d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_table },
1501d1df6fd8SDavid Lebrun 
15022d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH4]	= { .parse = parse_nla_nh4,
15032d9cc60aSDavid Lebrun 				    .put = put_nla_nh4,
15042d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh4 },
1505d1df6fd8SDavid Lebrun 
15062d9cc60aSDavid Lebrun 	[SEG6_LOCAL_NH6]	= { .parse = parse_nla_nh6,
15072d9cc60aSDavid Lebrun 				    .put = put_nla_nh6,
15082d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_nh6 },
1509d1df6fd8SDavid Lebrun 
15102d9cc60aSDavid Lebrun 	[SEG6_LOCAL_IIF]	= { .parse = parse_nla_iif,
15112d9cc60aSDavid Lebrun 				    .put = put_nla_iif,
15122d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_iif },
1513d1df6fd8SDavid Lebrun 
15142d9cc60aSDavid Lebrun 	[SEG6_LOCAL_OIF]	= { .parse = parse_nla_oif,
15152d9cc60aSDavid Lebrun 				    .put = put_nla_oif,
15162d9cc60aSDavid Lebrun 				    .cmp = cmp_nla_oif },
1517004d4b27SMathieu Xhonneux 
1518004d4b27SMathieu Xhonneux 	[SEG6_LOCAL_BPF]	= { .parse = parse_nla_bpf,
1519004d4b27SMathieu Xhonneux 				    .put = put_nla_bpf,
1520964adce5SAndrea Mayer 				    .cmp = cmp_nla_bpf,
1521964adce5SAndrea Mayer 				    .destroy = destroy_attr_bpf },
1522004d4b27SMathieu Xhonneux 
1523664d6f86SAndrea Mayer 	[SEG6_LOCAL_VRFTABLE]	= { .parse = parse_nla_vrftable,
1524664d6f86SAndrea Mayer 				    .put = put_nla_vrftable,
1525664d6f86SAndrea Mayer 				    .cmp = cmp_nla_vrftable },
1526664d6f86SAndrea Mayer 
1527*94604548SAndrea Mayer 	[SEG6_LOCAL_COUNTERS]	= { .parse = parse_nla_counters,
1528*94604548SAndrea Mayer 				    .put = put_nla_counters,
1529*94604548SAndrea Mayer 				    .cmp = cmp_nla_counters,
1530*94604548SAndrea Mayer 				    .destroy = destroy_attr_counters },
1531d1df6fd8SDavid Lebrun };
1532d1df6fd8SDavid Lebrun 
1533964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in
15340a3021f1SAndrea Mayer  * @parsed_attrs, starting from the first attribute up to the @max_parsed
15350a3021f1SAndrea Mayer  * (excluded) attribute.
1536964adce5SAndrea Mayer  */
15370a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed,
15380a3021f1SAndrea Mayer 			    struct seg6_local_lwt *slwt)
1539964adce5SAndrea Mayer {
1540964adce5SAndrea Mayer 	struct seg6_action_param *param;
1541964adce5SAndrea Mayer 	int i;
1542964adce5SAndrea Mayer 
1543964adce5SAndrea Mayer 	/* Every required seg6local attribute is identified by an ID which is
1544964adce5SAndrea Mayer 	 * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask;
1545964adce5SAndrea Mayer 	 *
15460a3021f1SAndrea Mayer 	 * We scan the 'parsed_attrs' bitmask, starting from the first attribute
1547964adce5SAndrea Mayer 	 * up to the @max_parsed (excluded) attribute.
1548964adce5SAndrea Mayer 	 * For each set attribute, we retrieve the corresponding destroy()
1549964adce5SAndrea Mayer 	 * callback. If the callback is not available, then we skip to the next
1550964adce5SAndrea Mayer 	 * attribute; otherwise, we call the destroy() callback.
1551964adce5SAndrea Mayer 	 */
1552964adce5SAndrea Mayer 	for (i = 0; i < max_parsed; ++i) {
1553300a0fd8SAndrea Mayer 		if (!(parsed_attrs & SEG6_F_ATTR(i)))
1554964adce5SAndrea Mayer 			continue;
1555964adce5SAndrea Mayer 
1556964adce5SAndrea Mayer 		param = &seg6_action_params[i];
1557964adce5SAndrea Mayer 
1558964adce5SAndrea Mayer 		if (param->destroy)
1559964adce5SAndrea Mayer 			param->destroy(slwt);
1560964adce5SAndrea Mayer 	}
1561964adce5SAndrea Mayer }
1562964adce5SAndrea Mayer 
1563964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing
1564964adce5SAndrea Mayer  * operations.
1565964adce5SAndrea Mayer  */
1566964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt)
1567964adce5SAndrea Mayer {
15680a3021f1SAndrea Mayer 	unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs;
15690a3021f1SAndrea Mayer 
15700a3021f1SAndrea Mayer 	__destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt);
15710a3021f1SAndrea Mayer }
15720a3021f1SAndrea Mayer 
15730a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs,
15740a3021f1SAndrea Mayer 				    struct seg6_local_lwt *slwt)
15750a3021f1SAndrea Mayer {
15760a3021f1SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
15770a3021f1SAndrea Mayer 	unsigned long parsed_optattrs = 0;
15780a3021f1SAndrea Mayer 	struct seg6_action_param *param;
15790a3021f1SAndrea Mayer 	int err, i;
15800a3021f1SAndrea Mayer 
15810a3021f1SAndrea Mayer 	for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) {
1582300a0fd8SAndrea Mayer 		if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i])
15830a3021f1SAndrea Mayer 			continue;
15840a3021f1SAndrea Mayer 
15850a3021f1SAndrea Mayer 		/* once here, the i-th attribute is provided by the
15860a3021f1SAndrea Mayer 		 * userspace AND it is identified optional as well.
15870a3021f1SAndrea Mayer 		 */
15880a3021f1SAndrea Mayer 		param = &seg6_action_params[i];
15890a3021f1SAndrea Mayer 
15900a3021f1SAndrea Mayer 		err = param->parse(attrs, slwt);
15910a3021f1SAndrea Mayer 		if (err < 0)
15920a3021f1SAndrea Mayer 			goto parse_optattrs_err;
15930a3021f1SAndrea Mayer 
15940a3021f1SAndrea Mayer 		/* current attribute has been correctly parsed */
1595300a0fd8SAndrea Mayer 		parsed_optattrs |= SEG6_F_ATTR(i);
15960a3021f1SAndrea Mayer 	}
15970a3021f1SAndrea Mayer 
15980a3021f1SAndrea Mayer 	/* store in the tunnel state all the optional attributed successfully
15990a3021f1SAndrea Mayer 	 * parsed.
16000a3021f1SAndrea Mayer 	 */
16010a3021f1SAndrea Mayer 	slwt->parsed_optattrs = parsed_optattrs;
16020a3021f1SAndrea Mayer 
16030a3021f1SAndrea Mayer 	return 0;
16040a3021f1SAndrea Mayer 
16050a3021f1SAndrea Mayer parse_optattrs_err:
16060a3021f1SAndrea Mayer 	__destroy_attrs(parsed_optattrs, i, slwt);
16070a3021f1SAndrea Mayer 
16080a3021f1SAndrea Mayer 	return err;
1609964adce5SAndrea Mayer }
1610964adce5SAndrea Mayer 
1611cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase
1612cfdf64a0SAndrea Mayer  * and after that all its attributes have been parsed successfully.
1613cfdf64a0SAndrea Mayer  */
1614cfdf64a0SAndrea Mayer static int
1615cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg,
1616cfdf64a0SAndrea Mayer 				struct netlink_ext_ack *extack)
1617cfdf64a0SAndrea Mayer {
1618cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1619cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1620cfdf64a0SAndrea Mayer 
1621cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1622cfdf64a0SAndrea Mayer 	if (!ops->build_state)
1623cfdf64a0SAndrea Mayer 		return 0;
1624cfdf64a0SAndrea Mayer 
1625cfdf64a0SAndrea Mayer 	return ops->build_state(slwt, cfg, extack);
1626cfdf64a0SAndrea Mayer }
1627cfdf64a0SAndrea Mayer 
1628cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the
1629cfdf64a0SAndrea Mayer  * tunnel is going to be destroyed.
1630cfdf64a0SAndrea Mayer  */
1631cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
1632cfdf64a0SAndrea Mayer {
1633cfdf64a0SAndrea Mayer 	struct seg6_action_desc *desc = slwt->desc;
1634cfdf64a0SAndrea Mayer 	struct seg6_local_lwtunnel_ops *ops;
1635cfdf64a0SAndrea Mayer 
1636cfdf64a0SAndrea Mayer 	ops = &desc->slwt_ops;
1637cfdf64a0SAndrea Mayer 	if (!ops->destroy_state)
1638cfdf64a0SAndrea Mayer 		return;
1639cfdf64a0SAndrea Mayer 
1640cfdf64a0SAndrea Mayer 	ops->destroy_state(slwt);
1641cfdf64a0SAndrea Mayer }
1642cfdf64a0SAndrea Mayer 
1643d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
1644d1df6fd8SDavid Lebrun {
1645d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
1646d1df6fd8SDavid Lebrun 	struct seg6_action_desc *desc;
16470a3021f1SAndrea Mayer 	unsigned long invalid_attrs;
1648d1df6fd8SDavid Lebrun 	int i, err;
1649d1df6fd8SDavid Lebrun 
1650d1df6fd8SDavid Lebrun 	desc = __get_action_desc(slwt->action);
1651d1df6fd8SDavid Lebrun 	if (!desc)
1652d1df6fd8SDavid Lebrun 		return -EINVAL;
1653d1df6fd8SDavid Lebrun 
1654d1df6fd8SDavid Lebrun 	if (!desc->input)
1655d1df6fd8SDavid Lebrun 		return -EOPNOTSUPP;
1656d1df6fd8SDavid Lebrun 
1657d1df6fd8SDavid Lebrun 	slwt->desc = desc;
1658d1df6fd8SDavid Lebrun 	slwt->headroom += desc->static_headroom;
1659d1df6fd8SDavid Lebrun 
16600a3021f1SAndrea Mayer 	/* Forcing the desc->optattrs *set* and the desc->attrs *set* to be
16610a3021f1SAndrea Mayer 	 * disjoined, this allow us to release acquired resources by optional
16620a3021f1SAndrea Mayer 	 * attributes and by required attributes independently from each other
16630d770360SAndrea Mayer 	 * without any interference.
16640a3021f1SAndrea Mayer 	 * In other terms, we are sure that we do not release some the acquired
16650a3021f1SAndrea Mayer 	 * resources twice.
16660a3021f1SAndrea Mayer 	 *
16670a3021f1SAndrea Mayer 	 * Note that if an attribute is configured both as required and as
16680a3021f1SAndrea Mayer 	 * optional, it means that the user has messed something up in the
16690a3021f1SAndrea Mayer 	 * seg6_action_table. Therefore, this check is required for SRv6
16700a3021f1SAndrea Mayer 	 * behaviors to work properly.
16710a3021f1SAndrea Mayer 	 */
16720a3021f1SAndrea Mayer 	invalid_attrs = desc->attrs & desc->optattrs;
16730a3021f1SAndrea Mayer 	if (invalid_attrs) {
16740a3021f1SAndrea Mayer 		WARN_ONCE(1,
16750a3021f1SAndrea Mayer 			  "An attribute cannot be both required AND optional");
16760a3021f1SAndrea Mayer 		return -EINVAL;
16770a3021f1SAndrea Mayer 	}
16780a3021f1SAndrea Mayer 
16790a3021f1SAndrea Mayer 	/* parse the required attributes */
1680d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1681300a0fd8SAndrea Mayer 		if (desc->attrs & SEG6_F_ATTR(i)) {
1682d1df6fd8SDavid Lebrun 			if (!attrs[i])
1683d1df6fd8SDavid Lebrun 				return -EINVAL;
1684d1df6fd8SDavid Lebrun 
1685d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1686d1df6fd8SDavid Lebrun 
1687d1df6fd8SDavid Lebrun 			err = param->parse(attrs, slwt);
1688d1df6fd8SDavid Lebrun 			if (err < 0)
16890a3021f1SAndrea Mayer 				goto parse_attrs_err;
1690d1df6fd8SDavid Lebrun 		}
1691d1df6fd8SDavid Lebrun 	}
1692d1df6fd8SDavid Lebrun 
16930a3021f1SAndrea Mayer 	/* parse the optional attributes, if any */
16940a3021f1SAndrea Mayer 	err = parse_nla_optional_attrs(attrs, slwt);
16950a3021f1SAndrea Mayer 	if (err < 0)
16960a3021f1SAndrea Mayer 		goto parse_attrs_err;
16970a3021f1SAndrea Mayer 
1698d1df6fd8SDavid Lebrun 	return 0;
1699964adce5SAndrea Mayer 
17000a3021f1SAndrea Mayer parse_attrs_err:
1701964adce5SAndrea Mayer 	/* release any resource that may have been acquired during the i-1
1702964adce5SAndrea Mayer 	 * parse() operations.
1703964adce5SAndrea Mayer 	 */
17040a3021f1SAndrea Mayer 	__destroy_attrs(desc->attrs, i, slwt);
1705964adce5SAndrea Mayer 
1706964adce5SAndrea Mayer 	return err;
1707d1df6fd8SDavid Lebrun }
1708d1df6fd8SDavid Lebrun 
1709faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla,
1710faee6769SAlexander Aring 				  unsigned int family, const void *cfg,
1711faee6769SAlexander Aring 				  struct lwtunnel_state **ts,
1712d1df6fd8SDavid Lebrun 				  struct netlink_ext_ack *extack)
1713d1df6fd8SDavid Lebrun {
1714d1df6fd8SDavid Lebrun 	struct nlattr *tb[SEG6_LOCAL_MAX + 1];
1715d1df6fd8SDavid Lebrun 	struct lwtunnel_state *newts;
1716d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt;
1717d1df6fd8SDavid Lebrun 	int err;
1718d1df6fd8SDavid Lebrun 
17196285217fSDavid Lebrun 	if (family != AF_INET6)
17206285217fSDavid Lebrun 		return -EINVAL;
17216285217fSDavid Lebrun 
17228cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla,
17238cb08174SJohannes Berg 					  seg6_local_policy, extack);
1724d1df6fd8SDavid Lebrun 
1725d1df6fd8SDavid Lebrun 	if (err < 0)
1726d1df6fd8SDavid Lebrun 		return err;
1727d1df6fd8SDavid Lebrun 
1728d1df6fd8SDavid Lebrun 	if (!tb[SEG6_LOCAL_ACTION])
1729d1df6fd8SDavid Lebrun 		return -EINVAL;
1730d1df6fd8SDavid Lebrun 
1731d1df6fd8SDavid Lebrun 	newts = lwtunnel_state_alloc(sizeof(*slwt));
1732d1df6fd8SDavid Lebrun 	if (!newts)
1733d1df6fd8SDavid Lebrun 		return -ENOMEM;
1734d1df6fd8SDavid Lebrun 
1735d1df6fd8SDavid Lebrun 	slwt = seg6_local_lwtunnel(newts);
1736d1df6fd8SDavid Lebrun 	slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
1737d1df6fd8SDavid Lebrun 
1738d1df6fd8SDavid Lebrun 	err = parse_nla_action(tb, slwt);
1739d1df6fd8SDavid Lebrun 	if (err < 0)
1740d1df6fd8SDavid Lebrun 		goto out_free;
1741d1df6fd8SDavid Lebrun 
1742cfdf64a0SAndrea Mayer 	err = seg6_local_lwtunnel_build_state(slwt, cfg, extack);
1743cfdf64a0SAndrea Mayer 	if (err < 0)
1744cfdf64a0SAndrea Mayer 		goto out_destroy_attrs;
1745cfdf64a0SAndrea Mayer 
1746d1df6fd8SDavid Lebrun 	newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
1747d1df6fd8SDavid Lebrun 	newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
1748d1df6fd8SDavid Lebrun 	newts->headroom = slwt->headroom;
1749d1df6fd8SDavid Lebrun 
1750d1df6fd8SDavid Lebrun 	*ts = newts;
1751d1df6fd8SDavid Lebrun 
1752d1df6fd8SDavid Lebrun 	return 0;
1753d1df6fd8SDavid Lebrun 
1754cfdf64a0SAndrea Mayer out_destroy_attrs:
1755cfdf64a0SAndrea Mayer 	destroy_attrs(slwt);
1756d1df6fd8SDavid Lebrun out_free:
1757d1df6fd8SDavid Lebrun 	kfree(newts);
1758d1df6fd8SDavid Lebrun 	return err;
1759d1df6fd8SDavid Lebrun }
1760d1df6fd8SDavid Lebrun 
1761d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
1762d1df6fd8SDavid Lebrun {
1763d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1764d1df6fd8SDavid Lebrun 
1765cfdf64a0SAndrea Mayer 	seg6_local_lwtunnel_destroy_state(slwt);
1766cfdf64a0SAndrea Mayer 
1767964adce5SAndrea Mayer 	destroy_attrs(slwt);
1768004d4b27SMathieu Xhonneux 
1769004d4b27SMathieu Xhonneux 	return;
1770d1df6fd8SDavid Lebrun }
1771d1df6fd8SDavid Lebrun 
1772d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb,
1773d1df6fd8SDavid Lebrun 				 struct lwtunnel_state *lwt)
1774d1df6fd8SDavid Lebrun {
1775d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1776d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
17770a3021f1SAndrea Mayer 	unsigned long attrs;
1778d1df6fd8SDavid Lebrun 	int i, err;
1779d1df6fd8SDavid Lebrun 
1780d1df6fd8SDavid Lebrun 	if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
1781d1df6fd8SDavid Lebrun 		return -EMSGSIZE;
1782d1df6fd8SDavid Lebrun 
17830a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
17840a3021f1SAndrea Mayer 
1785d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1786300a0fd8SAndrea Mayer 		if (attrs & SEG6_F_ATTR(i)) {
1787d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1788d1df6fd8SDavid Lebrun 			err = param->put(skb, slwt);
1789d1df6fd8SDavid Lebrun 			if (err < 0)
1790d1df6fd8SDavid Lebrun 				return err;
1791d1df6fd8SDavid Lebrun 		}
1792d1df6fd8SDavid Lebrun 	}
1793d1df6fd8SDavid Lebrun 
1794d1df6fd8SDavid Lebrun 	return 0;
1795d1df6fd8SDavid Lebrun }
1796d1df6fd8SDavid Lebrun 
1797d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
1798d1df6fd8SDavid Lebrun {
1799d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1800d1df6fd8SDavid Lebrun 	unsigned long attrs;
1801d1df6fd8SDavid Lebrun 	int nlsize;
1802d1df6fd8SDavid Lebrun 
1803d1df6fd8SDavid Lebrun 	nlsize = nla_total_size(4); /* action */
1804d1df6fd8SDavid Lebrun 
18050a3021f1SAndrea Mayer 	attrs = slwt->desc->attrs | slwt->parsed_optattrs;
1806d1df6fd8SDavid Lebrun 
1807300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH))
1808d1df6fd8SDavid Lebrun 		nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
1809d1df6fd8SDavid Lebrun 
1810300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE))
1811d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1812d1df6fd8SDavid Lebrun 
1813300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4))
1814d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1815d1df6fd8SDavid Lebrun 
1816300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6))
1817d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(16);
1818d1df6fd8SDavid Lebrun 
1819300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF))
1820d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1821d1df6fd8SDavid Lebrun 
1822300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF))
1823d1df6fd8SDavid Lebrun 		nlsize += nla_total_size(4);
1824d1df6fd8SDavid Lebrun 
1825300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF))
1826004d4b27SMathieu Xhonneux 		nlsize += nla_total_size(sizeof(struct nlattr)) +
1827004d4b27SMathieu Xhonneux 		       nla_total_size(MAX_PROG_NAME) +
1828004d4b27SMathieu Xhonneux 		       nla_total_size(4);
1829004d4b27SMathieu Xhonneux 
1830300a0fd8SAndrea Mayer 	if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE))
1831664d6f86SAndrea Mayer 		nlsize += nla_total_size(4);
1832664d6f86SAndrea Mayer 
1833*94604548SAndrea Mayer 	if (attrs & SEG6_F_LOCAL_COUNTERS)
1834*94604548SAndrea Mayer 		nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */
1835*94604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_PACKETS */
1836*94604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
1837*94604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_BYTES */
1838*94604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64)) +
1839*94604548SAndrea Mayer 			  /* SEG6_LOCAL_CNT_ERRORS */
1840*94604548SAndrea Mayer 			  nla_total_size_64bit(sizeof(__u64));
1841*94604548SAndrea Mayer 
1842d1df6fd8SDavid Lebrun 	return nlsize;
1843d1df6fd8SDavid Lebrun }
1844d1df6fd8SDavid Lebrun 
1845d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a,
1846d1df6fd8SDavid Lebrun 				struct lwtunnel_state *b)
1847d1df6fd8SDavid Lebrun {
1848d1df6fd8SDavid Lebrun 	struct seg6_local_lwt *slwt_a, *slwt_b;
1849d1df6fd8SDavid Lebrun 	struct seg6_action_param *param;
18500a3021f1SAndrea Mayer 	unsigned long attrs_a, attrs_b;
1851d1df6fd8SDavid Lebrun 	int i;
1852d1df6fd8SDavid Lebrun 
1853d1df6fd8SDavid Lebrun 	slwt_a = seg6_local_lwtunnel(a);
1854d1df6fd8SDavid Lebrun 	slwt_b = seg6_local_lwtunnel(b);
1855d1df6fd8SDavid Lebrun 
1856d1df6fd8SDavid Lebrun 	if (slwt_a->action != slwt_b->action)
1857d1df6fd8SDavid Lebrun 		return 1;
1858d1df6fd8SDavid Lebrun 
18590a3021f1SAndrea Mayer 	attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs;
18600a3021f1SAndrea Mayer 	attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs;
18610a3021f1SAndrea Mayer 
18620a3021f1SAndrea Mayer 	if (attrs_a != attrs_b)
1863d1df6fd8SDavid Lebrun 		return 1;
1864d1df6fd8SDavid Lebrun 
1865d1df6fd8SDavid Lebrun 	for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1866300a0fd8SAndrea Mayer 		if (attrs_a & SEG6_F_ATTR(i)) {
1867d1df6fd8SDavid Lebrun 			param = &seg6_action_params[i];
1868d1df6fd8SDavid Lebrun 			if (param->cmp(slwt_a, slwt_b))
1869d1df6fd8SDavid Lebrun 				return 1;
1870d1df6fd8SDavid Lebrun 		}
1871d1df6fd8SDavid Lebrun 	}
1872d1df6fd8SDavid Lebrun 
1873d1df6fd8SDavid Lebrun 	return 0;
1874d1df6fd8SDavid Lebrun }
1875d1df6fd8SDavid Lebrun 
1876d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = {
1877d1df6fd8SDavid Lebrun 	.build_state	= seg6_local_build_state,
1878d1df6fd8SDavid Lebrun 	.destroy_state	= seg6_local_destroy_state,
1879d1df6fd8SDavid Lebrun 	.input		= seg6_local_input,
1880d1df6fd8SDavid Lebrun 	.fill_encap	= seg6_local_fill_encap,
1881d1df6fd8SDavid Lebrun 	.get_encap_size	= seg6_local_get_encap_size,
1882d1df6fd8SDavid Lebrun 	.cmp_encap	= seg6_local_cmp_encap,
1883d1df6fd8SDavid Lebrun 	.owner		= THIS_MODULE,
1884d1df6fd8SDavid Lebrun };
1885d1df6fd8SDavid Lebrun 
1886d1df6fd8SDavid Lebrun int __init seg6_local_init(void)
1887d1df6fd8SDavid Lebrun {
1888300a0fd8SAndrea Mayer 	/* If the max total number of defined attributes is reached, then your
1889300a0fd8SAndrea Mayer 	 * kernel build stops here.
1890300a0fd8SAndrea Mayer 	 *
1891300a0fd8SAndrea Mayer 	 * This check is required to avoid arithmetic overflows when processing
1892300a0fd8SAndrea Mayer 	 * behavior attributes and the maximum number of defined attributes
1893300a0fd8SAndrea Mayer 	 * exceeds the allowed value.
1894300a0fd8SAndrea Mayer 	 */
1895300a0fd8SAndrea Mayer 	BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long));
1896300a0fd8SAndrea Mayer 
1897d1df6fd8SDavid Lebrun 	return lwtunnel_encap_add_ops(&seg6_local_ops,
1898d1df6fd8SDavid Lebrun 				      LWTUNNEL_ENCAP_SEG6_LOCAL);
1899d1df6fd8SDavid Lebrun }
1900d1df6fd8SDavid Lebrun 
1901d1df6fd8SDavid Lebrun void seg6_local_exit(void)
1902d1df6fd8SDavid Lebrun {
1903d1df6fd8SDavid Lebrun 	lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
1904d1df6fd8SDavid Lebrun }
1905