2104 lines · 53.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	IPv6 output functions *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> * *	Based on linux/net/ipv4/ip_output.c * *	Changes: *	A.N.Kuznetsov	:	airthmetics in fragmentation. *				extension headers are implemented. *				route changes now work. *				ip6_forward does not confuse sniffers. *				etc. * *      H. von Brand    :       Added missing #include <linux/string.h> *	Imran Patel	:	frag id should be in NBO *      Kazunori MIYAZAWA @USAGI *			:       add ip6_append_data and related functions *				for datagram xmit */ #include <linux/errno.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/in6.h>#include <linux/tcp.h>#include <linux/route.h>#include <linux/module.h>#include <linux/slab.h> #include <linux/bpf-cgroup.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv6.h> #include <net/sock.h>#include <net/snmp.h> #include <net/gso.h>#include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/rawv6.h>#include <net/icmp.h>#include <net/xfrm.h>#include <net/checksum.h>#include <linux/mroute6.h>#include <net/l3mdev.h>#include <net/lwtunnel.h>#include <net/ip_tunnels.h> static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb){	struct dst_entry *dst = skb_dst(skb);	struct net_device *dev = dst_dev_rcu(dst);	struct inet6_dev *idev = ip6_dst_idev(dst);	unsigned int hh_len = LL_RESERVED_SPACE(dev);	const struct in6_addr *daddr, *nexthop;	struct ipv6hdr *hdr;	struct neighbour *neigh;	int ret; 	/* Be paranoid, rather than too clever. */	if (unlikely(hh_len > skb_headroom(skb)) && dev->header_ops) {		/* idev stays alive because we hold rcu_read_lock(). */		skb = skb_expand_head(skb, hh_len);		if (!skb) {			IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);			return -ENOMEM;		}	} 	hdr = ipv6_hdr(skb);	daddr = &hdr->daddr;	if (unlikely(ipv6_addr_is_multicast(daddr))) {		if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&		    ((mroute6_is_socket(net, skb) &&		     !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||		     ipv6_chk_mcast_addr(dev, daddr, &hdr->saddr))) {			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); 			/* Do not check for IFF_ALLMULTI; multicast routing			   is not supported in any case.			 */			if (newskb)				NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING,					net, sk, newskb, NULL, newskb->dev,					dev_loopback_xmit); 			if (hdr->hop_limit == 0) {				IP6_INC_STATS(net, idev,					      IPSTATS_MIB_OUTDISCARDS);				kfree_skb(skb);				return 0;			}		} 		IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, skb->len);		if (IPV6_ADDR_MC_SCOPE(daddr) <= IPV6_ADDR_SCOPE_NODELOCAL &&		    !(dev->flags & IFF_LOOPBACK)) {			kfree_skb(skb);			return 0;		}	} 	if (lwtunnel_xmit_redirect(dst->lwtstate)) {		int res = lwtunnel_xmit(skb); 		if (res != LWTUNNEL_XMIT_CONTINUE)			return res;		hdr = ipv6_hdr(skb);		daddr = &hdr->daddr;	} 	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); 	nexthop = rt6_nexthop(dst_rt6_info(dst), daddr);	neigh = __ipv6_neigh_lookup_noref(dev, nexthop); 	if (IS_ERR_OR_NULL(neigh)) {		if (unlikely(!neigh))			neigh = __neigh_create(&nd_tbl, nexthop, dev, false);		if (IS_ERR(neigh)) {			IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);			kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);			return -EINVAL;		}	}	sock_confirm_neigh(skb, neigh);	ret = neigh_output(neigh, skb, false);	return ret;} static intip6_finish_output_gso_slowpath_drop(struct net *net, struct sock *sk,				    struct sk_buff *skb, unsigned int mtu){	struct sk_buff *segs, *nskb;	netdev_features_t features;	int ret = 0; 	/* Please see corresponding comment in ip_finish_output_gso	 * describing the cases where GSO segment length exceeds the	 * egress MTU.	 */	features = netif_skb_features(skb);	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);	if (IS_ERR_OR_NULL(segs)) {		kfree_skb(skb);		return -ENOMEM;	} 	consume_skb(skb); 	skb_list_walk_safe(segs, segs, nskb) {		int err; 		skb_mark_not_on_list(segs);		/* Last GSO segment can be smaller than gso_size (and MTU).		 * Adding a fragment header would produce an "atomic fragment",		 * which is considered harmful (RFC-8021). Avoid that.		 */		err = segs->len > mtu ?			ip6_fragment(net, sk, segs, ip6_finish_output2) :			ip6_finish_output2(net, sk, segs);		if (err && ret == 0)			ret = err;	} 	return ret;} static int ip6_finish_output_gso(struct net *net, struct sock *sk,				 struct sk_buff *skb, unsigned int mtu){	if (unlikely(!skb_gso_validate_network_len(skb, mtu)))		return ip6_finish_output_gso_slowpath_drop(net, sk, skb, mtu); 	return ip6_finish_output2(net, sk, skb);} static int __ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb){	unsigned int mtu; #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)	/* Policy lookup after SNAT yielded a new policy */	if (skb_dst(skb)->xfrm) {		IP6CB(skb)->flags |= IP6SKB_REROUTED;		return dst_output(net, sk, skb);	}#endif 	mtu = ip6_skb_dst_mtu(skb);	if (skb_is_gso(skb))		return ip6_finish_output_gso(net, sk, skb, mtu); 	if (unlikely(skb->len > mtu ||	    (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)))		return ip6_fragment(net, sk, skb, ip6_finish_output2); 	return ip6_finish_output2(net, sk, skb);} static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb){	int ret; 	ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);	switch (ret) {	case NET_XMIT_SUCCESS:	case NET_XMIT_CN:		return __ip6_finish_output(net, sk, skb) ? : ret;	default:		kfree_skb_reason(skb, SKB_DROP_REASON_BPF_CGROUP_EGRESS);		return ret;	}} int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb){	struct dst_entry *dst = skb_dst(skb);	struct net_device *dev, *indev = skb->dev;	struct inet6_dev *idev;	int ret; 	skb->protocol = htons(ETH_P_IPV6);	rcu_read_lock();	dev = dst_dev_rcu(dst);	idev = ip6_dst_idev(dst);	skb->dev = dev; 	if (unlikely(!idev || READ_ONCE(idev->cnf.disable_ipv6))) {		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);		rcu_read_unlock();		kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);		return 0;	} 	ret = NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,			   net, sk, skb, indev, dev,			   ip6_finish_output,			   !(IP6CB(skb)->flags & IP6SKB_REROUTED));	rcu_read_unlock();	return ret;}EXPORT_SYMBOL(ip6_output); bool ip6_autoflowlabel(struct net *net, const struct sock *sk){	if (!inet6_test_bit(AUTOFLOWLABEL_SET, sk))		return ip6_default_np_autolabel(net);	return inet6_test_bit(AUTOFLOWLABEL, sk);} int ip6_dst_hoplimit(struct dst_entry *dst){	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 	rcu_read_lock();	if (hoplimit == 0) {		struct net_device *dev = dst_dev_rcu(dst);		struct inet6_dev *idev; 		idev = __in6_dev_get(dev);		if (idev)			hoplimit = READ_ONCE(idev->cnf.hop_limit);		else			hoplimit = READ_ONCE(dev_net(dev)->ipv6.devconf_all->hop_limit);	}	rcu_read_unlock(); 	return hoplimit;}EXPORT_SYMBOL(ip6_dst_hoplimit); /* * xmit an sk_buff (used by TCP and SCTP) * Note : socket lock is not held for SYNACK packets, but might be modified * by calls to skb_set_owner_w() and ipv6_local_error(), * which are using proper atomic operations or spinlocks. */int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,	     __u32 mark, struct ipv6_txoptions *opt, int tclass, u32 priority){	const struct ipv6_pinfo *np = inet6_sk(sk);	struct in6_addr *first_hop = &fl6->daddr;	struct dst_entry *dst = skb_dst(skb);	struct inet6_dev *idev = ip6_dst_idev(dst);	struct net *net = sock_net(sk);	unsigned int head_room;	struct net_device *dev;	struct ipv6hdr *hdr;	u8  proto = fl6->flowi6_proto;	int seg_len = skb->len;	int ret, hlimit = -1;	u32 mtu; 	rcu_read_lock(); 	dev = dst_dev_rcu(dst);	head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dev);	if (opt)		head_room += opt->opt_nflen + opt->opt_flen; 	if (unlikely(head_room > skb_headroom(skb))) {		/* idev stays alive while we hold rcu_read_lock(). */		skb = skb_expand_head(skb, head_room);		if (!skb) {			IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);			ret = -ENOBUFS;			goto unlock;		}	} 	if (unlikely(opt)) {		seg_len += opt->opt_nflen + opt->opt_flen; 		if (opt->opt_flen)			proto = ipv6_push_frag_opts(skb, opt, proto); 		if (opt->opt_nflen)			proto = ipv6_push_nfrag_opts(skb, opt, proto,						     &first_hop,						     &fl6->saddr);	} 	if (unlikely(seg_len > IPV6_MAXPLEN))		seg_len = 0; 	__skb_push(skb, sizeof(struct ipv6hdr));	skb_reset_network_header(skb);	hdr = ipv6_hdr(skb); 	/*	 *	Fill in the IPv6 header	 */	if (np)		hlimit = READ_ONCE(np->hop_limit);	if (hlimit < 0)		hlimit = ip6_dst_hoplimit(dst); 	ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel,				ip6_autoflowlabel(net, sk), fl6)); 	hdr->payload_len = htons(seg_len);	hdr->nexthdr = proto;	hdr->hop_limit = hlimit; 	hdr->saddr = fl6->saddr;	hdr->daddr = *first_hop; 	skb->protocol = htons(ETH_P_IPV6);	skb->priority = priority;	skb->mark = mark; 	mtu = dst6_mtu(dst);	if (likely((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb))) {		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTREQUESTS); 		/* if egress device is enslaved to an L3 master device pass the		 * skb to its handler for processing		 */		skb = l3mdev_ip6_out((struct sock *)sk, skb);		if (unlikely(!skb)) {			ret = 0;			goto unlock;		} 		/* hooks should never assume socket lock is held.		 * we promote our socket to non const		 */		ret = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,			      net, (struct sock *)sk, skb, NULL, dev,			      dst_output);		goto unlock;	} 	ret = -EMSGSIZE;	skb->dev = dev;	/* ipv6_local_error() does not require socket lock,	 * we promote our socket to non const	 */	ipv6_local_error((struct sock *)sk, EMSGSIZE, fl6, mtu); 	IP6_INC_STATS(net, idev, IPSTATS_MIB_FRAGFAILS);	kfree_skb_reason(skb, SKB_DROP_REASON_PKT_TOO_BIG);unlock:	rcu_read_unlock();	return ret;}EXPORT_SYMBOL(ip6_xmit); static int ip6_call_ra_chain(struct sk_buff *skb, int sel){	struct ip6_ra_chain *ra;	struct sock *last = NULL; 	read_lock(&ip6_ra_lock);	for (ra = ip6_ra_chain; ra; ra = ra->next) {		struct sock *sk = ra->sk;		if (sk && ra->sel == sel &&		    (!sk->sk_bound_dev_if ||		     sk->sk_bound_dev_if == skb->dev->ifindex)) { 			if (inet6_test_bit(RTALERT_ISOLATE, sk) &&			    !net_eq(sock_net(sk), dev_net(skb->dev))) {				continue;			}			if (last) {				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);				if (skb2)					rawv6_rcv(last, skb2);			}			last = sk;		}	} 	if (last) {		rawv6_rcv(last, skb);		read_unlock(&ip6_ra_lock);		return 1;	}	read_unlock(&ip6_ra_lock);	return 0;} static int ip6_forward_proxy_check(struct sk_buff *skb){	struct ipv6hdr *hdr = ipv6_hdr(skb);	u8 nexthdr = hdr->nexthdr;	__be16 frag_off;	int offset; 	if (ipv6_ext_hdr(nexthdr)) {		offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off);		if (offset < 0)			return 0;	} else		offset = sizeof(struct ipv6hdr); 	if (nexthdr == IPPROTO_ICMPV6) {		struct icmp6hdr *icmp6; 		if (!pskb_may_pull(skb, (skb_network_header(skb) +					 offset + 1 - skb->data)))			return 0; 		icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); 		switch (icmp6->icmp6_type) {		case NDISC_ROUTER_SOLICITATION:		case NDISC_ROUTER_ADVERTISEMENT:		case NDISC_NEIGHBOUR_SOLICITATION:		case NDISC_NEIGHBOUR_ADVERTISEMENT:		case NDISC_REDIRECT:			/* For reaction involving unicast neighbor discovery			 * message destined to the proxied address, pass it to			 * input function.			 */			return 1;		default:			break;		}		hdr = ipv6_hdr(skb);	} 	/*	 * The proxying router can't forward traffic sent to a link-local	 * address, so signal the sender and discard the packet. This	 * behavior is clarified by the MIPv6 specification.	 */	if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) {		dst_link_failure(skb);		return -1;	} 	return 0;} static inline int ip6_forward_finish(struct net *net, struct sock *sk,				     struct sk_buff *skb){#ifdef CONFIG_NET_SWITCHDEV	if (skb->offload_l3_fwd_mark) {		consume_skb(skb);		return 0;	}#endif 	skb_clear_tstamp(skb);	return dst_output(net, sk, skb);

Lines 1–500 of 2104.