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

📄 ndisc.c

📁 嵌入式系统设计与实验教材二源码linux内核移植与编译
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	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 *//* Set to 3 to get tracing... */#define ND_DEBUG 1#define ND_PRINTK(x...) printk(KERN_DEBUG x)#define ND_NOPRINTK(x...) do { ; } while(0)#define ND_PRINTK0 ND_PRINTK#define ND_PRINTK1 ND_NOPRINTK#define ND_PRINTK2 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#define __NO_VERSION__#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>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <linux/if_arp.h>#include <linux/ipv6.h>#include <linux/icmpv6.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/checksum.h>#include <linux/proc_fs.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 ={	AF_INET6,	NULL,	ndisc_solicit,	ndisc_error_report,	neigh_resolve_output,	neigh_connected_output,	dev_queue_xmit,	dev_queue_xmit};static struct neigh_ops ndisc_hh_ops ={	AF_INET6,	NULL,	ndisc_solicit,	ndisc_error_report,	neigh_resolve_output,	neigh_resolve_output,	dev_queue_xmit,	dev_queue_xmit};static struct neigh_ops ndisc_direct_ops ={	AF_INET6,	NULL,	NULL,	NULL,	dev_queue_xmit,	dev_queue_xmit,	dev_queue_xmit,	dev_queue_xmit};struct neigh_table nd_tbl ={	NULL,	AF_INET6,	sizeof(struct neighbour) + sizeof(struct in6_addr),	sizeof(struct in6_addr),	ndisc_hash,	ndisc_constructor,	pndisc_constructor,	pndisc_destructor,	pndisc_redo,	"ndisc_cache",        { NULL, NULL, &nd_tbl, 0, NULL, NULL,		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 0 },	30*HZ, 128, 512, 1024,};#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;}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;	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){	u32 hash_val;	hash_val = *(u32*)(pkey + sizeof(struct in6_addr) - 4);	hash_val ^= (hash_val>>16);	hash_val ^= hash_val>>8;	hash_val ^= hash_val>>3;	hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;	return hash_val;}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 = in6_dev_get(dev);	int addr_type;	if (in6_dev == NULL)		return -EINVAL;	addr_type = ipv6_addr_type(addr);	if (in6_dev->nd_parms)		neigh->parms = in6_dev->nd_parms;	if (addr_type&IPV6_ADDR_MULTICAST)		neigh->type = RTN_MULTICAST;	else		neigh->type = 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 (addr_type&IPV6_ADDR_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);}static intndisc_build_ll_hdr(struct sk_buff *skb, struct net_device *dev,		   struct in6_addr *daddr, struct neighbour *neigh, int len){	unsigned char ha[MAX_ADDR_LEN];	unsigned char *h_dest = NULL;	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);	if (dev->hard_header) {		if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {			ndisc_mc_map(daddr, ha, dev, 1);			h_dest = ha;		} else if (neigh) {			read_lock_bh(&neigh->lock);			if (neigh->nud_state&NUD_VALID) {				memcpy(ha, neigh->ha, dev->addr_len);				h_dest = ha;			}			read_unlock_bh(&neigh->lock);		} else {			neigh = neigh_lookup(&nd_tbl, daddr, dev);			if (neigh) {				read_lock_bh(&neigh->lock);				if (neigh->nud_state&NUD_VALID) {					memcpy(ha, neigh->ha, dev->addr_len);					h_dest = ha;				}				read_unlock_bh(&neigh->lock);				neigh_release(neigh);			}		}		if (dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL, len) < 0)			return 0;	}	return 1;}/* *	Send a Neighbour Advertisement */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 sock *sk = ndisc_socket->sk;        struct nd_msg *msg;        int len;        struct sk_buff *skb;	int err;	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);	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 + dev->hard_header_len + 15,				  0, &err);	if (skb == NULL) {		ND_PRINTK1("send_na: alloc skb failed\n");		return;	}	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {		kfree_skb(skb);		return;	}	ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);	msg = (struct nd_msg *) skb_put(skb, len);        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((void*)&msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);	/* checksum */	msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, 						 IPPROTO_ICMPV6,						 csum_partial((__u8 *) msg, 							      len, 0));	dev_queue_xmit(skb);	ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);	ICMP6_INC_STATS(Icmp6OutMsgs);}        void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,		   struct in6_addr *solicit,		   struct in6_addr *daddr, struct in6_addr *saddr) {        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;	}	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);	send_llinfo = dev->addr_len && ipv6_addr_type(saddr) != IPV6_ADDR_ANY;	if (send_llinfo)		len += NDISC_OPT_SPACE(dev->addr_len);	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,				  0, &err);	if (skb == NULL) {		ND_PRINTK1("send_ns: alloc skb failed\n");		return;	}	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {		kfree_skb(skb);		return;	}	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);	msg = (struct nd_msg *)skb_put(skb, len);	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;	msg->icmph.icmp6_code = 0;	msg->icmph.icmp6_cksum = 0;	msg->icmph.icmp6_unused = 0;	/* Set the target address. */	ipv6_addr_copy(&msg->target, solicit);	if (send_llinfo)		ndisc_fill_option((void*)&msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);	/* checksum */	msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,						 daddr, len, 						 IPPROTO_ICMPV6,						 csum_partial((__u8 *) msg, 							      len, 0));	/* send it! */	dev_queue_xmit(skb);	ICMP6_INC_STATS(Icmp6OutNeighborSolicits);	ICMP6_INC_STATS(Icmp6OutMsgs);}void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,		   struct in6_addr *daddr){	struct sock *sk = ndisc_socket->sk;        struct sk_buff *skb;        struct icmp6hdr *hdr;	__u8 * opt;        int len;	int err;	len = sizeof(struct icmp6hdr);	if (dev->addr_len)		len += NDISC_OPT_SPACE(dev->addr_len);        skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,				  0, &err);	if (skb == NULL) {		ND_PRINTK1("send_ns: alloc skb failed\n");		return;	}	if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {		kfree_skb(skb);		return;	}	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);        hdr = (struct icmp6hdr *) skb_put(skb, len);        hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;        hdr->icmp6_code = 0;        hdr->icmp6_cksum = 0;        hdr->icmp6_unused = 0;	opt = (u8*) (hdr + 1);	if (dev->addr_len)		ndisc_fill_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);	/* checksum */	hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,					   IPPROTO_ICMPV6,					   csum_partial((__u8 *) hdr, len, 0));	/* send it! */	dev_queue_xmit(skb);	ICMP6_INC_STATS(Icmp6OutRouterSolicits);	ICMP6_INC_STATS(Icmp6OutMsgs);}		   static u8 * ndisc_find_option(u8 *opt, int opt_len, int len, int option){	while (opt_len <= len) {		int l = opt[1]<<3;		if (opt[0] == option && l >= opt_len)			return opt + 2;		if (l == 0) {			if (net_ratelimit())			    printk(KERN_WARNING "ndisc: option has 0 len\n");			return NULL;		}		opt += l;		len -= l;	}	return NULL;}static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb){	/*	 *	"The sender MUST return an ICMP	 *	 destination unreachable"	 */	dst_link_failure(skb);	kfree_skb(skb);}/* Called with locked neigh: either read or both */static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb){	struct in6_addr *saddr = NULL;	struct in6_addr mcaddr;	struct net_device *dev = neigh->dev;	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;	int probes = atomic_read(&neigh->probes);	if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev))		saddr = &skb->nh.ipv6h->saddr;	if ((probes -= neigh->parms->ucast_probes) < 0) {		if (!(neigh->nud_state&NUD_VALID))			ND_PRINTK1("trying to ucast probe in NUD_INVALID\n");		ndisc_send_ns(dev, neigh, target, target, saddr);	} else if ((probes -= neigh->parms->app_probes) < 0) {#ifdef CONFIG_ARPD		neigh_app_ns(neigh);#endif	} else {		addrconf_addr_solict_mult(target, &mcaddr);		ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);	}}static void ndisc_update(struct neighbour *neigh, u8* opt, int len, int type){	opt = ndisc_find_option(opt, neigh->dev->addr_len+2, len, type);	neigh_update(neigh, opt, NUD_STALE, 1, 1);}static void ndisc_router_discovery(struct sk_buff *skb){        struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;	struct neighbour *neigh;	struct inet6_dev *in6_dev;	struct rt6_info *rt;	int lifetime;	int optlen;	__u8 * opt = (__u8 *)(ra_msg + 1);	optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {		if (net_ratelimit())			printk(KERN_WARNING "ICMP RA: source address is not linklocal\n");		return;	}	/*	 *	set the RA_RECV flag in the interface	 */	in6_dev = in6_dev_get(skb->dev);	if (in6_dev == NULL) {		ND_PRINTK1("RA: can't find in6 device\n");		return;	}	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {		in6_dev_put(in6_dev);		return;	}	if (in6_dev->if_flags & IF_RS_SENT) {		/*		 *	flag that an RA was received after an RS was sent		 *	out on this interface.		 */		in6_dev->if_flags |= IF_RA_RCVD;	}	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);	if (rt && lifetime == 0) {		ip6_del_rt(rt);		rt = NULL;	}	if (rt == NULL && lifetime) {		ND_PRINTK2("ndisc_rdisc: adding default router\n");		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);		if (rt == NULL) {			ND_PRINTK1("route_add failed\n");			in6_dev_put(in6_dev);			return;		}		neigh = rt->rt6i_nexthop;		if (neigh == NULL) {			ND_PRINTK1("nd: add default router: null neighbour\n");			dst_release(&rt->u.dst);			in6_dev_put(in6_dev);			return;		}		neigh->flags |= NTF_ROUTER;		/*		 *	If we where using an "all destinations on link" route		 *	delete it		 */		rt6_purge_dflt_routers(RTF_ALLONLINK);	}	if (rt)		rt->rt6i_expires = jiffies + (HZ * lifetime);

⌨️ 快捷键说明

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