⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 exthdrs.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	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/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>#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)#include <net/xfrm.h>#endif#include <asm/uaccess.h>int ipv6_find_tlv(struct sk_buff *skb, int offset, int type){	const unsigned char *nh = skb_network_header(skb);	int packet_len = skb->tail - skb->network_header;	struct ipv6_opt_hdr *hdr;	int len;	if (offset + 2 > packet_len)		goto bad;	hdr = (struct ipv6_opt_hdr *)(nh + offset);	len = ((hdr->hdrlen + 1) << 3);	if (offset + len > packet_len)		goto bad;	offset += 2;	len -= 2;	while (len > 0) {		int opttype = nh[offset];		int optlen;		if (opttype == type)			return offset;		switch (opttype) {		case IPV6_TLV_PAD0:			optlen = 1;			break;		default:			optlen = nh[offset + 1] + 2;			if (optlen > len)				goto bad;			break;		}		offset += optlen;		len -= optlen;	}	/* not_found */ bad:	return -1;}EXPORT_SYMBOL_GPL(ipv6_find_tlv);/* *	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_network_header(skb)[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(&ipv6_hdr(skb)->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;	const unsigned char *nh = skb_network_header(skb);	int off = skb_network_header_len(skb);	int len = (skb_transport_header(skb)[1] + 1) << 3;	if (skb_transport_offset(skb) + len > skb_headlen(skb))		goto bad;	off += 2;	len -= 2;	while (len > 0) {		int optlen = nh[off + 1] + 2;		switch (nh[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 == nh[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. *****************************/#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)static int ipv6_dest_hao(struct sk_buff *skb, int optoff){	struct ipv6_destopt_hao *hao;	struct inet6_skb_parm *opt = IP6CB(skb);	struct ipv6hdr *ipv6h = ipv6_hdr(skb);	struct in6_addr tmp_addr;	int ret;	if (opt->dsthao) {		LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");		goto discard;	}	opt->dsthao = opt->dst1;	opt->dst1 = 0;	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);	if (hao->length != 16) {		LIMIT_NETDEBUG(			KERN_DEBUG "hao invalid option length = %d\n", hao->length);		goto discard;	}	if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {		LIMIT_NETDEBUG(			KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));		goto discard;	}	ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,			       (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);	if (unlikely(ret < 0))		goto discard;	if (skb_cloned(skb)) {		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))			goto discard;		/* update all variable using below by copied skbuff */		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +						  optoff);		ipv6h = ipv6_hdr(skb);	}	if (skb->ip_summed == CHECKSUM_COMPLETE)		skb->ip_summed = CHECKSUM_NONE;	ipv6_addr_copy(&tmp_addr, &ipv6h->saddr);	ipv6_addr_copy(&ipv6h->saddr, &hao->addr);	ipv6_addr_copy(&hao->addr, &tmp_addr);	if (skb->tstamp.tv64 == 0)		__net_timestamp(skb);	return 1; discard:	kfree_skb(skb);	return 0;}#endifstatic struct tlvtype_proc tlvprocdestopt_lst[] = {#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)	{		.type	= IPV6_TLV_HAO,		.func	= ipv6_dest_hao,	},#endif	{-1,			NULL}};static int ipv6_destopt_rcv(struct sk_buff *skb){	struct inet6_skb_parm *opt = IP6CB(skb);#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)	__u16 dstbuf;#endif	struct dst_entry *dst;	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||	    !pskb_may_pull(skb, (skb_transport_offset(skb) +				 ((skb_transport_header(skb)[1] + 1) << 3)))) {		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),				 IPSTATS_MIB_INHDRERRORS);		kfree_skb(skb);		return -1;	}	opt->lastopt = opt->dst1 = skb_network_header_len(skb);#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)	dstbuf = opt->dst1;#endif	dst = dst_clone(skb->dst);	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {		dst_release(dst);		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;		opt = IP6CB(skb);#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)		opt->nhoff = dstbuf;#else		opt->nhoff = opt->dst1;#endif		return 1;	}	IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);	dst_release(dst);	return -1;}static struct inet6_protocol destopt_protocol = {	.handler	=	ipv6_destopt_rcv,	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,};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 *skb){	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 *skb){	struct inet6_skb_parm *opt = IP6CB(skb);	struct in6_addr *addr = NULL;	struct in6_addr daddr;	struct inet6_dev *idev;	int n, i;	struct ipv6_rt_hdr *hdr;	struct rt0_hdr *rthdr;	int accept_source_route = ipv6_devconf.accept_source_route;	idev = in6_dev_get(skb->dev);	if (idev) {		if (accept_source_route > idev->cnf.accept_source_route)			accept_source_route = idev->cnf.accept_source_route;		in6_dev_put(idev);	}	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||	    !pskb_may_pull(skb, (skb_transport_offset(skb) +				 ((skb_transport_header(skb)[1] + 1) << 3)))) {		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),				 IPSTATS_MIB_INHDRERRORS);		kfree_skb(skb);		return -1;	}	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||	    skb->pkt_type != PACKET_HOST) {		IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),				 IPSTATS_MIB_INADDRERRORS);		kfree_skb(skb);		return -1;	}looped_back:	if (hdr->segments_left == 0) {		switch (hdr->type) {#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)		case IPV6_SRCRT_TYPE_2:			/* Silently discard type 2 header unless it was			 * processed by own			 */			if (!addr) {				IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),						 IPSTATS_MIB_INADDRERRORS);				kfree_skb(skb);				return -1;			}			break;#endif		default:			break;		}		opt->lastopt = opt->srcrt = skb_network_header_len(skb);		skb->transport_header += (hdr->hdrlen + 1) << 3;		opt->dst0 = opt->dst1;		opt->dst1 = 0;		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);		return 1;	}	switch (hdr->type) {#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)	case IPV6_SRCRT_TYPE_2:		if (accept_source_route < 0)			goto unknown_rh;		/* Silently discard invalid RTH type 2 */		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {			IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),					 IPSTATS_MIB_INHDRERRORS);			kfree_skb(skb);			return -1;		}		break;#endif	default:		goto unknown_rh;	}	/*	 *	This is the routing header forwarding algorithm from	 *	RFC 2460, page 16.

⌨️ 快捷键说明

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