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

📄 devinet.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	}	ipv4_devconf_setall(in_dev);	in_dev_hold(in_dev);	if (tb[IFA_ADDRESS] == NULL)		tb[IFA_ADDRESS] = tb[IFA_LOCAL];	ifa->ifa_prefixlen = ifm->ifa_prefixlen;	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);	ifa->ifa_flags = ifm->ifa_flags;	ifa->ifa_scope = ifm->ifa_scope;	ifa->ifa_dev = in_dev;	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);	if (tb[IFA_BROADCAST])		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);	if (tb[IFA_ANYCAST])		ifa->ifa_anycast = nla_get_be32(tb[IFA_ANYCAST]);	if (tb[IFA_LABEL])		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);	else		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);	return ifa;errout:	return ERR_PTR(err);}static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){	struct in_ifaddr *ifa;	ASSERT_RTNL();	ifa = rtm_to_ifaddr(nlh);	if (IS_ERR(ifa))		return PTR_ERR(ifa);	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);}/* *	Determine a default network mask, based on the IP address. */static __inline__ int inet_abc_len(__be32 addr){	int rc = -1;	/* Something else, probably a multicast. */	if (ZERONET(addr))		rc = 0;	else {		__u32 haddr = ntohl(addr);		if (IN_CLASSA(haddr))			rc = 8;		else if (IN_CLASSB(haddr))			rc = 16;		else if (IN_CLASSC(haddr))			rc = 24;	}	return rc;}int devinet_ioctl(unsigned int cmd, void __user *arg){	struct ifreq ifr;	struct sockaddr_in sin_orig;	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 = -EFAULT;	int tryaddrmatch = 0;	/*	 *	Fetch the caller's info block into kernel space	 */	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))		goto out;	ifr.ifr_name[IFNAMSIZ - 1] = 0;	/* save original address for comparison */	memcpy(&sin_orig, sin, sizeof(*sin));	colon = strchr(ifr.ifr_name, ':');	if (colon)		*colon = 0;#ifdef CONFIG_KMOD	dev_load(&init_net, 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 these 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)		 */		tryaddrmatch = (sin_orig.sin_family == AF_INET);		memset(sin, 0, sizeof(*sin));		sin->sin_family = AF_INET;		break;	case SIOCSIFFLAGS:		ret = -EACCES;		if (!capable(CAP_NET_ADMIN))			goto out;		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 */		ret = -EACCES;		if (!capable(CAP_NET_ADMIN))			goto out;		ret = -EINVAL;		if (sin->sin_family != AF_INET)			goto out;		break;	default:		ret = -EINVAL;		goto out;	}	rtnl_lock();	ret = -ENODEV;	if ((dev = __dev_get_by_name(&init_net, ifr.ifr_name)) == NULL)		goto done;	if (colon)		*colon = ':';	if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {		if (tryaddrmatch) {			/* Matthias Andree */			/* compare label and address (4.4BSD style) */			/* note: we only do this for a limited set of ioctls			   and only if the original address family was AF_INET.			   This is checked above. */			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;			     ifap = &ifa->ifa_next) {				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&				    sin_orig.sin_addr.s_addr ==							ifa->ifa_address) {					break; /* found */				}			}		}		/* we didn't get a match, maybe the application is		   4.3BSD-style and passed in junk so we fall back to		   comparing just the label */		if (!ifa) {			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;			     ifap = &ifa->ifa_next)				if (!strcmp(ifr.ifr_name, ifa->ifa_label))					break;		}	}	ret = -EADDRNOTAVAIL;	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)		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) {			ret = -EADDRNOTAVAIL;			if (!ifa)				break;			ret = 0;			if (!(ifr.ifr_flags & IFF_UP))				inet_del_ifa(in_dev, ifap, 1);			break;		}		ret = dev_change_flags(dev, ifr.ifr_flags);		break;	case SIOCSIFADDR:	/* Set interface address (and family) */		ret = -EINVAL;		if (inet_abc_len(sin->sin_addr.s_addr) < 0)			break;		if (!ifa) {			ret = -ENOBUFS;			if ((ifa = inet_alloc_ifa()) == NULL)				break;			if (colon)				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);			else				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);		} else {			ret = 0;			if (ifa->ifa_local == sin->sin_addr.s_addr)				break;			inet_del_ifa(in_dev, ifap, 0);			ifa->ifa_broadcast = 0;			ifa->ifa_anycast = 0;		}		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;		if (!(dev->flags & IFF_POINTOPOINT)) {			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);			if ((dev->flags & IFF_BROADCAST) &&			    ifa->ifa_prefixlen < 31)				ifa->ifa_broadcast = ifa->ifa_address |						     ~ifa->ifa_mask;		} else {			ifa->ifa_prefixlen = 32;			ifa->ifa_mask = inet_make_mask(32);		}		ret = inet_set_ifa(dev, ifa);		break;	case SIOCSIFBRDADDR:	/* Set the broadcast address */		ret = 0;		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {			inet_del_ifa(in_dev, ifap, 0);			ifa->ifa_broadcast = sin->sin_addr.s_addr;			inet_insert_ifa(ifa);		}		break;	case SIOCSIFDSTADDR:	/* Set the destination address */		ret = 0;		if (ifa->ifa_address == sin->sin_addr.s_addr)			break;		ret = -EINVAL;		if (inet_abc_len(sin->sin_addr.s_addr) < 0)			break;		ret = 0;		inet_del_ifa(in_dev, ifap, 0);		ifa->ifa_address = sin->sin_addr.s_addr;		inet_insert_ifa(ifa);		break;	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */		/*		 *	The mask we set must be legal.		 */		ret = -EINVAL;		if (bad_mask(sin->sin_addr.s_addr, 0))			break;		ret = 0;		if (ifa->ifa_mask != sin->sin_addr.s_addr) {			__be32 old_mask = ifa->ifa_mask;			inet_del_ifa(in_dev, ifap, 0);			ifa->ifa_mask = sin->sin_addr.s_addr;			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);			/* See if current broadcast address matches			 * with current netmask, then recalculate			 * the broadcast address. Otherwise it's a			 * funny address, so don't touch it since			 * the user seems to know what (s)he's doing...			 */			if ((dev->flags & IFF_BROADCAST) &&			    (ifa->ifa_prefixlen < 31) &&			    (ifa->ifa_broadcast ==			     (ifa->ifa_local|~old_mask))) {				ifa->ifa_broadcast = (ifa->ifa_local |						      ~sin->sin_addr.s_addr);			}			inet_insert_ifa(ifa);		}		break;	}done:	rtnl_unlock();out:	return ret;rarok:	rtnl_unlock();	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;	goto out;}static int inet_gifconf(struct net_device *dev, char __user *buf, int len){	struct in_device *in_dev = __in_dev_get_rtnl(dev);	struct in_ifaddr *ifa;	struct ifreq ifr;	int done = 0;	if (!in_dev || (ifa = in_dev->ifa_list) == NULL)		goto out;	for (; ifa; ifa = ifa->ifa_next) {		if (!buf) {			done += sizeof(ifr);			continue;		}		if (len < (int) sizeof(ifr))			break;		memset(&ifr, 0, sizeof(struct ifreq));		if (ifa->ifa_label)			strcpy(ifr.ifr_name, ifa->ifa_label);		else			strcpy(ifr.ifr_name, dev->name);		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =								ifa->ifa_local;		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {			done = -EFAULT;			break;		}		buf  += sizeof(struct ifreq);		len  -= sizeof(struct ifreq);		done += sizeof(struct ifreq);	}out:	return done;}__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope){	__be32 addr = 0;	struct in_device *in_dev;	rcu_read_lock();	in_dev = __in_dev_get_rcu(dev);	if (!in_dev)		goto no_in_dev;	for_primary_ifa(in_dev) {		if (ifa->ifa_scope > scope)			continue;		if (!dst || inet_ifa_match(dst, ifa)) {			addr = ifa->ifa_local;			break;		}		if (!addr)			addr = ifa->ifa_local;	} endfor_ifa(in_dev);no_in_dev:	rcu_read_unlock();	if (addr)		goto out;	/* Not loopback addresses on loopback should be preferred	   in this case. It is importnat that lo is the first interface	   in dev_base list.	 */	read_lock(&dev_base_lock);	rcu_read_lock();	for_each_netdev(&init_net, dev) {		if ((in_dev = __in_dev_get_rcu(dev)) == NULL)			continue;		for_primary_ifa(in_dev) {			if (ifa->ifa_scope != RT_SCOPE_LINK &&			    ifa->ifa_scope <= scope) {				addr = ifa->ifa_local;				goto out_unlock_both;			}		} endfor_ifa(in_dev);	}out_unlock_both:	read_unlock(&dev_base_lock);	rcu_read_unlock();out:	return addr;}static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,			      __be32 local, int scope){	int same = 0;	__be32 addr = 0;	for_ifa(in_dev) {		if (!addr &&		    (local == ifa->ifa_local || !local) &&		    ifa->ifa_scope <= scope) {			addr = ifa->ifa_local;			if (same)				break;		}		if (!same) {			same = (!local || inet_ifa_match(local, ifa)) &&				(!dst || inet_ifa_match(dst, ifa));			if (same && addr) {				if (local || !dst)					break;				/* Is the selected addr into dst subnet? */				if (inet_ifa_match(addr, ifa))					break;				/* No, then can we use new local src? */				if (ifa->ifa_scope <= scope) {					addr = ifa->ifa_local;					break;				}				/* search for large dst subnet for addr */				same = 0;			}		}	} endfor_ifa(in_dev);	return same? addr : 0;}/* * Confirm that local IP address exists using wildcards: * - dev: only on this interface, 0=any interface * - dst: only in the same subnet as dst, 0=any dst * - local: address, 0=autoselect the local address * - scope: maximum allowed scope value for the local address */__be32 inet_confirm_addr(const struct net_device *dev, __be32 dst, __be32 local, int scope){	__be32 addr = 0;	struct in_device *in_dev;	if (dev) {		rcu_read_lock();		if ((in_dev = __in_dev_get_rcu(dev)))			addr = confirm_addr_indev(in_dev, dst, local, scope);		rcu_read_unlock();		return addr;	}	read_lock(&dev_base_lock);	rcu_read_lock();	for_each_netdev(&init_net, dev) {		if ((in_dev = __in_dev_get_rcu(dev))) {			addr = confirm_addr_indev(in_dev, dst, local, scope);			if (addr)				break;		}	}	rcu_read_unlock();	read_unlock(&dev_base_lock);	return addr;}/* *	Device notifier */int register_inetaddr_notifier(struct notifier_block *nb){	return blocking_notifier_chain_register(&inetaddr_chain, nb);}int unregister_inetaddr_notifier(struct notifier_block *nb){	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);}/* Rename ifa_labels for a device name change. Make some effort to preserve existing * alias numbering and to create unique labels if possible.*/static void inetdev_changename(struct net_device *dev, struct in_device *in_dev){	struct in_ifaddr *ifa;	int named = 0;	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {		char old[IFNAMSIZ], *dot;		memcpy(old, ifa->ifa_label, IFNAMSIZ);		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);		if (named++ == 0)			continue;		dot = strchr(old, ':');		if (dot == NULL) {			sprintf(old, ":%d", named);			dot = old;		}		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) {			strcat(ifa->ifa_label, dot);		} else {			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);		}	}}/* Called only under RTNL semaphore */static int inetdev_event(struct notifier_block *this, unsigned long event,			 void *ptr){	struct net_device *dev = ptr;	struct in_device *in_dev = __in_dev_get_rtnl(dev);	if (dev->nd_net != &init_net)		return NOTIFY_DONE;

⌨️ 快捷键说明

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