xref: /openbmc/linux/net/ipv6/output_core.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23c73a036SVlad Yasevich /*
33c73a036SVlad Yasevich  * IPv6 library code, needed by static components when full IPv6 support is
43c73a036SVlad Yasevich  * not configured or static.  These functions are needed by GSO/GRO implementation.
53c73a036SVlad Yasevich  */
63c73a036SVlad Yasevich #include <linux/export.h>
75188cd44SBen Hutchings #include <net/ip.h>
83c73a036SVlad Yasevich #include <net/ipv6.h>
93c73a036SVlad Yasevich #include <net/ip6_fib.h>
103ce9b35fSCong Wang #include <net/addrconf.h>
116dfac5c3SHannes Frederic Sowa #include <net/secure_seq.h>
12a263653eSPablo Neira Ayuso #include <linux/netfilter.h>
133c73a036SVlad Yasevich 
__ipv6_select_ident(struct net * net,const struct in6_addr * dst,const struct in6_addr * src)14df453700SEric Dumazet static u32 __ipv6_select_ident(struct net *net,
15fd0273d7SMartin KaFai Lau 			       const struct in6_addr *dst,
16fd0273d7SMartin KaFai Lau 			       const struct in6_addr *src)
170508c07fSVlad Yasevich {
18*d247aabdSJason A. Donenfeld 	return get_random_u32_above(0);
190508c07fSVlad Yasevich }
200508c07fSVlad Yasevich 
210c19f846SWillem de Bruijn /* This function exists only for tap drivers that must support broken
220c19f846SWillem de Bruijn  * clients requesting UFO without specifying an IPv6 fragment ID.
230c19f846SWillem de Bruijn  *
240c19f846SWillem de Bruijn  * This is similar to ipv6_select_ident() but we use an independent hash
250c19f846SWillem de Bruijn  * seed to limit information leakage.
260c19f846SWillem de Bruijn  *
270c19f846SWillem de Bruijn  * The network header must be set before calling this.
280c19f846SWillem de Bruijn  */
ipv6_proxy_select_ident(struct net * net,struct sk_buff * skb)290c19f846SWillem de Bruijn __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
300c19f846SWillem de Bruijn {
310c19f846SWillem de Bruijn 	struct in6_addr buf[2];
320c19f846SWillem de Bruijn 	struct in6_addr *addrs;
330c19f846SWillem de Bruijn 	u32 id;
340c19f846SWillem de Bruijn 
350c19f846SWillem de Bruijn 	addrs = skb_header_pointer(skb,
360c19f846SWillem de Bruijn 				   skb_network_offset(skb) +
370c19f846SWillem de Bruijn 				   offsetof(struct ipv6hdr, saddr),
380c19f846SWillem de Bruijn 				   sizeof(buf), buf);
390c19f846SWillem de Bruijn 	if (!addrs)
400c19f846SWillem de Bruijn 		return 0;
410c19f846SWillem de Bruijn 
42df453700SEric Dumazet 	id = __ipv6_select_ident(net, &addrs[1], &addrs[0]);
430c19f846SWillem de Bruijn 	return htonl(id);
440c19f846SWillem de Bruijn }
450c19f846SWillem de Bruijn EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
460c19f846SWillem de Bruijn 
ipv6_select_ident(struct net * net,const struct in6_addr * daddr,const struct in6_addr * saddr)477f159867SEric Dumazet __be32 ipv6_select_ident(struct net *net,
48fd0273d7SMartin KaFai Lau 			 const struct in6_addr *daddr,
49fd0273d7SMartin KaFai Lau 			 const struct in6_addr *saddr)
500508c07fSVlad Yasevich {
510508c07fSVlad Yasevich 	u32 id;
520508c07fSVlad Yasevich 
53df453700SEric Dumazet 	id = __ipv6_select_ident(net, daddr, saddr);
54286c2349SMartin KaFai Lau 	return htonl(id);
550508c07fSVlad Yasevich }
560508c07fSVlad Yasevich EXPORT_SYMBOL(ipv6_select_ident);
570508c07fSVlad Yasevich 
ip6_find_1stfragopt(struct sk_buff * skb,u8 ** nexthdr)583c73a036SVlad Yasevich int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
593c73a036SVlad Yasevich {
606399f1faSSabrina Dubroca 	unsigned int offset = sizeof(struct ipv6hdr);
6129a3cad5SSimon Horman 	unsigned int packet_len = skb_tail_pointer(skb) -
6229a3cad5SSimon Horman 		skb_network_header(skb);
633c73a036SVlad Yasevich 	int found_rhdr = 0;
643c73a036SVlad Yasevich 	*nexthdr = &ipv6_hdr(skb)->nexthdr;
653c73a036SVlad Yasevich 
662423496aSCraig Gallek 	while (offset <= packet_len) {
672423496aSCraig Gallek 		struct ipv6_opt_hdr *exthdr;
683c73a036SVlad Yasevich 
693c73a036SVlad Yasevich 		switch (**nexthdr) {
703c73a036SVlad Yasevich 
713c73a036SVlad Yasevich 		case NEXTHDR_HOP:
723c73a036SVlad Yasevich 			break;
733c73a036SVlad Yasevich 		case NEXTHDR_ROUTING:
743c73a036SVlad Yasevich 			found_rhdr = 1;
753c73a036SVlad Yasevich 			break;
763c73a036SVlad Yasevich 		case NEXTHDR_DEST:
773c73a036SVlad Yasevich #if IS_ENABLED(CONFIG_IPV6_MIP6)
783c73a036SVlad Yasevich 			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
793c73a036SVlad Yasevich 				break;
803c73a036SVlad Yasevich #endif
813c73a036SVlad Yasevich 			if (found_rhdr)
823c73a036SVlad Yasevich 				return offset;
833c73a036SVlad Yasevich 			break;
843c73a036SVlad Yasevich 		default:
853c73a036SVlad Yasevich 			return offset;
863c73a036SVlad Yasevich 		}
873c73a036SVlad Yasevich 
882423496aSCraig Gallek 		if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
892423496aSCraig Gallek 			return -EINVAL;
902423496aSCraig Gallek 
913c73a036SVlad Yasevich 		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
923c73a036SVlad Yasevich 						 offset);
933de33e1bSStefano Brivio 		offset += ipv6_optlen(exthdr);
943de33e1bSStefano Brivio 		if (offset > IPV6_MAXPLEN)
956399f1faSSabrina Dubroca 			return -EINVAL;
962423496aSCraig Gallek 		*nexthdr = &exthdr->nexthdr;
973c73a036SVlad Yasevich 	}
983c73a036SVlad Yasevich 
992423496aSCraig Gallek 	return -EINVAL;
1003c73a036SVlad Yasevich }
1013c73a036SVlad Yasevich EXPORT_SYMBOL(ip6_find_1stfragopt);
1023ce9b35fSCong Wang 
1033ce9b35fSCong Wang #if IS_ENABLED(CONFIG_IPV6)
ip6_dst_hoplimit(struct dst_entry * dst)1043ce9b35fSCong Wang int ip6_dst_hoplimit(struct dst_entry *dst)
1053ce9b35fSCong Wang {
1063ce9b35fSCong Wang 	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
1073ce9b35fSCong Wang 	if (hoplimit == 0) {
1083ce9b35fSCong Wang 		struct net_device *dev = dst->dev;
1093ce9b35fSCong Wang 		struct inet6_dev *idev;
1103ce9b35fSCong Wang 
1113ce9b35fSCong Wang 		rcu_read_lock();
1123ce9b35fSCong Wang 		idev = __in6_dev_get(dev);
1133ce9b35fSCong Wang 		if (idev)
1143ce9b35fSCong Wang 			hoplimit = idev->cnf.hop_limit;
1153ce9b35fSCong Wang 		else
1163ce9b35fSCong Wang 			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
1173ce9b35fSCong Wang 		rcu_read_unlock();
1183ce9b35fSCong Wang 	}
1193ce9b35fSCong Wang 	return hoplimit;
1203ce9b35fSCong Wang }
1213ce9b35fSCong Wang EXPORT_SYMBOL(ip6_dst_hoplimit);
1223ce9b35fSCong Wang #endif
123788787b5SCong Wang 
__ip6_local_out(struct net * net,struct sock * sk,struct sk_buff * skb)124cf91a99dSEric W. Biederman int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
125788787b5SCong Wang {
126788787b5SCong Wang 	int len;
127788787b5SCong Wang 
128788787b5SCong Wang 	len = skb->len - sizeof(struct ipv6hdr);
129788787b5SCong Wang 	if (len > IPV6_MAXPLEN)
130788787b5SCong Wang 		len = 0;
131788787b5SCong Wang 	ipv6_hdr(skb)->payload_len = htons(len);
132f6c20c59Shuizhang 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
133788787b5SCong Wang 
134a8e3e1a9SDavid Ahern 	/* if egress device is enslaved to an L3 master device pass the
135a8e3e1a9SDavid Ahern 	 * skb to its handler for processing
136a8e3e1a9SDavid Ahern 	 */
137a8e3e1a9SDavid Ahern 	skb = l3mdev_ip6_out(sk, skb);
138a8e3e1a9SDavid Ahern 	if (unlikely(!skb))
139a8e3e1a9SDavid Ahern 		return 0;
140a8e3e1a9SDavid Ahern 
141b4e479a9SEli Cooper 	skb->protocol = htons(ETH_P_IPV6);
142b4e479a9SEli Cooper 
14329a26a56SEric W. Biederman 	return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
14429a26a56SEric W. Biederman 		       net, sk, skb, NULL, skb_dst(skb)->dev,
14513206b6bSEric W. Biederman 		       dst_output);
146788787b5SCong Wang }
147788787b5SCong Wang EXPORT_SYMBOL_GPL(__ip6_local_out);
148788787b5SCong Wang 
ip6_local_out(struct net * net,struct sock * sk,struct sk_buff * skb)14933224b16SEric W. Biederman int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
150788787b5SCong Wang {
151788787b5SCong Wang 	int err;
152788787b5SCong Wang 
153cf91a99dSEric W. Biederman 	err = __ip6_local_out(net, sk, skb);
154788787b5SCong Wang 	if (likely(err == 1))
15513206b6bSEric W. Biederman 		err = dst_output(net, sk, skb);
156788787b5SCong Wang 
157788787b5SCong Wang 	return err;
158788787b5SCong Wang }
159788787b5SCong Wang EXPORT_SYMBOL_GPL(ip6_local_out);
160