exthdrs.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 653 行 · 第 1/2 页

C
653
字号
/* *	Extension Header handling for IPv6 *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> *	Andi Kleen		<ak@muc.de> *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru> * *	$Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $ * *	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: *	yoshfuji		: ensure not to overrun while parsing  *				  tlv options. *	Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs(). *	YOSHIFUJI Hideaki @USAGI  Register inbound extension header *				  handlers as inet6_protocol{}. */#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/sched.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/in6.h>#include <linux/icmpv6.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 <asm/uaccess.h>/* *	Parsing tlv encoded headers. * *	Parsing function "func" returns 1, if parsing succeed *	and 0, if it failed. *	It MUST NOT touch skb->h. */struct tlvtype_proc {	int	type;	int	(*func)(struct sk_buff *skb, int offset);};/*********************  Generic functions *********************//* An unknown option is detected, decide what to do */static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff){	switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {	case 0: /* ignore */		return 1;	case 1: /* drop packet */		break;	case 3: /* Send ICMP if not a multicast address and drop packet */		/* Actually, it is redundant check. icmp_send		   will recheck in any case.		 */		if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))			break;	case 2: /* send ICMP PARM PROB regardless and drop packet */		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);		return 0;	};	kfree_skb(skb);	return 0;}/* Parse tlv encoded option header (hop-by-hop or destination) */static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb){	struct tlvtype_proc *curr;	int off = skb->h.raw - skb->nh.raw;	int len = ((skb->h.raw[1]+1)<<3);	if ((skb->h.raw + len) - skb->data > skb_headlen(skb))		goto bad;	off += 2;	len -= 2;	while (len > 0) {		int optlen = skb->nh.raw[off+1]+2;		switch (skb->nh.raw[off]) {		case IPV6_TLV_PAD0:			optlen = 1;			break;		case IPV6_TLV_PADN:			break;		default: /* Other TLV code so scan list */			if (optlen > len)				goto bad;			for (curr=procs; curr->type >= 0; curr++) {				if (curr->type == skb->nh.raw[off]) {					/* type specific length/alignment 					   checks will be performed in the 					   func(). */					if (curr->func(skb, off) == 0)						return 0;					break;				}			}			if (curr->type < 0) {				if (ip6_tlvopt_unknown(skb, off) == 0)					return 0;			}			break;		}		off += optlen;		len -= optlen;	}	if (len == 0)		return 1;bad:	kfree_skb(skb);	return 0;}/*****************************  Destination options header. *****************************/static struct tlvtype_proc tlvprocdestopt_lst[] = {	/* No destination options are defined now */	{-1,			NULL}};static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp){	struct sk_buff *skb = *skbp;	struct inet6_skb_parm *opt = IP6CB(skb);	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {		IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);		kfree_skb(skb);		return -1;	}	opt->dst1 = skb->h.raw - skb->nh.raw;	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {		skb->h.raw += ((skb->h.raw[1]+1)<<3);		*nhoffp = opt->dst1;		return 1;	}	IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);	return -1;}static struct inet6_protocol destopt_protocol = {	.handler	=	ipv6_destopt_rcv,	.flags		=	INET6_PROTO_NOPOLICY,};void __init ipv6_destopt_init(void){	if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)		printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");}/********************************  NONE header. No data in packet. ********************************/static int ipv6_nodata_rcv(struct sk_buff **skbp, unsigned int *nhoffp){	struct sk_buff *skb = *skbp;	kfree_skb(skb);	return 0;}static struct inet6_protocol nodata_protocol = {	.handler	=	ipv6_nodata_rcv,	.flags		=	INET6_PROTO_NOPOLICY,};void __init ipv6_nodata_init(void){	if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)		printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");}/********************************  Routing header. ********************************/static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp){	struct sk_buff *skb = *skbp;	struct inet6_skb_parm *opt = IP6CB(skb);	struct in6_addr *addr;	struct in6_addr daddr;	int n, i;	struct ipv6_rt_hdr *hdr;	struct rt0_hdr *rthdr;	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {		IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);		kfree_skb(skb);		return -1;	}	hdr = (struct ipv6_rt_hdr *) skb->h.raw;	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||	    skb->pkt_type != PACKET_HOST) {		IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);		kfree_skb(skb);		return -1;	}looped_back:	if (hdr->segments_left == 0) {		opt->srcrt = skb->h.raw - skb->nh.raw;		skb->h.raw += (hdr->hdrlen + 1) << 3;		opt->dst0 = opt->dst1;		opt->dst1 = 0;		*nhoffp = (&hdr->nexthdr) - skb->nh.raw;		return 1;	}	if (hdr->type != IPV6_SRCRT_TYPE_0) {		IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);		return -1;	}		if (hdr->hdrlen & 0x01) {		IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);		return -1;	}	/*	 *	This is the routing header forwarding algorithm from	 *	RFC 2460, page 16.	 */	n = hdr->hdrlen >> 1;	if (hdr->segments_left > n) {		IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);		return -1;	}	/* We are about to mangle packet header. Be careful!	   Do not damage packets queued somewhere.	 */	if (skb_cloned(skb)) {		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);		kfree_skb(skb);		/* the copy is a forwarded packet */		if (skb2 == NULL) {			IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);				return -1;		}		*skbp = skb = skb2;		opt = IP6CB(skb2);		hdr = (struct ipv6_rt_hdr *) skb2->h.raw;	}	if (skb->ip_summed == CHECKSUM_HW)		skb->ip_summed = CHECKSUM_NONE;	i = n - --hdr->segments_left;	rthdr = (struct rt0_hdr *) hdr;	addr = rthdr->addr;	addr += i - 1;	if (ipv6_addr_is_multicast(addr)) {		IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);		kfree_skb(skb);		return -1;	}	ipv6_addr_copy(&daddr, addr);	ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);	ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);	dst_release(xchg(&skb->dst, NULL));	ip6_route_input(skb);	if (skb->dst->error) {		skb_push(skb, skb->data - skb->nh.raw);		dst_input(skb);		return -1;	}	if (skb->dst->dev->flags&IFF_LOOPBACK) {		if (skb->nh.ipv6h->hop_limit <= 1) {			IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,				    0, skb->dev);			kfree_skb(skb);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?