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

📄 devinet.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *	NET3	IP device support routines. * *	Version: $Id: devinet.c,v 1.44 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. * *	Derived from the IP parts of dev.c 1.0.19 * 		Authors:	Ross Biro *				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 *		Matthias Andree:	in devinet_ioctl, compare label and *					address (4.4BSD alias style support), *					fall back to comparing just the label *					if no match found. */#include <asm/uaccess.h>#include <asm/system.h>#include <linux/bitops.h>#include <linux/capability.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.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_addr.h>#include <linux/if_ether.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.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#include <linux/kmod.h>#include <net/arp.h>#include <net/ip.h>#include <net/route.h>#include <net/ip_fib.h>#include <net/rtnetlink.h>struct ipv4_devconf ipv4_devconf = {	.data = {		[NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,	},};static struct ipv4_devconf ipv4_devconf_dflt = {	.data = {		[NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,		[NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,		[NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE - 1] = 1,	},};#define IPV4_DEVCONF_DFLT(attr) IPV4_DEVCONF(ipv4_devconf_dflt, attr)static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {	[IFA_LOCAL]     	= { .type = NLA_U32 },	[IFA_ADDRESS]   	= { .type = NLA_U32 },	[IFA_BROADCAST] 	= { .type = NLA_U32 },	[IFA_ANYCAST]   	= { .type = NLA_U32 },	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },};static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);static BLOCKING_NOTIFIER_HEAD(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);#endif/* Locks all the inet devices. */static struct in_ifaddr *inet_alloc_ifa(void){	struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);	if (ifa) {		INIT_RCU_HEAD(&ifa->rcu_head);	}	return ifa;}static void inet_rcu_free_ifa(struct rcu_head *head){	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);	if (ifa->ifa_dev)		in_dev_put(ifa->ifa_dev);	kfree(ifa);}static inline void inet_free_ifa(struct in_ifaddr *ifa){	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);}void in_dev_finish_destroy(struct in_device *idev){	struct net_device *dev = idev->dev;	BUG_TRAP(!idev->ifa_list);	BUG_TRAP(!idev->mc_list);#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);	else {		kfree(idev);	}}static struct in_device *inetdev_init(struct net_device *dev){	struct in_device *in_dev;	ASSERT_RTNL();	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);	if (!in_dev)		goto out;	INIT_RCU_HEAD(&in_dev->rcu_head);	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)		goto out_kfree;	/* Reference in_dev->dev */	dev_hold(dev);#ifdef CONFIG_SYSCTL	neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,			      NET_IPV4_NEIGH, "ipv4", NULL, NULL);#endif	/* Account for reference dev->ip_ptr (below) */	in_dev_hold(in_dev);#ifdef CONFIG_SYSCTL	devinet_sysctl_register(in_dev, &in_dev->cnf);#endif	ip_mc_init_dev(in_dev);	if (dev->flags & IFF_UP)		ip_mc_up(in_dev);	/* we can receive as soon as ip_ptr is set -- do this last */	rcu_assign_pointer(dev->ip_ptr, in_dev);out:	return in_dev;out_kfree:	kfree(in_dev);	in_dev = NULL;	goto out;}static void in_dev_rcu_put(struct rcu_head *head){	struct in_device *idev = container_of(head, struct in_device, rcu_head);	in_dev_put(idev);}static void inetdev_destroy(struct in_device *in_dev){	struct in_ifaddr *ifa;	struct net_device *dev;	ASSERT_RTNL();	dev = in_dev->dev;	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	dev->ip_ptr = NULL;#ifdef CONFIG_SYSCTL	neigh_sysctl_unregister(in_dev->arp_parms);#endif	neigh_parms_release(&arp_tbl, in_dev->arp_parms);	arp_ifdown(dev);	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);}int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b){	rcu_read_lock();	for_primary_ifa(in_dev) {		if (inet_ifa_match(a, ifa)) {			if (!b || inet_ifa_match(b, ifa)) {				rcu_read_unlock();				return 1;			}		}	} endfor_ifa(in_dev);	rcu_read_unlock();	return 0;}static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,			 int destroy, struct nlmsghdr *nlh, u32 pid){	struct in_ifaddr *promote = NULL;	struct in_ifaddr *ifa, *ifa1 = *ifap;	struct in_ifaddr *last_prim = in_dev->ifa_list;	struct in_ifaddr *prev_prom = NULL;	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);	ASSERT_RTNL();	/* 1. Deleting primary ifaddr forces deletion all secondaries	 * unless alias promotion is set	 **/	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {		struct in_ifaddr **ifap1 = &ifa1->ifa_next;		while ((ifa = *ifap1) != NULL) {			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&			    ifa1->ifa_scope <= ifa->ifa_scope)				last_prim = ifa;			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||			    ifa1->ifa_mask != ifa->ifa_mask ||			    !inet_ifa_match(ifa1->ifa_address, ifa)) {				ifap1 = &ifa->ifa_next;				prev_prom = ifa;				continue;			}			if (!do_promote) {				*ifap1 = ifa->ifa_next;				rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);				blocking_notifier_call_chain(&inetaddr_chain,						NETDEV_DOWN, ifa);				inet_free_ifa(ifa);			} else {				promote = ifa;				break;			}		}	}	/* 2. Unlink it */	*ifap = ifa1->ifa_next;	/* 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, nlh, pid);	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);	if (promote) {		if (prev_prom) {			prev_prom->ifa_next = promote->ifa_next;			promote->ifa_next = last_prim->ifa_next;			last_prim->ifa_next = promote;		}		promote->ifa_flags &= ~IFA_F_SECONDARY;		rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);		blocking_notifier_call_chain(&inetaddr_chain,				NETDEV_UP, promote);		for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {			if (ifa1->ifa_mask != ifa->ifa_mask ||			    !inet_ifa_match(ifa1->ifa_address, ifa))					continue;			fib_add_ifaddr(ifa);		}	}	if (destroy)		inet_free_ifa(ifa1);}static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,			 int destroy){	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);}static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,			     u32 pid){	struct in_device *in_dev = ifa->ifa_dev;	struct in_ifaddr *ifa1, **ifap, **last_primary;	ASSERT_RTNL();	if (!ifa->ifa_local) {		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;	*ifap = ifa;	/* 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, nlh, pid);	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);	return 0;}static int inet_insert_ifa(struct in_ifaddr *ifa){	return __inet_insert_ifa(ifa, NULL, 0);}static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa){	struct in_device *in_dev = __in_dev_get_rtnl(dev);	ASSERT_RTNL();	if (!in_dev) {		inet_free_ifa(ifa);		return -ENOBUFS;	}	ipv4_devconf_setall(in_dev);	if (ifa->ifa_dev != in_dev) {		BUG_TRAP(!ifa->ifa_dev);		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(&init_net, 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, __be32 prefix,				    __be32 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;}static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){	struct nlattr *tb[IFA_MAX+1];	struct in_device *in_dev;	struct ifaddrmsg *ifm;	struct in_ifaddr *ifa, **ifap;	int err = -EINVAL;	ASSERT_RTNL();	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);	if (err < 0)		goto errout;	ifm = nlmsg_data(nlh);	in_dev = inetdev_by_index(ifm->ifa_index);	if (in_dev == NULL) {		err = -ENODEV;		goto errout;	}	__in_dev_put(in_dev);	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;	     ifap = &ifa->ifa_next) {		if (tb[IFA_LOCAL] &&		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))			continue;		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))			continue;		if (tb[IFA_ADDRESS] &&		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))			continue;		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);		return 0;	}	err = -EADDRNOTAVAIL;errout:	return err;}static struct in_ifaddr *rtm_to_ifaddr(struct nlmsghdr *nlh){	struct nlattr *tb[IFA_MAX+1];	struct in_ifaddr *ifa;	struct ifaddrmsg *ifm;	struct net_device *dev;	struct in_device *in_dev;	int err = -EINVAL;	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);	if (err < 0)		goto errout;	ifm = nlmsg_data(nlh);	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) {		err = -EINVAL;		goto errout;	}	dev = __dev_get_by_index(&init_net, ifm->ifa_index);	if (dev == NULL) {		err = -ENODEV;		goto errout;	}	in_dev = __in_dev_get_rtnl(dev);	if (in_dev == NULL) {		err = -ENOBUFS;		goto errout;	}	ifa = inet_alloc_ifa();	if (ifa == NULL) {		/*		 * A potential indev allocation can be left alive, it stays		 * assigned to its device and is destroy with it.		 */		err = -ENOBUFS;		goto errout;

⌨️ 快捷键说明

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