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

📄 addrconf.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *	IPv6 Address [auto]configuration *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru> * *	$Id: addrconf.c,v 1.69 2001/10/31 21:55:54 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 double kfree on module *						unload. *	Maciej W. Rozycki		:	FDDI support *	sekiya@USAGI			:	Don't send too many RS *						packets. *	yoshfuji@USAGI			:       Fixed interval between DAD *						packets. *	YOSHIFUJI Hideaki @USAGI	:	improved accuracy of *						address validation timer. *	YOSHIFUJI Hideaki @USAGI	:	Privacy Extensions (RFC3041) *						support. *	Yuji SEKIYA @USAGI		:	Don't assign a same IPv6 *						address on a same interface. *	YOSHIFUJI Hideaki @USAGI	:	ARCnet support *	YOSHIFUJI Hideaki @USAGI	:	convert /proc/net/if_inet6 to *						seq_file. *	YOSHIFUJI Hideaki @USAGI	:	improved source address *						selection; consider scope, *						status etc. */#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_addr.h>#include <linux/if_arp.h>#include <linux/if_arcnet.h>#include <linux/if_infiniband.h>#include <linux/route.h>#include <linux/inetdevice.h>#include <linux/init.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <linux/capability.h>#include <linux/delay.h>#include <linux/notifier.h>#include <linux/string.h>#include <net/net_namespace.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/tcp.h>#include <net/ip.h>#include <net/netlink.h>#include <net/pkt_sched.h>#include <linux/if_tunnel.h>#include <linux/rtnetlink.h>#ifdef CONFIG_IPV6_PRIVACY#include <linux/random.h>#endif#include <asm/uaccess.h>#include <asm/unaligned.h>#include <linux/proc_fs.h>#include <linux/seq_file.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#define	INFINITY_LIFE_TIME	0xFFFFFFFF#define TIME_DELTA(a,b) ((unsigned long)((long)(a) - (long)(b)))#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#ifdef CONFIG_IPV6_PRIVACYstatic int __ipv6_regen_rndid(struct inet6_dev *idev);static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);static void ipv6_regen_rndid(unsigned long data);static int desync_factor = MAX_DESYNC_FACTOR * HZ;#endifstatic int ipv6_count_addresses(struct inet6_dev *idev);/* *	Configured unicast address hash table */static struct inet6_ifaddr		*inet6_addr_lst[IN6_ADDR_HSIZE];static DEFINE_RWLOCK(addrconf_hash_lock);static void addrconf_verify(unsigned long);static DEFINE_TIMER(addr_chk_timer, addrconf_verify, 0, 0);static DEFINE_SPINLOCK(addrconf_verify_lock);static void addrconf_join_anycast(struct inet6_ifaddr *ifp);static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);static int addrconf_ifdown(struct net_device *dev, int how);static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags);static void addrconf_dad_timer(unsigned long data);static void addrconf_dad_completed(struct inet6_ifaddr *ifp);static void addrconf_dad_run(struct inet6_dev *idev);static void addrconf_rs_timer(unsigned long data);static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);static void inet6_prefix_notify(int event, struct inet6_dev *idev,				struct prefix_info *pinfo);static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);struct ipv6_devconf ipv6_devconf __read_mostly = {	.forwarding		= 0,	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,	.mtu6			= IPV6_MIN_MTU,	.accept_ra		= 1,	.accept_redirects	= 1,	.autoconf		= 1,	.force_mld_version	= 0,	.dad_transmits		= 1,	.rtr_solicits		= MAX_RTR_SOLICITATIONS,	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,#ifdef CONFIG_IPV6_PRIVACY	.use_tempaddr 		= 0,	.temp_valid_lft		= TEMP_VALID_LIFETIME,	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,	.regen_max_retry	= REGEN_MAX_RETRY,	.max_desync_factor	= MAX_DESYNC_FACTOR,#endif	.max_addresses		= IPV6_MAX_ADDRESSES,	.accept_ra_defrtr	= 1,	.accept_ra_pinfo	= 1,#ifdef CONFIG_IPV6_ROUTER_PREF	.accept_ra_rtr_pref	= 1,	.rtr_probe_interval	= 60 * HZ,#ifdef CONFIG_IPV6_ROUTE_INFO	.accept_ra_rt_info_max_plen = 0,#endif#endif	.proxy_ndp		= 0,	.accept_source_route	= 0,	/* we do not accept RH0 by default. */};static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {	.forwarding		= 0,	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,	.mtu6			= IPV6_MIN_MTU,	.accept_ra		= 1,	.accept_redirects	= 1,	.autoconf		= 1,	.dad_transmits		= 1,	.rtr_solicits		= MAX_RTR_SOLICITATIONS,	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,#ifdef CONFIG_IPV6_PRIVACY	.use_tempaddr		= 0,	.temp_valid_lft		= TEMP_VALID_LIFETIME,	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,	.regen_max_retry	= REGEN_MAX_RETRY,	.max_desync_factor	= MAX_DESYNC_FACTOR,#endif	.max_addresses		= IPV6_MAX_ADDRESSES,	.accept_ra_defrtr	= 1,	.accept_ra_pinfo	= 1,#ifdef CONFIG_IPV6_ROUTER_PREF	.accept_ra_rtr_pref	= 1,	.rtr_probe_interval	= 60 * HZ,#ifdef CONFIG_IPV6_ROUTE_INFO	.accept_ra_rt_info_max_plen = 0,#endif#endif	.proxy_ndp		= 0,	.accept_source_route	= 0,	/* we do not accept RH0 by default. */};/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;/* Check if a valid qdisc is available */static inline int addrconf_qdisc_ok(struct net_device *dev){	return (dev->qdisc != &noop_qdisc);}static void addrconf_del_timer(struct inet6_ifaddr *ifp){	if (del_timer(&ifp->timer))		__in6_ifa_put(ifp);}enum addrconf_timer_t{	AC_NONE,	AC_DAD,	AC_RS,};static void addrconf_mod_timer(struct inet6_ifaddr *ifp,			       enum addrconf_timer_t what,			       unsigned long when){	if (!del_timer(&ifp->timer))		in6_ifa_hold(ifp);	switch (what) {	case AC_DAD:		ifp->timer.function = addrconf_dad_timer;		break;	case AC_RS:		ifp->timer.function = addrconf_rs_timer;		break;	default:;	}	ifp->timer.expires = jiffies + when;	add_timer(&ifp->timer);}static int snmp6_alloc_dev(struct inet6_dev *idev){	if (snmp_mib_init((void **)idev->stats.ipv6,			  sizeof(struct ipstats_mib),			  __alignof__(struct ipstats_mib)) < 0)		goto err_ip;	if (snmp_mib_init((void **)idev->stats.icmpv6,			  sizeof(struct icmpv6_mib),			  __alignof__(struct icmpv6_mib)) < 0)		goto err_icmp;	if (snmp_mib_init((void **)idev->stats.icmpv6msg,			  sizeof(struct icmpv6msg_mib),			  __alignof__(struct icmpv6msg_mib)) < 0)		goto err_icmpmsg;	return 0;err_icmpmsg:	snmp_mib_free((void **)idev->stats.icmpv6);err_icmp:	snmp_mib_free((void **)idev->stats.ipv6);err_ip:	return -ENOMEM;}static void snmp6_free_dev(struct inet6_dev *idev){	snmp_mib_free((void **)idev->stats.icmpv6msg);	snmp_mib_free((void **)idev->stats.icmpv6);	snmp_mib_free((void **)idev->stats.ipv6);}/* Nobody refers to this device, we may destroy it. */static void in6_dev_finish_destroy_rcu(struct rcu_head *head){	struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu);	kfree(idev);}void in6_dev_finish_destroy(struct inet6_dev *idev){	struct net_device *dev = idev->dev;	BUG_TRAP(idev->addr_list==NULL);	BUG_TRAP(idev->mc_list==NULL);#ifdef NET_REFCNT_DEBUG	printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");#endif	dev_put(dev);	if (!idev->dead) {		printk("Freeing alive inet6 device %p\n", idev);		return;	}	snmp6_free_dev(idev);	call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);}EXPORT_SYMBOL(in6_dev_finish_destroy);static struct inet6_dev * ipv6_add_dev(struct net_device *dev){	struct inet6_dev *ndev;	struct in6_addr maddr;	ASSERT_RTNL();	if (dev->mtu < IPV6_MIN_MTU)		return NULL;	ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);	if (ndev == NULL)		return NULL;	rwlock_init(&ndev->lock);	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;	}	/* We refer to the device */	dev_hold(dev);	if (snmp6_alloc_dev(ndev) < 0) {		ADBG((KERN_WARNING			"%s(): cannot allocate memory for statistics; dev=%s.\n",			__FUNCTION__, dev->name));		neigh_parms_release(&nd_tbl, ndev->nd_parms);		ndev->dead = 1;		in6_dev_finish_destroy(ndev);		return NULL;	}	if (snmp6_register_dev(ndev) < 0) {		ADBG((KERN_WARNING			"%s(): cannot create /proc/net/dev_snmp6/%s\n",			__FUNCTION__, dev->name));		neigh_parms_release(&nd_tbl, ndev->nd_parms);		ndev->dead = 1;		in6_dev_finish_destroy(ndev);		return NULL;	}	/* One reference from device.  We must do this before	 * we invoke __ipv6_regen_rndid().	 */	in6_dev_hold(ndev);#ifdef CONFIG_IPV6_PRIVACY	init_timer(&ndev->regen_timer);	ndev->regen_timer.function = ipv6_regen_rndid;	ndev->regen_timer.data = (unsigned long) ndev;	if ((dev->flags&IFF_LOOPBACK) ||	    dev->type == ARPHRD_TUNNEL ||#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)	    dev->type == ARPHRD_SIT ||#endif	    dev->type == ARPHRD_NONE) {		printk(KERN_INFO		       "%s: Disabled Privacy Extensions\n",		       dev->name);		ndev->cnf.use_tempaddr = -1;	} else {		in6_dev_hold(ndev);		ipv6_regen_rndid((unsigned long) ndev);	}#endif	if (netif_running(dev) && addrconf_qdisc_ok(dev))		ndev->if_flags |= IF_READY;	ipv6_mc_init_dev(ndev);	ndev->tstamp = jiffies;#ifdef CONFIG_SYSCTL	neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,			      NET_IPV6_NEIGH, "ipv6",			      &ndisc_ifinfo_sysctl_change,			      NULL);	addrconf_sysctl_register(ndev, &ndev->cnf);#endif	/* protected by rtnl_lock */	rcu_assign_pointer(dev->ip6_ptr, ndev);	/* Join all-node multicast group */	ipv6_addr_all_nodes(&maddr);	ipv6_dev_mc_inc(dev, &maddr);	return ndev;}static struct inet6_dev * ipv6_find_idev(struct net_device *dev){	struct inet6_dev *idev;	ASSERT_RTNL();	if ((idev = __in6_dev_get(dev)) == NULL) {		if ((idev = ipv6_add_dev(dev)) == NULL)			return NULL;	}	if (dev->flags&IFF_UP)		ipv6_mc_up(idev);	return idev;}#ifdef CONFIG_SYSCTLstatic void dev_forward_change(struct inet6_dev *idev){	struct net_device *dev;	struct inet6_ifaddr *ifa;	struct in6_addr addr;	if (!idev)		return;	dev = idev->dev;	if (dev && (dev->flags & IFF_MULTICAST)) {		ipv6_addr_all_routers(&addr);		if (idev->cnf.forwarding)			ipv6_dev_mc_inc(dev, &addr);		else			ipv6_dev_mc_dec(dev, &addr);	}	for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {		if (ifa->flags&IFA_F_TENTATIVE)			continue;		if (idev->cnf.forwarding)			addrconf_join_anycast(ifa);		else			addrconf_leave_anycast(ifa);	}}static void addrconf_forward_change(void){	struct net_device *dev;	struct inet6_dev *idev;	read_lock(&dev_base_lock);	for_each_netdev(&init_net, dev) {		rcu_read_lock();		idev = __in6_dev_get(dev);		if (idev) {			int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding);			idev->cnf.forwarding = ipv6_devconf.forwarding;			if (changed)				dev_forward_change(idev);		}		rcu_read_unlock();	}	read_unlock(&dev_base_lock);}#endif/* Nobody refers to this ifaddr, destroy it */void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp){	BUG_TRAP(ifp->if_next==NULL);	BUG_TRAP(ifp->lst_next==NULL);#ifdef NET_REFCNT_DEBUG	printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");#endif	in6_dev_put(ifp->idev);	if (del_timer(&ifp->timer))		printk("Timer is still running, when freeing ifa=%p\n", ifp);	if (!ifp->dead) {		printk("Freeing alive inet6 address %p\n", ifp);		return;	}	dst_release(&ifp->rt->u.dst);	kfree(ifp);}static voidipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp){	struct inet6_ifaddr *ifa, **ifap;	int ifp_scope = ipv6_addr_src_scope(&ifp->addr);	/*	 * Each device address list is sorted in order of scope -	 * global before linklocal.	 */	for (ifap = &idev->addr_list; (ifa = *ifap) != NULL;	     ifap = &ifa->if_next) {		if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))			break;	}	ifp->if_next = *ifap;	*ifap = ifp;}/* On success it returns ifp with increased reference count */static struct inet6_ifaddr *ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,	      int scope, u32 flags){	struct inet6_ifaddr *ifa = NULL;	struct rt6_info *rt;	int hash;	int err = 0;	rcu_read_lock_bh();	if (idev->dead) {		err = -ENODEV;			/*XXX*/		goto out2;	}	write_lock(&addrconf_hash_lock);	/* Ignore adding duplicate addresses on an interface */	if (ipv6_chk_same_addr(addr, idev->dev)) {

⌨️ 快捷键说明

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