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

📄 devinet.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	NET3	IP device support routines. * *	Version: $Id: devinet.c,v 1.39 2000/12/10 22:24:11 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. * *	Derived from the IP parts of dev.c 1.0.19 * 		Authors:	Ross Biro, <bir7@leland.Stanford.Edu> *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> *				Mark Evans, <evansmp@uhura.aston.ac.uk> * *	Additional Authors: *		Alan Cox, <gw4pts@gw4pts.ampr.org> *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * *	Changes: *	        Alexey Kuznetsov:	pa_* fields are replaced with ifaddr lists. *		Cyrus Durgin:		updated for kmod */#include <linux/config.h> #include <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/if_ether.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/rtnetlink.h>#include <linux/init.h>#include <linux/notifier.h>#include <linux/inetdevice.h>#include <linux/igmp.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#include <net/ip.h>#include <net/route.h>#include <net/ip_fib.h>struct ipv4_devconf ipv4_devconf = { 1, 1, 1, 1, 0, };static struct ipv4_devconf ipv4_devconf_dflt = { 1, 1, 1, 1, 1, };#ifdef CONFIG_RTNETLINKstatic void rtmsg_ifa(int event, struct in_ifaddr *);#else#define rtmsg_ifa(a,b)	do { } while(0)#endifstatic struct notifier_block *inetaddr_chain;static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy);#ifdef CONFIG_SYSCTLstatic void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p);static void devinet_sysctl_unregister(struct ipv4_devconf *p);#endifint inet_ifa_count;int inet_dev_count;/* Locks all the inet devices. */rwlock_t inetdev_lock = RW_LOCK_UNLOCKED;static struct in_ifaddr * inet_alloc_ifa(void){	struct in_ifaddr *ifa;	ifa = kmalloc(sizeof(*ifa), GFP_KERNEL);	if (ifa) {		memset(ifa, 0, sizeof(*ifa));		inet_ifa_count++;	}	return ifa;}static __inline__ void inet_free_ifa(struct in_ifaddr *ifa){	if (ifa->ifa_dev)		__in_dev_put(ifa->ifa_dev);	kfree(ifa);	inet_ifa_count--;}void in_dev_finish_destroy(struct in_device *idev){	struct net_device *dev = idev->dev;	BUG_TRAP(idev->ifa_list==NULL);	BUG_TRAP(idev->mc_list==NULL);#ifdef NET_REFCNT_DEBUG	printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n", idev, dev ? dev->name : "NIL");#endif	dev_put(dev);	if (!idev->dead) {		printk("Freeing alive in_device %p\n", idev);		return;	}	inet_dev_count--;	kfree(idev);}struct in_device *inetdev_init(struct net_device *dev){	struct in_device *in_dev;	ASSERT_RTNL();	in_dev = kmalloc(sizeof(*in_dev), GFP_KERNEL);	if (!in_dev)		return NULL;	memset(in_dev, 0, sizeof(*in_dev));	in_dev->lock = RW_LOCK_UNLOCKED;	memcpy(&in_dev->cnf, &ipv4_devconf_dflt, sizeof(in_dev->cnf));	in_dev->cnf.sysctl = NULL;	in_dev->dev = dev;	if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) {		kfree(in_dev);		return NULL;	}	inet_dev_count++;	/* Reference in_dev->dev */	dev_hold(dev);#ifdef CONFIG_SYSCTL	neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");#endif	write_lock_bh(&inetdev_lock);	dev->ip_ptr = in_dev;	/* Account for reference dev->ip_ptr */	in_dev_hold(in_dev);	write_unlock_bh(&inetdev_lock);#ifdef CONFIG_SYSCTL	devinet_sysctl_register(in_dev, &in_dev->cnf);#endif	if (dev->flags&IFF_UP)		ip_mc_up(in_dev);	return in_dev;}static void inetdev_destroy(struct in_device *in_dev){	struct in_ifaddr *ifa;	ASSERT_RTNL();	in_dev->dead = 1;	ip_mc_destroy_dev(in_dev);	while ((ifa = in_dev->ifa_list) != NULL) {		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);		inet_free_ifa(ifa);	}#ifdef CONFIG_SYSCTL	devinet_sysctl_unregister(&in_dev->cnf);#endif	write_lock_bh(&inetdev_lock);	in_dev->dev->ip_ptr = NULL;	/* in_dev_put following below will kill the in_device */	write_unlock_bh(&inetdev_lock);	neigh_parms_release(&arp_tbl, in_dev->arp_parms);	in_dev_put(in_dev);}int inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b){	read_lock(&in_dev->lock);	for_primary_ifa(in_dev) {		if (inet_ifa_match(a, ifa)) {			if (!b || inet_ifa_match(b, ifa)) {				read_unlock(&in_dev->lock);				return 1;			}		}	} endfor_ifa(in_dev);	read_unlock(&in_dev->lock);	return 0;} static voidinet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy){	struct in_ifaddr *ifa1 = *ifap;	ASSERT_RTNL();	/* 1. Deleting primary ifaddr forces deletion all secondaries */	if (!(ifa1->ifa_flags&IFA_F_SECONDARY)) {		struct in_ifaddr *ifa;		struct in_ifaddr **ifap1 = &ifa1->ifa_next;		while ((ifa=*ifap1) != NULL) {			if (!(ifa->ifa_flags&IFA_F_SECONDARY) ||			    ifa1->ifa_mask != ifa->ifa_mask ||			    !inet_ifa_match(ifa1->ifa_address, ifa)) {				ifap1 = &ifa->ifa_next;				continue;			}			write_lock_bh(&in_dev->lock);			*ifap1 = ifa->ifa_next;			write_unlock_bh(&in_dev->lock);			rtmsg_ifa(RTM_DELADDR, ifa);			notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa);			inet_free_ifa(ifa);		}	}	/* 2. Unlink it */	write_lock_bh(&in_dev->lock);	*ifap = ifa1->ifa_next;	write_unlock_bh(&in_dev->lock);	/* 3. Announce address deletion */	/* Send message first, then call notifier.	   At first sight, FIB update triggered by notifier	   will refer to already deleted ifaddr, that could confuse	   netlink listeners. It is not true: look, gated sees	   that route deleted and if it still thinks that ifaddr	   is valid, it will try to restore deleted routes... Grr.	   So that, this order is correct.	 */	rtmsg_ifa(RTM_DELADDR, ifa1);	notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);	if (destroy) {		inet_free_ifa(ifa1);		if (in_dev->ifa_list == NULL)			inetdev_destroy(in_dev);	}}static intinet_insert_ifa(struct in_ifaddr *ifa){	struct in_device *in_dev = ifa->ifa_dev;	struct in_ifaddr *ifa1, **ifap, **last_primary;	ASSERT_RTNL();	if (ifa->ifa_local == 0) {		inet_free_ifa(ifa);		return 0;	}	ifa->ifa_flags &= ~IFA_F_SECONDARY;	last_primary = &in_dev->ifa_list;	for (ifap=&in_dev->ifa_list; (ifa1=*ifap)!=NULL; ifap=&ifa1->ifa_next) {		if (!(ifa1->ifa_flags&IFA_F_SECONDARY) && ifa->ifa_scope <= ifa1->ifa_scope)			last_primary = &ifa1->ifa_next;		if (ifa1->ifa_mask == ifa->ifa_mask && inet_ifa_match(ifa1->ifa_address, ifa)) {			if (ifa1->ifa_local == ifa->ifa_local) {				inet_free_ifa(ifa);				return -EEXIST;			}			if (ifa1->ifa_scope != ifa->ifa_scope) {				inet_free_ifa(ifa);				return -EINVAL;			}			ifa->ifa_flags |= IFA_F_SECONDARY;		}	}	if (!(ifa->ifa_flags&IFA_F_SECONDARY)) {		net_srandom(ifa->ifa_local);		ifap = last_primary;	}	ifa->ifa_next = *ifap;	write_lock_bh(&in_dev->lock);	*ifap = ifa;	write_unlock_bh(&in_dev->lock);	/* Send message first, then call notifier.	   Notifier will trigger FIB update, so that	   listeners of netlink will know about new ifaddr */	rtmsg_ifa(RTM_NEWADDR, ifa);	notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);	return 0;}static intinet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa){	struct in_device *in_dev = __in_dev_get(dev);	ASSERT_RTNL();	if (in_dev == NULL) {		in_dev = inetdev_init(dev);		if (in_dev == NULL) {			inet_free_ifa(ifa);			return -ENOBUFS;		}	}	if (ifa->ifa_dev != in_dev) {		BUG_TRAP(ifa->ifa_dev==NULL);		in_dev_hold(in_dev);		ifa->ifa_dev=in_dev;	}	if (LOOPBACK(ifa->ifa_local))		ifa->ifa_scope = RT_SCOPE_HOST;	return inet_insert_ifa(ifa);}struct in_device *inetdev_by_index(int ifindex){	struct net_device *dev;	struct in_device *in_dev = NULL;	read_lock(&dev_base_lock);	dev = __dev_get_by_index(ifindex);	if (dev)		in_dev = in_dev_get(dev);	read_unlock(&dev_base_lock);	return in_dev;}/* Called only from RTNL semaphored context. No locks. */struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask){	ASSERT_RTNL();	for_primary_ifa(in_dev) {		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))			return ifa;	} endfor_ifa(in_dev);	return NULL;}#ifdef CONFIG_RTNETLINKintinet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){	struct rtattr  **rta = arg;	struct in_device *in_dev;	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);	struct in_ifaddr *ifa, **ifap;	ASSERT_RTNL();	if ((in_dev = inetdev_by_index(ifm->ifa_index)) == NULL)		return -EADDRNOTAVAIL;	__in_dev_put(in_dev);	for (ifap=&in_dev->ifa_list; (ifa=*ifap)!=NULL; ifap=&ifa->ifa_next) {		if ((rta[IFA_LOCAL-1] && memcmp(RTA_DATA(rta[IFA_LOCAL-1]), &ifa->ifa_local, 4)) ||		    (rta[IFA_LABEL-1] && strcmp(RTA_DATA(rta[IFA_LABEL-1]), ifa->ifa_label)) ||		    (rta[IFA_ADDRESS-1] &&		     (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||		      !inet_ifa_match(*(u32*)RTA_DATA(rta[IFA_ADDRESS-1]), ifa))))			continue;		inet_del_ifa(in_dev, ifap, 1);		return 0;	}	return -EADDRNOTAVAIL;}intinet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){	struct rtattr **rta = arg;	struct net_device *dev;	struct in_device *in_dev;	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);	struct in_ifaddr *ifa;	ASSERT_RTNL();	if (ifm->ifa_prefixlen > 32 || rta[IFA_LOCAL-1] == NULL)		return -EINVAL;	if ((dev = __dev_get_by_index(ifm->ifa_index)) == NULL)		return -ENODEV;	if ((in_dev = __in_dev_get(dev)) == NULL) {		in_dev = inetdev_init(dev);		if (!in_dev)			return -ENOBUFS;	}	if ((ifa = inet_alloc_ifa()) == NULL)		return -ENOBUFS;	if (rta[IFA_ADDRESS-1] == NULL)		rta[IFA_ADDRESS-1] = rta[IFA_LOCAL-1];	memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 4);	memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 4);	ifa->ifa_prefixlen = ifm->ifa_prefixlen;	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);	if (rta[IFA_BROADCAST-1])		memcpy(&ifa->ifa_broadcast, RTA_DATA(rta[IFA_BROADCAST-1]), 4);	if (rta[IFA_ANYCAST-1])		memcpy(&ifa->ifa_anycast, RTA_DATA(rta[IFA_ANYCAST-1]), 4);	ifa->ifa_flags = ifm->ifa_flags;	ifa->ifa_scope = ifm->ifa_scope;	in_dev_hold(in_dev);	ifa->ifa_dev = in_dev;	if (rta[IFA_LABEL-1])		memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL-1]), IFNAMSIZ);	else		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);	return inet_insert_ifa(ifa);}#endif/*  *	Determine a default network mask, based on the IP address.  */static __inline__ int inet_abc_len(u32 addr){  	if (ZERONET(addr))  		return 0;  	addr = ntohl(addr);  	if (IN_CLASSA(addr))   		return 8;  	if (IN_CLASSB(addr))   		return 16;  	if (IN_CLASSC(addr))   		return 24;	/*	 *	Something else, probably a multicast. 	 */  	   	return -1;}int devinet_ioctl(unsigned int cmd, void *arg){	struct ifreq ifr;	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;	struct in_device *in_dev;	struct in_ifaddr **ifap = NULL;	struct in_ifaddr *ifa = NULL;	struct net_device *dev;	char *colon;	int ret = 0;	/*	 *	Fetch the caller's info block into kernel space	 */	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))		return -EFAULT;	ifr.ifr_name[IFNAMSIZ-1] = 0;	colon = strchr(ifr.ifr_name, ':');	if (colon)		*colon = 0;#ifdef CONFIG_KMOD	dev_load(ifr.ifr_name);#endif	switch(cmd) {	case SIOCGIFADDR:	/* Get interface address */	case SIOCGIFBRDADDR:	/* Get the broadcast address */	case SIOCGIFDSTADDR:	/* Get the destination address */	case SIOCGIFNETMASK:	/* Get the netmask for the interface */		/* Note that this ioctls will not sleep,		   so that we do not impose a lock.		   One day we will be forced to put shlock here (I mean SMP)		 */		memset(sin, 0, sizeof(*sin));		sin->sin_family = AF_INET;		break;	case SIOCSIFFLAGS:		if (!capable(CAP_NET_ADMIN))			return -EACCES;		break;	case SIOCSIFADDR:	/* Set interface address (and family) */	case SIOCSIFBRDADDR:	/* Set the broadcast address */	case SIOCSIFDSTADDR:	/* Set the destination address */	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */		if (!capable(CAP_NET_ADMIN))			return -EACCES;		if (sin->sin_family != AF_INET)			return -EINVAL;		break;	default:		return -EINVAL;	}	dev_probe_lock();	rtnl_lock();	if ((dev = __dev_get_by_name(ifr.ifr_name)) == NULL) {		ret = -ENODEV;		goto done;	}	if (colon)		*colon = ':';	if ((in_dev=__in_dev_get(dev)) != NULL) {		for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)			if (strcmp(ifr.ifr_name, ifa->ifa_label) == 0)				break;	}	if (ifa == NULL && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) {		ret = -EADDRNOTAVAIL;		goto done;	}	switch(cmd) {		case SIOCGIFADDR:	/* Get interface address */			sin->sin_addr.s_addr = ifa->ifa_local;			goto rarok;		case SIOCGIFBRDADDR:	/* Get the broadcast address */			sin->sin_addr.s_addr = ifa->ifa_broadcast;			goto rarok;		case SIOCGIFDSTADDR:	/* Get the destination address */			sin->sin_addr.s_addr = ifa->ifa_address;			goto rarok;		case SIOCGIFNETMASK:	/* Get the netmask for the interface */			sin->sin_addr.s_addr = ifa->ifa_mask;			goto rarok;		case SIOCSIFFLAGS:			if (colon) {				if (ifa == NULL) {					ret = -EADDRNOTAVAIL;					break;				}				if (!(ifr.ifr_flags&IFF_UP))					inet_del_ifa(in_dev, ifap, 1);

⌨️ 快捷键说明

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