ip6_input.c 7.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *	IPv6 input
3
 *	Linux INET6 implementation
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 *	Authors:
 *	Pedro Roque		<roque@di.fc.ul.pt>
 *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
 *
 *	$Id: ip6_input.c,v 1.19 2000/12/13 18:31:50 davem Exp $
 *
 *	Based in linux/net/ipv4/ip_input.c
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */
/* Changes
 *
 * 	Mitsuru KANDA @USAGI and
 * 	YOSHIFUJI Hideaki @USAGI: Remove ipv6_parse_exthdrs().
 */

#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/in6.h>
#include <linux/icmpv6.h>
32
#include <linux/mroute6.h>
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>

#include <net/sock.h>
#include <net/snmp.h>

#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/xfrm.h>



51
inline int ip6_rcv_finish( struct sk_buff *skb)
L
Linus Torvalds 已提交
52 53 54 55 56 57 58
{
	if (skb->dst == NULL)
		ip6_route_input(skb);

	return dst_input(skb);
}

D
David S. Miller 已提交
59
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
L
Linus Torvalds 已提交
60 61 62
{
	struct ipv6hdr *hdr;
	u32 		pkt_len;
63
	struct inet6_dev *idev;
L
Linus Torvalds 已提交
64

65 66 67 68 69 70
	if (skb->pkt_type == PACKET_OTHERHOST) {
		kfree_skb(skb);
		return 0;
	}

	rcu_read_lock();
L
Linus Torvalds 已提交
71

72 73 74
	idev = __in6_dev_get(skb->dev);

	IP6_INC_STATS_BH(idev, IPSTATS_MIB_INRECEIVES);
L
Linus Torvalds 已提交
75 76

	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
77 78
		IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
		rcu_read_unlock();
L
Linus Torvalds 已提交
79 80 81
		goto out;
	}

82 83
	memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));

L
Linus Torvalds 已提交
84 85 86 87 88 89
	/*
	 * Store incoming device index. When the packet will
	 * be queued, we cannot refer to skb->dev anymore.
	 *
	 * BTW, when we send a packet for our own local address on a
	 * non-loopback interface (e.g. ethX), it is being delivered
90
	 * via the loopback interface (lo) here; skb->dev = loopback_dev.
L
Linus Torvalds 已提交
91 92 93 94
	 * It, however, should be considered as if it is being
	 * arrived via the sending interface (ethX), because of the
	 * nature of scoping architecture. --yoshfuji
	 */
95
	IP6CB(skb)->iif = skb->dst ? ip6_dst_idev(skb->dst)->dev->ifindex : dev->ifindex;
L
Linus Torvalds 已提交
96

97
	if (unlikely(!pskb_may_pull(skb, sizeof(*hdr))))
L
Linus Torvalds 已提交
98 99
		goto err;

100
	hdr = ipv6_hdr(skb);
L
Linus Torvalds 已提交
101 102 103 104

	if (hdr->version != 6)
		goto err;

105
	skb->transport_header = skb->network_header + sizeof(*hdr);
106 107
	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);

L
Linus Torvalds 已提交
108 109 110 111
	pkt_len = ntohs(hdr->payload_len);

	/* pkt_len may be zero if Jumbo payload option is present */
	if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
112 113 114 115
		if (pkt_len + sizeof(struct ipv6hdr) > skb->len) {
			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INTRUNCATEDPKTS);
			goto drop;
		}
L
Linus Torvalds 已提交
116
		if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) {
117
			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
118 119
			goto drop;
		}
120
		hdr = ipv6_hdr(skb);
L
Linus Torvalds 已提交
121 122 123
	}

	if (hdr->nexthdr == NEXTHDR_HOP) {
124
		if (ipv6_parse_hopopts(skb) < 0) {
125 126
			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
			rcu_read_unlock();
L
Linus Torvalds 已提交
127 128 129 130
			return 0;
		}
	}

131 132
	rcu_read_unlock();

133 134
	return NF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, dev, NULL,
		       ip6_rcv_finish);
L
Linus Torvalds 已提交
135
err:
136
	IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
L
Linus Torvalds 已提交
137
drop:
138
	rcu_read_unlock();
L
Linus Torvalds 已提交
139 140 141 142 143 144 145 146 147 148
	kfree_skb(skb);
out:
	return 0;
}

/*
 *	Deliver the packet to the host
 */


