diff options
Diffstat (limited to 'net/ipv6/seg6_iptunnel.c')
| -rw-r--r-- | net/ipv6/seg6_iptunnel.c | 82 | 
1 files changed, 63 insertions, 19 deletions
| diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 264d772d3c7d..bd6cc688bd19 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev,  }  /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ -static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)  {  	struct net *net = dev_net(skb_dst(skb)->dev);  	struct ipv6hdr *hdr, *inner_hdr; @@ -116,15 +116,22 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)  	 * hlim will be decremented in ip6_forward() afterwards and  	 * decapsulation will overwrite inner hlim with outer hlim  	 */ -	ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), -		     ip6_flowlabel(inner_hdr)); -	hdr->hop_limit = inner_hdr->hop_limit; + +	if (skb->protocol == htons(ETH_P_IPV6)) { +		ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), +			     ip6_flowlabel(inner_hdr)); +		hdr->hop_limit = inner_hdr->hop_limit; +	} else { +		ip6_flow_hdr(hdr, 0, 0); +		hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); +	} +  	hdr->nexthdr = NEXTHDR_ROUTING;  	isrh = (void *)hdr + sizeof(*hdr);  	memcpy(isrh, osrh, hdrlen); -	isrh->nexthdr = NEXTHDR_IPV6; +	isrh->nexthdr = proto;  	hdr->daddr = isrh->segments[isrh->first_segment];  	set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr); @@ -141,10 +148,10 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)  	return 0;  } +EXPORT_SYMBOL_GPL(seg6_do_srh_encap);  /* insert an SRH within an IPv6 packet, just after the IPv6 header */ -#ifdef CONFIG_IPV6_SEG6_INLINE -static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)  {  	struct ipv6hdr *hdr, *oldhdr;  	struct ipv6_sr_hdr *isrh; @@ -193,13 +200,13 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)  	return 0;  } -#endif +EXPORT_SYMBOL_GPL(seg6_do_srh_inline);  static int seg6_do_srh(struct sk_buff *skb)  {  	struct dst_entry *dst = skb_dst(skb);  	struct seg6_iptunnel_encap *tinfo; -	int err = 0; +	int proto, err = 0;  	tinfo = seg6_encap_lwtunnel(dst->lwtstate); @@ -209,19 +216,47 @@ static int seg6_do_srh(struct sk_buff *skb)  	}  	switch (tinfo->mode) { -#ifdef CONFIG_IPV6_SEG6_INLINE  	case SEG6_IPTUN_MODE_INLINE: +		if (skb->protocol != htons(ETH_P_IPV6)) +			return -EINVAL; +  		err = seg6_do_srh_inline(skb, tinfo->srh); +		if (err) +			return err; +  		skb_reset_inner_headers(skb);  		break; -#endif  	case SEG6_IPTUN_MODE_ENCAP: -		err = seg6_do_srh_encap(skb, tinfo->srh); +		if (skb->protocol == htons(ETH_P_IPV6)) +			proto = IPPROTO_IPV6; +		else if (skb->protocol == htons(ETH_P_IP)) +			proto = IPPROTO_IPIP; +		else +			return -EINVAL; + +		err = seg6_do_srh_encap(skb, tinfo->srh, proto); +		if (err) +			return err; + +		skb->protocol = htons(ETH_P_IPV6);  		break; -	} +	case SEG6_IPTUN_MODE_L2ENCAP: +		if (!skb_mac_header_was_set(skb)) +			return -EINVAL; -	if (err) -		return err; +		if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0) +			return -ENOMEM; + +		skb_mac_header_rebuild(skb); +		skb_push(skb, skb->mac_len); + +		err = seg6_do_srh_encap(skb, tinfo->srh, NEXTHDR_NONE); +		if (err) +			return err; + +		skb->protocol = htons(ETH_P_IPV6); +		break; +	}  	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));  	skb_set_transport_header(skb, sizeof(struct ipv6hdr)); @@ -336,6 +371,9 @@ static int seg6_build_state(struct nlattr *nla,  	struct seg6_lwt *slwt;  	int err; +	if (family != AF_INET && family != AF_INET6) +		return -EINVAL; +  	err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla,  			       seg6_iptunnel_policy, extack); @@ -357,12 +395,15 @@ static int seg6_build_state(struct nlattr *nla,  		return -EINVAL;  	switch (tuninfo->mode) { -#ifdef CONFIG_IPV6_SEG6_INLINE  	case SEG6_IPTUN_MODE_INLINE: +		if (family != AF_INET6) +			return -EINVAL; +  		break; -#endif  	case SEG6_IPTUN_MODE_ENCAP:  		break; +	case SEG6_IPTUN_MODE_L2ENCAP: +		break;  	default:  		return -EINVAL;  	} @@ -386,8 +427,11 @@ static int seg6_build_state(struct nlattr *nla,  	memcpy(&slwt->tuninfo, tuninfo, tuninfo_len);  	newts->type = LWTUNNEL_ENCAP_SEG6; -	newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | -			LWTUNNEL_STATE_INPUT_REDIRECT; +	newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; + +	if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) +		newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; +  	newts->headroom = seg6_lwt_headroom(tuninfo);  	*ts = newts; |