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

📄 ndisc.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *	Neighbour Discovery for IPv6 *	Linux INET6 implementation  * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt>	 *	Mike Shaver		<shaver@ingenia.com> * *	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: * *	Lars Fenneberg			:	fixed MTU setting on receipt *						of an RA. * *	Janos Farkas			:	kmalloc failure checks *	Alexey Kuznetsov		:	state machine reworked *						and moved to net/core. *	Pekka Savola			:	RFC2461 validation *	YOSHIFUJI Hideaki @USAGI	:	Verify ND options properly *//* Set to 3 to get tracing... */#define ND_DEBUG 1#define ND_PRINTK(fmt, args...) do { if (net_ratelimit()) { printk(fmt, ## args); } } while(0)#define ND_NOPRINTK(x...) do { ; } while(0)#define ND_PRINTK0 ND_PRINTK#define ND_PRINTK1 ND_NOPRINTK#define ND_PRINTK2 ND_NOPRINTK#define ND_PRINTK3 ND_NOPRINTK#if ND_DEBUG >= 1#undef ND_PRINTK1#define ND_PRINTK1 ND_PRINTK#endif#if ND_DEBUG >= 2#undef ND_PRINTK2#define ND_PRINTK2 ND_PRINTK#endif#if ND_DEBUG >= 3#undef ND_PRINTK3#define ND_PRINTK3 ND_PRINTK#endif#include <linux/module.h>#include <linux/config.h>#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/in6.h>#include <linux/route.h>#include <linux/init.h>#include <linux/rcupdate.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <linux/if_arp.h>#include <linux/ipv6.h>#include <linux/icmpv6.h>#include <linux/jhash.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/ndisc.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/icmp.h>#include <net/flow.h>#include <net/ip6_checksum.h>#include <linux/proc_fs.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv6.h>static struct socket *ndisc_socket;static u32 ndisc_hash(const void *pkey, const struct net_device *dev);static int ndisc_constructor(struct neighbour *neigh);static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);static int pndisc_constructor(struct pneigh_entry *n);static void pndisc_destructor(struct pneigh_entry *n);static void pndisc_redo(struct sk_buff *skb);static struct neigh_ops ndisc_generic_ops = {	.family =		AF_INET6,	.solicit =		ndisc_solicit,	.error_report =		ndisc_error_report,	.output =		neigh_resolve_output,	.connected_output =	neigh_connected_output,	.hh_output =		dev_queue_xmit,	.queue_xmit =		dev_queue_xmit,};static struct neigh_ops ndisc_hh_ops = {	.family =		AF_INET6,	.solicit =		ndisc_solicit,	.error_report =		ndisc_error_report,	.output =		neigh_resolve_output,	.connected_output =	neigh_resolve_output,	.hh_output =		dev_queue_xmit,	.queue_xmit =		dev_queue_xmit,};static struct neigh_ops ndisc_direct_ops = {	.family =		AF_INET6,	.output =		dev_queue_xmit,	.connected_output =	dev_queue_xmit,	.hh_output =		dev_queue_xmit,	.queue_xmit =		dev_queue_xmit,};struct neigh_table nd_tbl = {	.family =	AF_INET6,	.entry_size =	sizeof(struct neighbour) + sizeof(struct in6_addr),	.key_len =	sizeof(struct in6_addr),	.hash =		ndisc_hash,	.constructor =	ndisc_constructor,	.pconstructor =	pndisc_constructor,	.pdestructor =	pndisc_destructor,	.proxy_redo =	pndisc_redo,	.id =		"ndisc_cache",	.parms = {		.tbl =			&nd_tbl,		.base_reachable_time =	30 * HZ,		.retrans_time =	 1 * HZ,		.gc_staletime =	60 * HZ,		.reachable_time =		30 * HZ,		.delay_probe_time =	 5 * HZ,		.queue_len =		 3,		.ucast_probes =	 3,		.mcast_probes =	 3,		.anycast_delay =	 1 * HZ,		.proxy_delay =		(8 * HZ) / 10,		.proxy_qlen =		64,	},	.gc_interval =	  30 * HZ,	.gc_thresh1 =	 128,	.gc_thresh2 =	 512,	.gc_thresh3 =	1024,};/* ND options */struct ndisc_options {	struct nd_opt_hdr *nd_opt_array[7];	struct nd_opt_hdr *nd_opt_piend;};#define nd_opts_src_lladdr	nd_opt_array[ND_OPT_SOURCE_LL_ADDR]#define nd_opts_tgt_lladdr	nd_opt_array[ND_OPT_TARGET_LL_ADDR]#define nd_opts_pi		nd_opt_array[ND_OPT_PREFIX_INFO]#define nd_opts_pi_end		nd_opt_piend#define nd_opts_rh		nd_opt_array[ND_OPT_REDIRECT_HDR]#define nd_opts_mtu		nd_opt_array[ND_OPT_MTU]#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)static u8 *ndisc_fill_option(u8 *opt, int type, void *data, int data_len){	int space = NDISC_OPT_SPACE(data_len);	opt[0] = type;	opt[1] = space>>3;	memcpy(opt+2, data, data_len);	data_len += 2;	opt += data_len;	if ((space -= data_len) > 0)		memset(opt, 0, space);	return opt + space;}static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,					    struct nd_opt_hdr *end){	int type;	if (!cur || !end || cur >= end)		return NULL;	type = cur->nd_opt_type;	do {		cur = ((void *)cur) + (cur->nd_opt_len << 3);	} while(cur < end && cur->nd_opt_type != type);	return (cur <= end && cur->nd_opt_type == type ? cur : NULL);}static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,						 struct ndisc_options *ndopts){	struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;	if (!nd_opt || opt_len < 0 || !ndopts)		return NULL;	memset(ndopts, 0, sizeof(*ndopts));	while (opt_len) {		int l;		if (opt_len < sizeof(struct nd_opt_hdr))			return NULL;		l = nd_opt->nd_opt_len << 3;		if (opt_len < l || l == 0)			return NULL;		switch (nd_opt->nd_opt_type) {		case ND_OPT_SOURCE_LL_ADDR:		case ND_OPT_TARGET_LL_ADDR:		case ND_OPT_MTU:		case ND_OPT_REDIRECT_HDR:			if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {				ND_PRINTK2(KERN_WARNING					   "%s(): duplicated ND6 option found: type=%d\n",					   __FUNCTION__,					   nd_opt->nd_opt_type);			} else {				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;			}			break;		case ND_OPT_PREFIX_INFO:			ndopts->nd_opts_pi_end = nd_opt;			if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0)				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;			break;		default:			/*			 * Unknown options must be silently ignored,			 * to accommodate future extension to the protocol.			 */			ND_PRINTK2(KERN_NOTICE				   "%s(): ignored unsupported option; type=%d, len=%d\n",				   __FUNCTION__,				   nd_opt->nd_opt_type, nd_opt->nd_opt_len);		}		opt_len -= l;		nd_opt = ((void *)nd_opt) + l;	}	return ndopts;}int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir){	switch (dev->type) {	case ARPHRD_ETHER:	case ARPHRD_IEEE802:	/* Not sure. Check it later. --ANK */	case ARPHRD_FDDI:		ipv6_eth_mc_map(addr, buf);		return 0;	case ARPHRD_IEEE802_TR:		ipv6_tr_mc_map(addr,buf);		return 0;	case ARPHRD_ARCNET:		ipv6_arcnet_mc_map(addr, buf);		return 0;	default:		if (dir) {			memcpy(buf, dev->broadcast, dev->addr_len);			return 0;		}	}	return -EINVAL;}static u32 ndisc_hash(const void *pkey, const struct net_device *dev){	const u32 *p32 = pkey;	u32 addr_hash, i;	addr_hash = 0;	for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++)		addr_hash ^= *p32++;	return jhash_2words(addr_hash, dev->ifindex, nd_tbl.hash_rnd);}static int ndisc_constructor(struct neighbour *neigh){	struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;	struct net_device *dev = neigh->dev;	struct inet6_dev *in6_dev;	struct neigh_parms *parms;	int is_multicast = ipv6_addr_is_multicast(addr);	rcu_read_lock();	in6_dev = in6_dev_get(dev);	if (in6_dev == NULL) {		rcu_read_unlock();		return -EINVAL;	}	parms = in6_dev->nd_parms;	__neigh_parms_put(neigh->parms);	neigh->parms = neigh_parms_clone(parms);	rcu_read_unlock();	neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;	if (dev->hard_header == NULL) {		neigh->nud_state = NUD_NOARP;		neigh->ops = &ndisc_direct_ops;		neigh->output = neigh->ops->queue_xmit;	} else {		if (is_multicast) {			neigh->nud_state = NUD_NOARP;			ndisc_mc_map(addr, neigh->ha, dev, 1);		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {			neigh->nud_state = NUD_NOARP;			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);			if (dev->flags&IFF_LOOPBACK)				neigh->type = RTN_LOCAL;		} else if (dev->flags&IFF_POINTOPOINT) {			neigh->nud_state = NUD_NOARP;			memcpy(neigh->ha, dev->broadcast, dev->addr_len);		}		if (dev->hard_header_cache)			neigh->ops = &ndisc_hh_ops;		else			neigh->ops = &ndisc_generic_ops;		if (neigh->nud_state&NUD_VALID)			neigh->output = neigh->ops->connected_output;		else			neigh->output = neigh->ops->output;	}	in6_dev_put(in6_dev);	return 0;}static int pndisc_constructor(struct pneigh_entry *n){	struct in6_addr *addr = (struct in6_addr*)&n->key;	struct in6_addr maddr;	struct net_device *dev = n->dev;	if (dev == NULL || __in6_dev_get(dev) == NULL)		return -EINVAL;	addrconf_addr_solict_mult(addr, &maddr);	ipv6_dev_mc_inc(dev, &maddr);	return 0;}static void pndisc_destructor(struct pneigh_entry *n){	struct in6_addr *addr = (struct in6_addr*)&n->key;	struct in6_addr maddr;	struct net_device *dev = n->dev;	if (dev == NULL || __in6_dev_get(dev) == NULL)		return;	addrconf_addr_solict_mult(addr, &maddr);	ipv6_dev_mc_dec(dev, &maddr);}/* *	Send a Neighbour Advertisement */static inline void ndisc_flow_init(struct flowi *fl, u8 type,			    struct in6_addr *saddr, struct in6_addr *daddr){	memset(fl, 0, sizeof(*fl));	ipv6_addr_copy(&fl->fl6_src, saddr);	ipv6_addr_copy(&fl->fl6_dst, daddr);	fl->proto	 	= IPPROTO_ICMPV6;	fl->fl_icmp_type	= type;	fl->fl_icmp_code	= 0;}static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,		   struct in6_addr *daddr, struct in6_addr *solicited_addr,	 	   int router, int solicited, int override, int inc_opt) {	struct in6_addr tmpaddr;	struct inet6_ifaddr *ifp;	struct inet6_dev *idev;	struct flowi fl;	struct dst_entry* dst;        struct sock *sk = ndisc_socket->sk;	struct in6_addr *src_addr;        struct nd_msg *msg;        int len;        struct sk_buff *skb;	int err;	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);	/* for anycast or proxy, solicited_addr != src_addr */	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1); 	if (ifp) {		src_addr = solicited_addr;		in6_ifa_put(ifp);	} else {		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, 0))			return;		src_addr = &tmpaddr;	}	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr);	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);	if (!dst)		return;	err = xfrm_lookup(&dst, &fl, NULL, 0);	if (err < 0) {		dst_release(dst);		return;	}	if (inc_opt) {		if (dev->addr_len)			len += NDISC_OPT_SPACE(dev->addr_len);		else			inc_opt = 0;	}	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev),				  1, &err);	if (skb == NULL) {		ND_PRINTK0(KERN_ERR			   "ICMPv6 NA: %s() failed to allocate an skb.\n", 			   __FUNCTION__);		dst_release(dst);		return;	}	skb_reserve(skb, LL_RESERVED_SPACE(dev));	ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len);	msg = (struct nd_msg *)skb_put(skb, len);	skb->h.raw = (unsigned char*)msg;        msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;        msg->icmph.icmp6_code = 0;        msg->icmph.icmp6_cksum = 0;        msg->icmph.icmp6_unused = 0;        msg->icmph.icmp6_router    = router;        msg->icmph.icmp6_solicited = solicited;        msg->icmph.icmp6_override  = !!override;        /* Set the target address. */	ipv6_addr_copy(&msg->target, solicited_addr);	if (inc_opt)		ndisc_fill_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);	/* checksum */	msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len, 						 IPPROTO_ICMPV6,						 csum_partial((__u8 *) msg, 							      len, 0));	skb->dst = dst;	idev = in6_dev_get(dst->dev);	IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);	if (!err) {		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);	}	if (likely(idev != NULL))		in6_dev_put(idev);}        void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,		   struct in6_addr *solicit,		   struct in6_addr *daddr, struct in6_addr *saddr) {	struct flowi fl;	struct dst_entry* dst;	struct inet6_dev *idev;        struct sock *sk = ndisc_socket->sk;        struct sk_buff *skb;        struct nd_msg *msg;	struct in6_addr addr_buf;        int len;	int err;	int send_llinfo;	if (saddr == NULL) {		if (ipv6_get_lladdr(dev, &addr_buf))			return;		saddr = &addr_buf;	}	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);	if (!dst)		return;

⌨️ 快捷键说明

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