File Index
Files and directories
Sign in
FILE

seg6_iptunnel.c

File preview with syntax highlighting.

linux-6.19.14/net/ipv6/seg6_iptunnel.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  SR-IPv6 implementation
  4 *
  5 *  Author:
  6 *  David Lebrun <david.lebrun@uclouvain.be>
  7 */
  8
  9#include <linux/types.h>
 10#include <linux/skbuff.h>
 11#include <linux/net.h>
 12#include <linux/module.h>
 13#include <net/ip.h>
 14#include <net/ip_tunnels.h>
 15#include <net/lwtunnel.h>
 16#include <net/netevent.h>
 17#include <net/netns/generic.h>
 18#include <net/ip6_fib.h>
 19#include <net/route.h>
 20#include <net/seg6.h>
 21#include <linux/seg6.h>
 22#include <linux/seg6_iptunnel.h>
 23#include <net/addrconf.h>
 24#include <net/ip6_route.h>
 25#include <net/dst_cache.h>
 26#ifdef CONFIG_IPV6_SEG6_HMAC
 27#include <net/seg6_hmac.h>
 28#endif
 29#include <linux/netfilter.h>
 30
 31static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
 32{
 33	int head = 0;
 34
 35	switch (tuninfo->mode) {
 36	case SEG6_IPTUN_MODE_INLINE:
 37		break;
 38	case SEG6_IPTUN_MODE_ENCAP:
 39	case SEG6_IPTUN_MODE_ENCAP_RED:
 40		head = sizeof(struct ipv6hdr);
 41		break;
 42	case SEG6_IPTUN_MODE_L2ENCAP:
 43	case SEG6_IPTUN_MODE_L2ENCAP_RED:
 44		return 0;
 45	}
 46
 47	return ((tuninfo->srh->hdrlen + 1) << 3) + head;
 48}
 49
 50struct seg6_lwt {
 51	struct dst_cache cache_input;
 52	struct dst_cache cache_output;
 53	struct seg6_iptunnel_encap tuninfo[];
 54};
 55
 56static inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt)
 57{
 58	return (struct seg6_lwt *)lwt->data;
 59}
 60
 61static inline struct seg6_iptunnel_encap *
 62seg6_encap_lwtunnel(struct lwtunnel_state *lwt)
 63{
 64	return seg6_lwt_lwtunnel(lwt)->tuninfo;
 65}
 66
 67static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {
 68	[SEG6_IPTUNNEL_SRH]	= { .type = NLA_BINARY },
 69};
 70
 71static int nla_put_srh(struct sk_buff *skb, int attrtype,
 72		       struct seg6_iptunnel_encap *tuninfo)
 73{
 74	struct seg6_iptunnel_encap *data;
 75	struct nlattr *nla;
 76	int len;
 77
 78	len = SEG6_IPTUN_ENCAP_SIZE(tuninfo);
 79
 80	nla = nla_reserve(skb, attrtype, len);
 81	if (!nla)
 82		return -EMSGSIZE;
 83
 84	data = nla_data(nla);
 85	memcpy(data, tuninfo, len);
 86
 87	return 0;
 88}
 89
 90static void set_tun_src(struct net *net, struct net_device *dev,
 91			struct in6_addr *daddr, struct in6_addr *saddr)
 92{
 93	struct seg6_pernet_data *sdata = seg6_pernet(net);
 94	struct in6_addr *tun_src;
 95
 96	rcu_read_lock();
 97
 98	tun_src = rcu_dereference(sdata->tun_src);
 99
100	if (!ipv6_addr_any(tun_src)) {
101		memcpy(saddr, tun_src, sizeof(struct in6_addr));
102	} else {
103		ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC,
104				   saddr);
105	}
106
107	rcu_read_unlock();
108}
109
110/* Compute flowlabel for outer IPv6 header */
111static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb,
112				  struct ipv6hdr *inner_hdr)
113{
114	int do_flowlabel = net->ipv6.sysctl.seg6_flowlabel;
115	__be32 flowlabel = 0;
116	u32 hash;
117
118	if (do_flowlabel > 0) {
119		hash = skb_get_hash(skb);
120		hash = rol32(hash, 16);
121		flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
122	} else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) {
123		flowlabel = ip6_flowlabel(inner_hdr);
124	}
125	return flowlabel;
126}
127
128static int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
129			       int proto, struct dst_entry *cache_dst)
130{
131	struct dst_entry *dst = skb_dst(skb);
132	struct net_device *dev = dst_dev(dst);
133	struct net *net = dev_net(dev);
134	struct ipv6hdr *hdr, *inner_hdr;
135	struct ipv6_sr_hdr *isrh;
136	int hdrlen, tot_len, err;
137	__be32 flowlabel;
138
139	hdrlen = (osrh->hdrlen + 1) << 3;
140	tot_len = hdrlen + sizeof(*hdr);
141
142	err = skb_cow_head(skb, tot_len + dst_dev_overhead(cache_dst, skb));
143	if (unlikely(err))
144		return err;
145
146	inner_hdr = ipv6_hdr(skb);
147	flowlabel = seg6_make_flowlabel(net, skb, inner_hdr);
148
149	skb_push(skb, tot_len);
150	skb_reset_network_header(skb);
151	skb_mac_header_rebuild(skb);
152	hdr = ipv6_hdr(skb);
153
154	/* inherit tc, flowlabel and hlim
155	 * hlim will be decremented in ip6_forward() afterwards and
156	 * decapsulation will overwrite inner hlim with outer hlim
157	 */
158
159	if (skb->protocol == htons(ETH_P_IPV6)) {
160		ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
161			     flowlabel);
162		hdr->hop_limit = inner_hdr->hop_limit;
163	} else {
164		ip6_flow_hdr(hdr, 0, flowlabel);
165		hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
166
167		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
168
169		/* the control block has been erased, so we have to set the
170		 * iif once again.
171		 * We read the receiving interface index directly from the
172		 * skb->skb_iif as it is done in the IPv4 receiving path (i.e.:
173		 * ip_rcv_core(...)).
174		 */
175		IP6CB(skb)->iif = skb->skb_iif;
176	}
177
178	hdr->nexthdr = NEXTHDR_ROUTING;
179
180	isrh = (void *)hdr + sizeof(*hdr);
181	memcpy(isrh, osrh, hdrlen);
182
183	isrh->nexthdr = proto;
184
185	hdr->daddr = isrh->segments[isrh->first_segment];
186	set_tun_src(net, dev, &hdr->daddr, &hdr->saddr);
187
188#ifdef CONFIG_IPV6_SEG6_HMAC
189	if (sr_has_hmac(isrh)) {
190		err = seg6_push_hmac(net, &hdr->saddr, isrh);
191		if (unlikely(err))
192			return err;
193	}
194#endif
195
196	hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
197
198	skb_postpush_rcsum(skb, hdr, tot_len);
199
200	return 0;
201}
202
203/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
204int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
205{
206	return __seg6_do_srh_encap(skb, osrh, proto, NULL);
207}
208EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
209
210/* encapsulate an IPv6 packet within an outer IPv6 header with reduced SRH */
211static int seg6_do_srh_encap_red(struct sk_buff *skb,
212				 struct ipv6_sr_hdr *osrh, int proto,
213				 struct dst_entry *cache_dst)
214{
215	__u8 first_seg = osrh->first_segment;
216	struct dst_entry *dst = skb_dst(skb);
217	struct net_device *dev = dst_dev(dst);
218	struct net *net = dev_net(dev);
219	struct ipv6hdr *hdr, *inner_hdr;
220	int hdrlen = ipv6_optlen(osrh);
221	int red_tlv_offset, tlv_offset;
222	struct ipv6_sr_hdr *isrh;
223	bool skip_srh = false;
224	__be32 flowlabel;
225	int tot_len, err;
226	int red_hdrlen;
227	int tlvs_len;
228
229	if (first_seg > 0) {
230		red_hdrlen = hdrlen - sizeof(struct in6_addr);
231	} else {
232		/* NOTE: if tag/flags and/or other TLVs are introduced in the
233		 * seg6_iptunnel infrastructure, they should be considered when
234		 * deciding to skip the SRH.
235		 */
236		skip_srh = !sr_has_hmac(osrh);
237
238		red_hdrlen = skip_srh ? 0 : hdrlen;
239	}
240
241	tot_len = red_hdrlen + sizeof(struct ipv6hdr);
242
243	err = skb_cow_head(skb, tot_len + dst_dev_overhead(cache_dst, skb));
244	if (unlikely(err))
245		return err;
246
247	inner_hdr = ipv6_hdr(skb);
248	flowlabel = seg6_make_flowlabel(net, skb, inner_hdr);
249
250	skb_push(skb, tot_len);
251	skb_reset_network_header(skb);
252	skb_mac_header_rebuild(skb);
253	hdr = ipv6_hdr(skb);
254
255	/* based on seg6_do_srh_encap() */
256	if (skb->protocol == htons(ETH_P_IPV6)) {
257		ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
258			     flowlabel);
259		hdr->hop_limit = inner_hdr->hop_limit;
260	} else {
261		ip6_flow_hdr(hdr, 0, flowlabel);
262		hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
263
264		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
265		IP6CB(skb)->iif = skb->skb_iif;
266	}
267
268	/* no matter if we have to skip the SRH or not, the first segment
269	 * always comes in the pushed IPv6 header.
270	 */
271	hdr->daddr = osrh->segments[first_seg];
272
273	if (skip_srh) {
274		hdr->nexthdr = proto;
275
276		set_tun_src(net, dev, &hdr->daddr, &hdr->saddr);
277		goto out;
278	}
279
280	/* we cannot skip the SRH, slow path */
281
282	hdr->nexthdr = NEXTHDR_ROUTING;
283	isrh = (void *)hdr + sizeof(struct ipv6hdr);
284
285	if (unlikely(!first_seg)) {
286		/* this is a very rare case; we have only one SID but
287		 * we cannot skip the SRH since we are carrying some
288		 * other info.
289		 */
290		memcpy(isrh, osrh, hdrlen);
291		goto srcaddr;
292	}
293
294	tlv_offset = sizeof(*osrh) + (first_seg + 1) * sizeof(struct in6_addr);
295	red_tlv_offset = tlv_offset - sizeof(struct in6_addr);
296
297	memcpy(isrh, osrh, red_tlv_offset);
298
299	tlvs_len = hdrlen - tlv_offset;
300	if (unlikely(tlvs_len > 0)) {
301		const void *s = (const void *)osrh + tlv_offset;
302		void *d = (void *)isrh + red_tlv_offset;
303
304		memcpy(d, s, tlvs_len);
305	}
306
307	--isrh->first_segment;
308	isrh->hdrlen -= 2;
309
310srcaddr:
311	isrh->nexthdr = proto;
312	set_tun_src(net, dev, &hdr->daddr, &hdr->saddr);
313
314#ifdef CONFIG_IPV6_SEG6_HMAC
315	if (unlikely(!skip_srh && sr_has_hmac(isrh))) {
316		err = seg6_push_hmac(net, &hdr->saddr, isrh);
317		if (unlikely(err))
318			return err;
319	}
320#endif
321
322out:
323	hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
324
325	skb_postpush_rcsum(skb, hdr, tot_len);
326
327	return 0;
328}
329
330static int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
331				struct dst_entry *cache_dst)
332{
333	struct ipv6hdr *hdr, *oldhdr;
334	struct ipv6_sr_hdr *isrh;
335	int hdrlen, err;
336
337	hdrlen = (osrh->hdrlen + 1) << 3;
338
339	err = skb_cow_head(skb, hdrlen + dst_dev_overhead(cache_dst, skb));
340	if (unlikely(err))
341		return err;
342
343	oldhdr = ipv6_hdr(skb);
344
345	skb_pull(skb, sizeof(struct ipv6hdr));
346	skb_postpull_rcsum(skb, skb_network_header(skb),
347			   sizeof(struct ipv6hdr));
348
349	skb_push(skb, sizeof(struct ipv6hdr) + hdrlen);
350	skb_reset_network_header(skb);
351	skb_mac_header_rebuild(skb);
352
353	hdr = ipv6_hdr(skb);
354
355	memmove(hdr, oldhdr, sizeof(*hdr));
356
357	isrh = (void *)hdr + sizeof(*hdr);
358	memcpy(isrh, osrh, hdrlen);
359
360	isrh->nexthdr = hdr->nexthdr;
361	hdr->nexthdr = NEXTHDR_ROUTING;
362
363	isrh->segments[0] = hdr->daddr;
364	hdr->daddr = isrh->segments[isrh->first_segment];
365
366#ifdef CONFIG_IPV6_SEG6_HMAC
367	if (sr_has_hmac(isrh)) {
368		struct net *net = skb_dst_dev_net(skb);
369
370		err = seg6_push_hmac(net, &hdr->saddr, isrh);
371		if (unlikely(err))
372			return err;
373	}
374#endif
375
376	hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
377
378	skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen);
379
380	return 0;
381}
382
383static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst)
384{
385	struct dst_entry *dst = skb_dst(skb);
386	struct seg6_iptunnel_encap *tinfo;
387	int proto, err = 0;
388
389	tinfo = seg6_encap_lwtunnel(dst->lwtstate);
390
391	switch (tinfo->mode) {
392	case SEG6_IPTUN_MODE_INLINE:
393		if (skb->protocol != htons(ETH_P_IPV6))
394			return -EINVAL;
395
396		err = __seg6_do_srh_inline(skb, tinfo->srh, cache_dst);
397		if (err)
398			return err;
399		break;
400	case SEG6_IPTUN_MODE_ENCAP:
401	case SEG6_IPTUN_MODE_ENCAP_RED:
402		err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);
403		if (err)
404			return err;
405
406		if (skb->protocol == htons(ETH_P_IPV6))
407			proto = IPPROTO_IPV6;
408		else if (skb->protocol == htons(ETH_P_IP))
409			proto = IPPROTO_IPIP;
410		else
411			return -EINVAL;
412
413		if (tinfo->mode == SEG6_IPTUN_MODE_ENCAP)
414			err = __seg6_do_srh_encap(skb, tinfo->srh,
415						  proto, cache_dst);
416		else
417			err = seg6_do_srh_encap_red(skb, tinfo->srh,
418						    proto, cache_dst);
419
420		if (err)
421			return err;
422
423		skb_set_inner_transport_header(skb, skb_transport_offset(skb));
424		skb_set_inner_protocol(skb, skb->protocol);
425		skb->protocol = htons(ETH_P_IPV6);
426		break;
427	case SEG6_IPTUN_MODE_L2ENCAP:
428	case SEG6_IPTUN_MODE_L2ENCAP_RED:
429		if (!skb_mac_header_was_set(skb))
430			return -EINVAL;
431
432		if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0)
433			return -ENOMEM;
434
435		skb_mac_header_rebuild(skb);
436		skb_push(skb, skb->mac_len);
437
438		if (tinfo->mode == SEG6_IPTUN_MODE_L2ENCAP)
439			err = __seg6_do_srh_encap(skb, tinfo->srh,
440						  IPPROTO_ETHERNET,
441						  cache_dst);
442		else
443			err = seg6_do_srh_encap_red(skb, tinfo->srh,
444						    IPPROTO_ETHERNET,
445						    cache_dst);
446
447		if (err)
448			return err;
449
450		skb->protocol = htons(ETH_P_IPV6);
451		break;
452	}
453
454	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
455	nf_reset_ct(skb);
456
457	return 0;
458}
459
460/* insert an SRH within an IPv6 packet, just after the IPv6 header */
461int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
462{
463	return __seg6_do_srh_inline(skb, osrh, NULL);
464}
465EXPORT_SYMBOL_GPL(seg6_do_srh_inline);
466
467static int seg6_input_finish(struct net *net, struct sock *sk,
468			     struct sk_buff *skb)
469{
470	return dst_input(skb);
471}
472
473static int seg6_input_core(struct net *net, struct sock *sk,
474			   struct sk_buff *skb)
475{
476	struct dst_entry *orig_dst = skb_dst(skb);
477	struct dst_entry *dst = NULL;
478	struct lwtunnel_state *lwtst;
479	struct seg6_lwt *slwt;
480	int err;
481
482	/* We cannot dereference "orig_dst" once ip6_route_input() or
483	 * skb_dst_drop() is called. However, in order to detect a dst loop, we
484	 * need the address of its lwtstate. So, save the address of lwtstate
485	 * now and use it later as a comparison.
486	 */
487	lwtst = orig_dst->lwtstate;
488
489	slwt = seg6_lwt_lwtunnel(lwtst);
490
491	local_bh_disable();
492	dst = dst_cache_get(&slwt->cache_input);
493	local_bh_enable();
494
495	err = seg6_do_srh(skb, dst);
496	if (unlikely(err)) {
497		dst_release(dst);
498		goto drop;
499	}
500
501	if (!dst) {
502		ip6_route_input(skb);
503		dst = skb_dst(skb);
504
505		/* cache only if we don't create a dst reference loop */
506		if (!dst->error && lwtst != dst->lwtstate) {
507			local_bh_disable();
508			dst_cache_set_ip6(&slwt->cache_input, dst,
509					  &ipv6_hdr(skb)->saddr);
510			local_bh_enable();
511		}
512
513		err = skb_cow_head(skb, LL_RESERVED_SPACE(dst_dev(dst)));
514		if (unlikely(err))
515			goto drop;
516	} else {
517		skb_dst_drop(skb);
518		skb_dst_set(skb, dst);
519	}
520
521	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
522		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
523			       dev_net(skb->dev), NULL, skb, NULL,
524			       skb_dst_dev(skb), seg6_input_finish);
525
526	return seg6_input_finish(dev_net(skb->dev), NULL, skb);
527drop:
528	kfree_skb(skb);
529	return err;
530}
531
532static int seg6_input_nf(struct sk_buff *skb)
533{
534	struct net_device *dev = skb_dst_dev(skb);
535	struct net *net = dev_net(skb->dev);
536
537	switch (skb->protocol) {
538	case htons(ETH_P_IP):
539		return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, NULL,
540			       skb, NULL, dev, seg6_input_core);
541	case htons(ETH_P_IPV6):
542		return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, NULL,
543			       skb, NULL, dev, seg6_input_core);
544	}
545
546	return -EINVAL;
547}
548
549static int seg6_input(struct sk_buff *skb)
550{
551	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
552		return seg6_input_nf(skb);
553
554	return seg6_input_core(dev_net(skb->dev), NULL, skb);
555}
556
557static int seg6_output_core(struct net *net, struct sock *sk,
558			    struct sk_buff *skb)
559{
560	struct dst_entry *orig_dst = skb_dst(skb);
561	struct dst_entry *dst = NULL;
562	struct seg6_lwt *slwt;
563	int err;
564
565	slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate);
566
567	local_bh_disable();
568	dst = dst_cache_get(&slwt->cache_output);
569	local_bh_enable();
570
571	err = seg6_do_srh(skb, dst);
572	if (unlikely(err))
573		goto drop;
574
575	if (unlikely(!dst)) {
576		struct ipv6hdr *hdr = ipv6_hdr(skb);
577		struct flowi6 fl6;
578
579		memset(&fl6, 0, sizeof(fl6));
580		fl6.daddr = hdr->daddr;
581		fl6.saddr = hdr->saddr;
582		fl6.flowlabel = ip6_flowinfo(hdr);
583		fl6.flowi6_mark = skb->mark;
584		fl6.flowi6_proto = hdr->nexthdr;
585
586		dst = ip6_route_output(net, NULL, &fl6);
587		if (dst->error) {
588			err = dst->error;
589			goto drop;
590		}
591
592		/* cache only if we don't create a dst reference loop */
593		if (orig_dst->lwtstate != dst->lwtstate) {
594			local_bh_disable();
595			dst_cache_set_ip6(&slwt->cache_output, dst, &fl6.saddr);
596			local_bh_enable();
597		}
598
599		err = skb_cow_head(skb, LL_RESERVED_SPACE(dst_dev(dst)));
600		if (unlikely(err))
601			goto drop;
602	}
603
604	skb_dst_drop(skb);
605	skb_dst_set(skb, dst);
606
607	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
608		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
609			       NULL, dst_dev(dst), dst_output);
610
611	return dst_output(net, sk, skb);
612drop:
613	dst_release(dst);
614	kfree_skb(skb);
615	return err;
616}
617
618static int seg6_output_nf(struct net *net, struct sock *sk, struct sk_buff *skb)
619{
620	struct net_device *dev = skb_dst_dev(skb);
621
622	switch (skb->protocol) {
623	case htons(ETH_P_IP):
624		return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb,
625			       NULL, dev, seg6_output_core);
626	case htons(ETH_P_IPV6):
627		return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb,
628			       NULL, dev, seg6_output_core);
629	}
630
631	return -EINVAL;
632}
633
634static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
635{
636	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
637		return seg6_output_nf(net, sk, skb);
638
639	return seg6_output_core(net, sk, skb);
640}
641
642static int seg6_build_state(struct net *net, struct nlattr *nla,
643			    unsigned int family, const void *cfg,
644			    struct lwtunnel_state **ts,
645			    struct netlink_ext_ack *extack)
646{
647	struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1];
648	struct seg6_iptunnel_encap *tuninfo;
649	struct lwtunnel_state *newts;
650	int tuninfo_len, min_size;
651	struct seg6_lwt *slwt;
652	int err;
653
654	if (family != AF_INET && family != AF_INET6)
655		return -EINVAL;
656
657	err = nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla,
658					  seg6_iptunnel_policy, extack);
659
660	if (err < 0)
661		return err;
662
663	if (!tb[SEG6_IPTUNNEL_SRH])
664		return -EINVAL;
665
666	tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]);
667	tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]);
668
669	/* tuninfo must contain at least the iptunnel encap structure,
670	 * the SRH and one segment
671	 */
672	min_size = sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) +
673		   sizeof(struct in6_addr);
674	if (tuninfo_len < min_size)
675		return -EINVAL;
676
677	switch (tuninfo->mode) {
678	case SEG6_IPTUN_MODE_INLINE:
679		if (family != AF_INET6)
680			return -EINVAL;
681
682		break;
683	case SEG6_IPTUN_MODE_ENCAP:
684		break;
685	case SEG6_IPTUN_MODE_L2ENCAP:
686		break;
687	case SEG6_IPTUN_MODE_ENCAP_RED:
688		break;
689	case SEG6_IPTUN_MODE_L2ENCAP_RED:
690		break;
691	default:
692		return -EINVAL;
693	}
694
695	/* verify that SRH is consistent */
696	if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false))
697		return -EINVAL;
698
699	newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt));
700	if (!newts)
701		return -ENOMEM;
702
703	slwt = seg6_lwt_lwtunnel(newts);
704
705	err = dst_cache_init(&slwt->cache_input, GFP_ATOMIC);
706	if (err)
707		goto err_free_newts;
708
709	err = dst_cache_init(&slwt->cache_output, GFP_ATOMIC);
710	if (err)
711		goto err_destroy_input;
712
713	memcpy(&slwt->tuninfo, tuninfo, tuninfo_len);
714
715	newts->type = LWTUNNEL_ENCAP_SEG6;
716	newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
717
718	if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP)
719		newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
720
721	newts->headroom = seg6_lwt_headroom(tuninfo);
722
723	*ts = newts;
724
725	return 0;
726
727err_destroy_input:
728	dst_cache_destroy(&slwt->cache_input);
729err_free_newts:
730	kfree(newts);
731	return err;
732}
733
734static void seg6_destroy_state(struct lwtunnel_state *lwt)
735{
736	struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwt);
737
738	dst_cache_destroy(&slwt->cache_input);
739	dst_cache_destroy(&slwt->cache_output);
740}
741
742static int seg6_fill_encap_info(struct sk_buff *skb,
743				struct lwtunnel_state *lwtstate)
744{
745	struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);
746
747	if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo))
748		return -EMSGSIZE;
749
750	return 0;
751}
752
753static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)
754{
755	struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);
756
757	return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo));
758}
759
760static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
761{
762	struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a);
763	struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b);
764	int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr);
765
766	if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr))
767		return 1;
768
769	return memcmp(a_hdr, b_hdr, len);
770}
771
772static const struct lwtunnel_encap_ops seg6_iptun_ops = {
773	.build_state = seg6_build_state,
774	.destroy_state = seg6_destroy_state,
775	.output = seg6_output,
776	.input = seg6_input,
777	.fill_encap = seg6_fill_encap_info,
778	.get_encap_size = seg6_encap_nlsize,
779	.cmp_encap = seg6_encap_cmp,
780	.owner = THIS_MODULE,
781};
782
783int __init seg6_iptunnel_init(void)
784{
785	return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6);
786}
787
788void seg6_iptunnel_exit(void)
789{
790	lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6);
791}
~/ Read-only file preview.