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

📄 addrconf.c

📁 ipv6地址转换器
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *	IPv6 Address [auto]configuration *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt>	 * *	$Id: addrconf.c,v 1.48.2.1 1999/06/28 10:39:39 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: * *	Janos Farkas			:	delete timer on ifdown *	<chexum@bankinf.banki.hu> *	Andi Kleen			:	kill doube kfree on module *						unload. */#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/netdevice.h>#include <linux/if_arp.h>#include <linux/route.h>#include <linux/inetdevice.h>#include <linux/init.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <linux/delay.h>#include <linux/proc_fs.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/ip.h>#include <linux/if_tunnel.h>#include <linux/rtnetlink.h>#include <asm/uaccess.h>/* Set to 3 to get tracing... */#define ACONF_DEBUG 2#if ACONF_DEBUG >= 3#define ADBG(x) printk x#else#define ADBG(x)#endif#ifdef CONFIG_SYSCTLstatic void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);static void addrconf_sysctl_unregister(struct ipv6_devconf *p);#endif/* *	Configured unicast address list */static struct inet6_ifaddr		*inet6_addr_lst[IN6_ADDR_HSIZE];/* *	AF_INET6 device list */static struct inet6_dev		*inet6_dev_lst[IN6_ADDR_HSIZE];static atomic_t			addr_list_lock = ATOMIC_INIT(0);void addrconf_verify(unsigned long);static struct timer_list addr_chk_timer = {	NULL, NULL,	0, 0, addrconf_verify};/* These locks protect only against address deletions,   but not against address adds or status updates.   It is OK. The only race is when address is selected,   which becomes invalid immediately after selection.   It is harmless, because this address could be already invalid   several usecs ago.   Its important, that:   1. The result of inet6_add_addr() is used only inside lock      or from bh_atomic context.   2. inet6_get_lladdr() is used only from bh protected context.   3. The result of ipv6_chk_addr() is not used outside of bh protected context. */static __inline__ void addrconf_lock(void){	atomic_inc(&addr_list_lock);	synchronize_bh();}static __inline__ void addrconf_unlock(void){	atomic_dec(&addr_list_lock);}static int addrconf_ifdown(struct device *dev, int how);static void addrconf_dad_start(struct inet6_ifaddr *ifp);static void addrconf_dad_timer(unsigned long data);static void addrconf_dad_completed(struct inet6_ifaddr *ifp);static void addrconf_rs_timer(unsigned long data);static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);struct ipv6_devconf ipv6_devconf ={	0,				/* forwarding		*/	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/	IPV6_MIN_MTU,			/* mtu			*/	1,				/* accept RAs		*/	1,				/* accept redirects	*/	1,				/* autoconfiguration	*/	1,				/* dad transmits	*/	MAX_RTR_SOLICITATIONS,		/* router solicits	*/	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/};static struct ipv6_devconf ipv6_devconf_dflt ={	0,				/* forwarding		*/	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/	IPV6_MIN_MTU,			/* mtu			*/	1,				/* accept RAs		*/	1,				/* accept redirects	*/	1,				/* autoconfiguration	*/	1,				/* dad transmits	*/	MAX_RTR_SOLICITATIONS,		/* router solicits	*/	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/};int ipv6_addr_type(struct in6_addr *addr){	u32 st;	st = addr->s6_addr32[0];	/* Consider all addresses with the first three bits different of	   000 and 111 as unicasts.	 */	if ((st & __constant_htonl(0xE0000000)) != __constant_htonl(0x00000000) &&	    (st & __constant_htonl(0xE0000000)) != __constant_htonl(0xE0000000))		return IPV6_ADDR_UNICAST;	if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {		int type = IPV6_ADDR_MULTICAST;		switch((st & __constant_htonl(0x00FF0000))) {			case __constant_htonl(0x00010000):				type |= IPV6_ADDR_LOOPBACK;				break;			case __constant_htonl(0x00020000):				type |= IPV6_ADDR_LINKLOCAL;				break;			case __constant_htonl(0x00050000):				type |= IPV6_ADDR_SITELOCAL;				break;		};		return type;	}		if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))		return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);	if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))		return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);	if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {		if (addr->s6_addr32[2] == 0) {			if (addr->in6_u.u6_addr32[3] == 0)				return IPV6_ADDR_ANY;			if (addr->s6_addr32[3] == __constant_htonl(0x00000001))				return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);			return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);		}		if (addr->s6_addr32[2] == __constant_htonl(0x0000ffff))			return IPV6_ADDR_MAPPED;	}	return IPV6_ADDR_RESERVED;}static struct inet6_dev * ipv6_add_dev(struct device *dev){	struct inet6_dev *ndev, **bptr, *iter;	int hash;	if (dev->mtu < IPV6_MIN_MTU)		return NULL;	ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL);	if (ndev) {		memset(ndev, 0, sizeof(struct inet6_dev));		ndev->dev = dev;		memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));		ndev->cnf.mtu6 = dev->mtu;		ndev->cnf.sysctl = NULL;		ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);		if (ndev->nd_parms == NULL) {			kfree(ndev);			return NULL;		}#ifdef CONFIG_SYSCTL		neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");		addrconf_sysctl_register(ndev, &ndev->cnf);#endif		hash = ipv6_devindex_hash(dev->ifindex);		bptr = &inet6_dev_lst[hash];		iter = *bptr;		for (; iter; iter = iter->next)			bptr = &iter->next;		*bptr = ndev;	}	return ndev;}static struct inet6_dev * ipv6_find_idev(struct device *dev){	struct inet6_dev *idev;	if ((idev = ipv6_get_idev(dev)) == NULL) {		idev = ipv6_add_dev(dev);		if (idev == NULL)			return NULL;		if (dev->flags&IFF_UP)			ipv6_mc_up(idev);	}	return idev;}static void addrconf_forward_change(struct inet6_dev *idev){	int i;	if (idev)		return;	for (i = 0; i < IN6_ADDR_HSIZE; i++) {		for (idev = inet6_dev_lst[i]; idev; idev = idev->next)			idev->cnf.forwarding = ipv6_devconf.forwarding;	}}struct inet6_dev * ipv6_get_idev(struct device *dev){	struct inet6_dev *idev;	int hash;	hash = ipv6_devindex_hash(dev->ifindex);	for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {		if (idev->dev == dev)			return idev;	}	return NULL;}static struct inet6_ifaddr *ipv6_add_addr(struct inet6_dev *idev, struct in6_addr *addr, int scope){	struct inet6_ifaddr *ifa;	int hash;	ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);	if (ifa == NULL) {		ADBG(("ipv6_add_addr: malloc failed\n"));		return NULL;	}	memset(ifa, 0, sizeof(struct inet6_ifaddr));	memcpy(&ifa->addr, addr, sizeof(struct in6_addr));	init_timer(&ifa->timer);	ifa->timer.data = (unsigned long) ifa;	ifa->scope = scope;	ifa->idev = idev;	/* Add to list. */	hash = ipv6_addr_hash(addr);	ifa->lst_next = inet6_addr_lst[hash];	inet6_addr_lst[hash] = ifa;	/* Add to inet6_dev unicast addr list. */	ifa->if_next = idev->addr_list;	idev->addr_list = ifa;	return ifa;}static void ipv6_del_addr(struct inet6_ifaddr *ifp){	struct inet6_ifaddr *iter, **back;	int hash;	if (atomic_read(&addr_list_lock)) {		ifp->flags |= ADDR_INVALID;		ipv6_ifa_notify(RTM_DELADDR, ifp);		return;	}	hash = ipv6_addr_hash(&ifp->addr);	iter = inet6_addr_lst[hash];	back = &inet6_addr_lst[hash];	for (; iter; iter = iter->lst_next) {		if (iter == ifp) {			*back = ifp->lst_next;			synchronize_bh();			ifp->lst_next = NULL;			break;		}		back = &(iter->lst_next);	}	iter = ifp->idev->addr_list;	back = &ifp->idev->addr_list;	for (; iter; iter = iter->if_next) {		if (iter == ifp) {			*back = ifp->if_next;			synchronize_bh();			ifp->if_next = NULL;			break;		}		back = &(iter->if_next);	}	ipv6_ifa_notify(RTM_DELADDR, ifp);		kfree(ifp);}/* *	Choose an apropriate source address *	should do: *	i)	get an address with an apropriate scope *	ii)	see if there is a specific route for the destination and use *		an address of the attached interface  *	iii)	don't use deprecated addresses */int ipv6_get_saddr(struct dst_entry *dst,		   struct in6_addr *daddr, struct in6_addr *saddr){	int scope;	struct inet6_ifaddr *ifp = NULL;	struct inet6_ifaddr *match = NULL;	struct device *dev = NULL;	struct rt6_info *rt;	int err;	int i;	rt = (struct rt6_info *) dst;	if (rt)		dev = rt->rt6i_dev;		addrconf_lock();	scope = ipv6_addr_scope(daddr);	if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {		/*		 *	route for the "all destinations on link" rule		 *	when no routers are present		 */		scope = IFA_LINK;	}	/*	 *	known dev	 *	search dev and walk through dev addresses	 */	if (dev) {		struct inet6_dev *idev;		int hash;		if (dev->flags & IFF_LOOPBACK)			scope = IFA_HOST;		hash = ipv6_devindex_hash(dev->ifindex);		for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {			if (idev->dev == dev) {				for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {					if (ifp->scope == scope) {						if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))							goto out;						if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))							match = ifp;					}				}				break;			}		}	}	if (scope == IFA_LINK)		goto out;	/*	 *	dev == NULL or search failed for specified dev	 */	for (i=0; i < IN6_ADDR_HSIZE; i++) {		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {			if (ifp->scope == scope) {				if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))					goto out;				if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))					match = ifp;			}		}	}out:	if (ifp == NULL)		ifp = match;	err = -ENETUNREACH;	if (ifp) {		memcpy(saddr, &ifp->addr, sizeof(struct in6_addr));		err = 0;	}	addrconf_unlock();	return err;}struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev){	struct inet6_ifaddr *ifp = NULL;	struct inet6_dev *idev;	if ((idev = ipv6_get_idev(dev)) != NULL) {		addrconf_lock();		for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {			if (ifp->scope == IFA_LINK)				break;		}		addrconf_unlock();	}	return ifp;}/* *	Retrieve the ifaddr struct from an v6 address *	Called from ipv6_rcv to check if the address belongs  *	to the host. */struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr, struct device *dev, int nd){	struct inet6_ifaddr * ifp;	u8 hash;	unsigned flags = 0;	if (!nd)		flags |= DAD_STATUS|ADDR_INVALID;	addrconf_lock();	hash = ipv6_addr_hash(addr);	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {		if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && !(ifp->flags&flags)) {			if (dev == NULL || ifp->idev->dev == dev ||			    !(ifp->scope&(IFA_LINK|IFA_HOST)))				break;		}	}	addrconf_unlock();	return ifp;}void addrconf_dad_failure(struct inet6_ifaddr *ifp){	printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);	del_timer(&ifp->timer);	ipv6_del_addr(ifp);}/* Join to solicited addr multicast group. */static void addrconf_join_solict(struct device *dev, struct in6_addr *addr){	struct in6_addr maddr;	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))		return;#ifndef CONFIG_IPV6_NO_PB	addrconf_addr_solict_mult_old(addr, &maddr);	ipv6_dev_mc_inc(dev, &maddr);#endif#ifdef CONFIG_IPV6_EUI64	addrconf_addr_solict_mult_new(addr, &maddr);	ipv6_dev_mc_inc(dev, &maddr);#endif}static void addrconf_leave_solict(struct device *dev, struct in6_addr *addr){	struct in6_addr maddr;	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))		return;#ifndef CONFIG_IPV6_NO_PB	addrconf_addr_solict_mult_old(addr, &maddr);	ipv6_dev_mc_dec(dev, &maddr);#endif#ifdef CONFIG_IPV6_EUI64	addrconf_addr_solict_mult_new(addr, &maddr);	ipv6_dev_mc_dec(dev, &maddr);#endif}#ifdef CONFIG_IPV6_EUI64static int ipv6_generate_eui64(u8 *eui, struct device *dev){	switch (dev->type) {	case ARPHRD_ETHER:		if (dev->addr_len != ETH_ALEN)			return -1;		memcpy(eui, dev->dev_addr, 3);		memcpy(eui + 5, dev->dev_addr+3, 3);		eui[3] = 0xFF;		eui[4] = 0xFE;		eui[0] ^= 2;		return 0;	}	return -1;}static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev){	int err = -1;	struct inet6_ifaddr *ifp;	for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {		if (ifp->scope == IFA_LINK && !(ifp->flags&(ADDR_STATUS|DAD_STATUS))) {			memcpy(eui, ifp->addr.s6_addr+8, 8);			err = 0;			break;		}	}	return err;}#endif/* *	Add prefix route. */static voidaddrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev,		      unsigned long expires, unsigned flags){	struct in6_rtmsg rtmsg;	memset(&rtmsg, 0, sizeof(rtmsg));	memcpy(&rtmsg.rtmsg_dst, pfx, sizeof(struct in6_addr));	rtmsg.rtmsg_dst_len = plen;	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;	rtmsg.rtmsg_ifindex = dev->ifindex;	rtmsg.rtmsg_info = expires;	rtmsg.rtmsg_flags = RTF_UP|flags;	rtmsg.rtmsg_type = RTMSG_NEWROUTE;	/* Prevent useless cloning on PtP SIT.	   This thing is done here expecting that the whole	   class of non-broadcast devices need not cloning.	 */	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;	ip6_route_add(&rtmsg);}/* Create "default" multicast route to the interface */static void addrconf_add_mroute(struct device *dev){	struct in6_rtmsg rtmsg;	memset(&rtmsg, 0, sizeof(rtmsg));	ipv6_addr_set(&rtmsg.rtmsg_dst,		      __constant_htonl(0xFF000000), 0, 0, 0);	rtmsg.rtmsg_dst_len = 8;	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;	rtmsg.rtmsg_ifindex = dev->ifindex;	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;	rtmsg.rtmsg_type = RTMSG_NEWROUTE;	ip6_route_add(&rtmsg);}static void sit_route_add(struct device *dev){	struct in6_rtmsg rtmsg;	memset(&rtmsg, 0, sizeof(rtmsg));	rtmsg.rtmsg_type	= RTMSG_NEWROUTE;	rtmsg.rtmsg_metric	= IP6_RT_PRIO_ADDRCONF;	/* prefix length - 96 bytes "::d.d.d.d" */	rtmsg.rtmsg_dst_len	= 96;	rtmsg.rtmsg_flags	= RTF_UP|RTF_NONEXTHOP;	rtmsg.rtmsg_ifindex	= dev->ifindex;	ip6_route_add(&rtmsg);

⌨️ 快捷键说明

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