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

📄 ndisc.c

📁 linux 内核源代码
💻 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: * *	Pierre Ynard			:	export userland ND options *						through netlink (RDNSS support) *	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/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_addr.h>#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/netlink.h>#include <linux/rtnetlink.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[__ND_OPT_ARRAY_MAX];#ifdef CONFIG_IPV6_ROUTE_INFO	struct nd_opt_hdr *nd_opts_ri;	struct nd_opt_hdr *nd_opts_ri_end;#endif	struct nd_opt_hdr *nd_useropts;	struct nd_opt_hdr *nd_useropts_end;};#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_array[__ND_OPT_PREFIX_INFO_END]#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)/* * Return the padding between the option length and the start of the * link addr.  Currently only IP-over-InfiniBand needs this, although * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may * also need a pad of 2. */static int ndisc_addr_option_pad(unsigned short type){	switch (type) {	case ARPHRD_INFINIBAND: return 2;	default:                return 0;	}}static inline int ndisc_opt_addr_space(struct net_device *dev){	return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));}static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,				  unsigned short addr_type){	int space = NDISC_OPT_SPACE(data_len);	int pad   = ndisc_addr_option_pad(addr_type);	opt[0] = type;	opt[1] = space>>3;	memset(opt + 2, 0, pad);	opt   += pad;	space -= pad;	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 inline int ndisc_is_useropt(struct nd_opt_hdr *opt){	return (opt->nd_opt_type == ND_OPT_RDNSS);}static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,					     struct nd_opt_hdr *end){	if (!cur || !end || cur >= end)		return NULL;	do {		cur = ((void *)cur) + (cur->nd_opt_len << 3);	} while(cur < end && !ndisc_is_useropt(cur));	return (cur <= end && ndisc_is_useropt(cur) ? 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])				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;			break;#ifdef CONFIG_IPV6_ROUTE_INFO		case ND_OPT_ROUTE_INFO:			ndopts->nd_opts_ri_end = nd_opt;			if (!ndopts->nd_opts_ri)				ndopts->nd_opts_ri = nd_opt;			break;#endif		default:			if (ndisc_is_useropt(nd_opt)) {				ndopts->nd_useropts_end = nd_opt;				if (!ndopts->nd_useropts)					ndopts->nd_useropts = nd_opt;			} else {				/*				 * 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;}static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,				      struct net_device *dev){	u8 *lladdr = (u8 *)(p + 1);	int lladdrlen = p->nd_opt_len << 3;	int prepad = ndisc_addr_option_pad(dev->type);	if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad))		return NULL;	return (lladdr + prepad);}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;	case ARPHRD_INFINIBAND:		ipv6_ib_mc_map(addr, buf);		return 0;	default:		if (dir) {			memcpy(buf, dev->broadcast, dev->addr_len);			return 0;		}	}	return -EINVAL;}EXPORT_SYMBOL(ndisc_mc_map);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->header_ops) {		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->header_ops->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,			    int oif){	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;	fl->oif			= oif;	security_sk_classify_flow(ndisc_socket->sk, fl);}static void __ndisc_send(struct net_device *dev,			 struct neighbour *neigh,			 struct in6_addr *daddr, struct in6_addr *saddr,			 struct icmp6hdr *icmp6h, struct in6_addr *target,			 int llinfo){	struct flowi fl;	struct dst_entry *dst;	struct sock *sk = ndisc_socket->sk;	struct sk_buff *skb;	struct icmp6hdr *hdr;	struct inet6_dev *idev;	int len;	int err;	u8 *opt, type;	type = icmp6h->icmp6_type;	ndisc_flow_init(&fl, type, saddr, daddr,			dev->ifindex);	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);	if (!dst)		return;	err = xfrm_lookup(&dst, &fl, NULL, 0);	if (err < 0)		return;	if (!dev->addr_len)		llinfo = 0;	len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);	if (llinfo)		len += ndisc_opt_addr_space(dev);	skb = sock_alloc_send_skb(sk,				  (MAX_HEADER + sizeof(struct ipv6hdr) +				   len + LL_RESERVED_SPACE(dev)),				  1, &err);	if (!skb) {		ND_PRINTK0(KERN_ERR			   "ICMPv6 ND: %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, saddr, daddr, IPPROTO_ICMPV6, len);	skb->transport_header = skb->tail;	skb_put(skb, len);	hdr = (struct icmp6hdr *)skb_transport_header(skb);	memcpy(hdr, icmp6h, sizeof(*hdr));	opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);	if (target) {		ipv6_addr_copy((struct in6_addr *)opt, target);		opt += sizeof(*target);	}	if (llinfo)		ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,				       dev->addr_len, dev->type);	hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,					   IPPROTO_ICMPV6,					   csum_partial((__u8 *) hdr,							len, 0));	skb->dst = dst;	idev = in6_dev_get(dst->dev);	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);	if (!err) {		ICMP6MSGOUT_INC_STATS(idev, type);		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);	}	if (likely(idev != NULL))		in6_dev_put(idev);}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 in6_addr *src_addr;	struct icmp6hdr icmp6h = {		.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,	};	/* for anycast or proxy, solicited_addr != src_addr */	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);	if (ifp) {		src_addr = solicited_addr;		if (ifp->flags & IFA_F_OPTIMISTIC)			override = 0;		in6_ifa_put(ifp);	} else {		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))			return;		src_addr = &tmpaddr;	}	icmp6h.icmp6_router = router;	icmp6h.icmp6_solicited = solicited;	icmp6h.icmp6_override = override;	__ndisc_send(dev, neigh, daddr, src_addr,		     &icmp6h, solicited_addr,		     inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);}void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,		   struct in6_addr *solicit,		   struct in6_addr *daddr, struct in6_addr *saddr){	struct in6_addr addr_buf;	struct icmp6hdr icmp6h = {		.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,	};	if (saddr == NULL) {		if (ipv6_get_lladdr(dev, &addr_buf,				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))			return;		saddr = &addr_buf;	}

⌨️ 快捷键说明

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