P
Patrick McHardy 已提交
149
static int ip6_input_finish(struct sk_buff *skb)
L
Linus Torvalds 已提交
150 151 152
{
	struct inet6_protocol *ipprot;
	unsigned int nhoff;
153
	int nexthdr, raw;
L
Linus Torvalds 已提交
154
	u8 hash;
155
	struct inet6_dev *idev;
L
Linus Torvalds 已提交
156 157 158 159 160 161 162

	/*
	 *	Parse extension headers
	 */

	rcu_read_lock();
resubmit:
163
	idev = ip6_dst_idev(skb->dst);
164
	if (!pskb_pull(skb, skb_transport_offset(skb)))
L
Linus Torvalds 已提交
165
		goto discard;
166
	nhoff = IP6CB(skb)->nhoff;
167
	nexthdr = skb_network_header(skb)[nhoff];
L
Linus Torvalds 已提交
168

169
	raw = raw6_local_deliver(skb, nexthdr);
L
Linus Torvalds 已提交
170 171 172 173

	hash = nexthdr & (MAX_INET_PROTOS - 1);
	if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {
		int ret;
174

L
Linus Torvalds 已提交
175
		if (ipprot->flags & INET6_PROTO_FINAL) {
176
			struct ipv6hdr *hdr;
L
Linus Torvalds 已提交
177

178 179 180 181 182
			/* Free reference early: we don't need it any more,
			   and it may hold ip_conntrack module loaded
			   indefinitely. */
			nf_reset(skb);

183
			skb_postpull_rcsum(skb, skb_network_header(skb),
184
					   skb_network_header_len(skb));
185
			hdr = ipv6_hdr(skb);
L
Linus Torvalds 已提交
186 187 188 189 190 191 192
			if (ipv6_addr_is_multicast(&hdr->daddr) &&
			    !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
			    &hdr->saddr) &&
			    !ipv6_is_mld(skb, nexthdr))
				goto discard;
		}
		if (!(ipprot->flags & INET6_PROTO_NOPOLICY) &&
193
		    !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
L
Linus Torvalds 已提交
194
			goto discard;
195

196
		ret = ipprot->handler(skb);
L
Linus Torvalds 已提交
197 198 199
		if (ret > 0)
			goto resubmit;
		else if (ret == 0)
200
			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
L
Linus Torvalds 已提交
201
	} else {
202
		if (!raw) {
L
Linus Torvalds 已提交
203
			if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
204
				IP6_INC_STATS_BH(idev, IPSTATS_MIB_INUNKNOWNPROTOS);
205
				icmpv6_send(skb, ICMPV6_PARAMPROB,
206 207
					    ICMPV6_UNK_NEXTHDR, nhoff,
					    skb->dev);
L
Linus Torvalds 已提交
208
			}
209
		} else
210
			IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
211
		kfree_skb(skb);
L
Linus Torvalds 已提交
212 213 214 215 216
	}
	rcu_read_unlock();
	return 0;

discard:
217
	IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225
	rcu_read_unlock();
	kfree_skb(skb);
	return 0;
}


int ip6_input(struct sk_buff *skb)
{
226 227
	return NF_HOOK(PF_INET6, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
		       ip6_input_finish);
L
Linus Torvalds 已提交
228 229 230 231 232 233 234
}

int ip6_mc_input(struct sk_buff *skb)
{
	struct ipv6hdr *hdr;
	int deliver;

235
	IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS);
L
Linus Torvalds 已提交
236

237
	hdr = ipv6_hdr(skb);
238
	deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
L
Linus Torvalds 已提交
239

240
#ifdef CONFIG_IPV6_MROUTE
L
Linus Torvalds 已提交
241
	/*
242
	 *      IPv6 multicast router mode is now supported ;)
L
Linus Torvalds 已提交
243
	 */
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	if (ipv6_devconf.mc_forwarding &&
	    likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) {
		/*
		 * Okay, we try to forward - split and duplicate
		 * packets.
		 */
		struct sk_buff *skb2;
		struct inet6_skb_parm *opt = IP6CB(skb);

		/* Check for MLD */
		if (unlikely(opt->ra)) {
			/* Check if this is a mld message */
			u8 *ptr = skb_network_header(skb) + opt->ra;
			struct icmp6hdr *icmp6;
			u8 nexthdr = hdr->nexthdr;
			int offset;

			/* Check if the value of Router Alert
			 * is for MLD (0x0000).
			 */
			if ((ptr[2] | ptr[3]) == 0) {
				if (!ipv6_ext_hdr(nexthdr)) {
					/* BUG */
					goto discard;
				}
				offset = ipv6_skip_exthdr(skb, sizeof(*hdr),
							  &nexthdr);
				if (offset < 0)
					goto discard;

				if (nexthdr != IPPROTO_ICMPV6)
					goto discard;

				if (!pskb_may_pull(skb, (skb_network_header(skb) +
						   offset + 1 - skb->data)))
					goto discard;

				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:
					break;
				default:
					/* Bogus */
					goto discard;
				}
				deliver = 1;
				goto out;
			}
			/* unknown RA - process it normally */
		}
L
Linus Torvalds 已提交
298

299 300 301 302 303 304
		if (deliver)
			skb2 = skb_clone(skb, GFP_ATOMIC);
		else {
			skb2 = skb;
			skb = NULL;
		}
305

306 307 308
		if (skb2) {
			skb2->dev = skb2->dst->dev;
			ip6_mr_input(skb2);
L
Linus Torvalds 已提交
309 310 311
		}
	}
#endif
312
out:
L
Linus Torvalds 已提交
313 314 315 316
	if (likely(deliver)) {
		ip6_input(skb);
		return 0;
	}
317
discard:
L
Linus Torvalds 已提交
318 319 320 321 322
	/* discard */
	kfree_skb(skb);

	return 0;
}