diff options
Diffstat (limited to 'net/ipv6/ip6_input.c')
| -rw-r--r-- | net/ipv6/ip6_input.c | 106 | 
1 files changed, 66 insertions, 40 deletions
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index a83e9209cec..51d54dc376f 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -44,12 +44,19 @@  #include <net/ip6_route.h>  #include <net/addrconf.h>  #include <net/xfrm.h> +#include <net/inet_ecn.h> - -inline int ip6_rcv_finish( struct sk_buff *skb) +int ip6_rcv_finish(struct sk_buff *skb)  { -	if (skb_dst(skb) == NULL) +	if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) { +		const struct inet6_protocol *ipprot; + +		ipprot = rcu_dereference(inet6_protos[ipv6_hdr(skb)->nexthdr]); +		if (ipprot && ipprot->early_demux) +			ipprot->early_demux(skb); +	} +	if (!skb_dst(skb))  		ip6_route_input(skb);  	return dst_input(skb); @@ -57,7 +64,7 @@ inline int ip6_rcv_finish( struct sk_buff *skb)  int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)  { -	struct ipv6hdr *hdr; +	const struct ipv6hdr *hdr;  	u32 		pkt_len;  	struct inet6_dev *idev;  	struct net *net = dev_net(skb->dev); @@ -102,6 +109,10 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt  	if (hdr->version != 6)  		goto err; +	IP6_ADD_STATS_BH(dev_net(dev), idev, +			 IPSTATS_MIB_NOECTPKTS + +				(ipv6_get_dsfield(hdr) & INET_ECN_MASK), +			 max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));  	/*  	 * RFC4291 2.5.3  	 * A packet received on an interface with a destination address @@ -111,6 +122,35 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt  	    ipv6_addr_loopback(&hdr->daddr))  		goto err; +	/* RFC4291 Errata ID: 3480 +	 * Interface-Local scope spans only a single interface on a +	 * node and is useful only for loopback transmission of +	 * multicast.  Packets with interface-local scope received +	 * from another node must be discarded. +	 */ +	if (!(skb->pkt_type == PACKET_LOOPBACK || +	      dev->flags & IFF_LOOPBACK) && +	    ipv6_addr_is_multicast(&hdr->daddr) && +	    IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) +		goto err; + +	/* RFC4291 2.7 +	 * Nodes must not originate a packet to a multicast address whose scope +	 * field contains the reserved value 0; if such a packet is received, it +	 * must be silently dropped. +	 */ +	if (ipv6_addr_is_multicast(&hdr->daddr) && +	    IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) +		goto err; + +	/* +	 * RFC4291 2.7 +	 * Multicast addresses must not be used as source addresses in IPv6 +	 * packets or appear in any Routing header. +	 */ +	if (ipv6_addr_is_multicast(&hdr->saddr)) +		goto err; +  	skb->transport_header = skb->network_header + sizeof(*hdr);  	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); @@ -160,12 +200,12 @@ drop:  static int ip6_input_finish(struct sk_buff *skb)  { +	struct net *net = dev_net(skb_dst(skb)->dev);  	const struct inet6_protocol *ipprot; -	unsigned int nhoff; -	int nexthdr, raw; -	u8 hash;  	struct inet6_dev *idev; -	struct net *net = dev_net(skb_dst(skb)->dev); +	unsigned int nhoff; +	int nexthdr; +	bool raw;  	/*  	 *	Parse extension headers @@ -180,13 +220,11 @@ resubmit:  	nexthdr = skb_network_header(skb)[nhoff];  	raw = raw6_local_deliver(skb, nexthdr); - -	hash = nexthdr & (MAX_INET_PROTOS - 1); -	if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) { +	if ((ipprot = rcu_dereference(inet6_protos[nexthdr])) != NULL) {  		int ret;  		if (ipprot->flags & INET6_PROTO_FINAL) { -			struct ipv6hdr *hdr; +			const struct ipv6hdr *hdr;  			/* Free reference early: we don't need it any more,  			   and it may hold ip_conntrack module loaded @@ -199,7 +237,7 @@ resubmit:  			if (ipv6_addr_is_multicast(&hdr->daddr) &&  			    !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,  			    &hdr->saddr) && -			    !ipv6_is_mld(skb, nexthdr)) +			    !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb)))  				goto discard;  		}  		if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && @@ -219,9 +257,11 @@ resubmit:  				icmpv6_send(skb, ICMPV6_PARAMPROB,  					    ICMPV6_UNK_NEXTHDR, nhoff);  			} -		} else +			kfree_skb(skb); +		} else {  			IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDELIVERS); -		kfree_skb(skb); +			consume_skb(skb); +		}  	}  	rcu_read_unlock();  	return 0; @@ -242,8 +282,8 @@ int ip6_input(struct sk_buff *skb)  int ip6_mc_input(struct sk_buff *skb)  { -	struct ipv6hdr *hdr; -	int deliver; +	const struct ipv6hdr *hdr; +	bool deliver;  	IP6_UPD_PO_STATS_BH(dev_net(skb_dst(skb)->dev),  			 ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INMCAST, @@ -257,7 +297,8 @@ int ip6_mc_input(struct sk_buff *skb)  	 *      IPv6 multicast router mode is now supported ;)  	 */  	if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && -	    !(ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) && +	    !(ipv6_addr_type(&hdr->daddr) & +	      (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) &&  	    likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) {  		/*  		 * Okay, we try to forward - split and duplicate @@ -267,46 +308,31 @@ int ip6_mc_input(struct sk_buff *skb)  		struct inet6_skb_parm *opt = IP6CB(skb);  		/* Check for MLD */ -		if (unlikely(opt->ra)) { +		if (unlikely(opt->flags & IP6SKB_ROUTERALERT)) {  			/* Check if this is a mld message */ -			u8 *ptr = skb_network_header(skb) + opt->ra; -			struct icmp6hdr *icmp6;  			u8 nexthdr = hdr->nexthdr; +			__be16 frag_off;  			int offset;  			/* Check if the value of Router Alert  			 * is for MLD (0x0000).  			 */ -			if ((ptr[2] | ptr[3]) == 0) { -				deliver = 0; +			if (opt->ra == htons(IPV6_OPT_ROUTERALERT_MLD)) { +				deliver = false;  				if (!ipv6_ext_hdr(nexthdr)) {  					/* BUG */  					goto out;  				}  				offset = ipv6_skip_exthdr(skb, sizeof(*hdr), -							  &nexthdr); +							  &nexthdr, &frag_off);  				if (offset < 0)  					goto out; -				if (nexthdr != IPPROTO_ICMPV6) -					goto out; - -				if (!pskb_may_pull(skb, (skb_network_header(skb) + -						   offset + 1 - skb->data))) +				if (!ipv6_is_mld(skb, nexthdr, offset))  					goto out; -				icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); - -				switch (icmp6->icmp6_type) { -				case ICMPV6_MGM_QUERY: -				case ICMPV6_MGM_REPORT: -				case ICMPV6_MGM_REDUCTION: -				case ICMPV6_MLD2_REPORT: -					deliver = 1; -					break; -				} -				goto out; +				deliver = true;  			}  			/* unknown RA - process it normally */  		}  | 
