Skip to content
Snippets Groups Projects
dev.c 144 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (old) {
    		synchronize_net();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev_put(old);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (master)
    		slave->flags |= IFF_SLAVE;
    	else
    		slave->flags &= ~IFF_SLAVE;
    
    	rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
    	return 0;
    }
    
    EXPORT_SYMBOL(netdev_set_master);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static void dev_change_rx_flags(struct net_device *dev, int flags)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    	if ((dev->flags & IFF_UP) && ops->ndo_change_rx_flags)
    		ops->ndo_change_rx_flags(dev, flags);
    
    static int __dev_set_promiscuity(struct net_device *dev, int inc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned short old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	dev->flags |= IFF_PROMISC;
    	dev->promiscuity += inc;
    	if (dev->promiscuity == 0) {
    		/*
    		 * Avoid overflow.
    		 * If inc causes overflow, untouch promisc and return error.
    		 */
    		if (inc < 0)
    			dev->flags &= ~IFF_PROMISC;
    		else {
    			dev->promiscuity -= inc;
    			printk(KERN_WARNING "%s: promiscuity touches roof, "
    				"set promiscuity failed, promiscuity feature "
    				"of device might be broken.\n", dev->name);
    			return -EOVERFLOW;
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(KERN_INFO "device %s %s promiscuous mode\n",
    		       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
    
    		if (audit_enabled) {
    			current_uid_gid(&uid, &gid);
    
    			audit_log(current->audit_context, GFP_ATOMIC,
    				AUDIT_ANOM_PROMISCUOUS,
    				"dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
    				dev->name, (dev->flags & IFF_PROMISC),
    				(old_flags & IFF_PROMISC),
    				audit_get_loginuid(current),
    
    				audit_get_sessionid(current));
    
    		dev_change_rx_flags(dev, IFF_PROMISC);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    /**
     *	dev_set_promiscuity	- update promiscuity count on a device
     *	@dev: device
     *	@inc: modifier
     *
     *	Add or remove promiscuity from a device. While the count in the device
     *	remains above zero the interface remains promiscuous. Once it hits zero
     *	the device reverts back to normal filtering operation. A negative inc
     *	value is used to drop promiscuity on the device.
    
     *	Return 0 if successful or a negative errno code on error.
    
    int dev_set_promiscuity(struct net_device *dev, int inc)
    
    {
    	unsigned short old_flags = dev->flags;
    
    	err = __dev_set_promiscuity(dev, inc);
    
    	if (dev->flags != old_flags)
    		dev_set_rx_mode(dev);
    
    EXPORT_SYMBOL(dev_set_promiscuity);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	dev_set_allmulti	- update allmulti count on a device
     *	@dev: device
     *	@inc: modifier
     *
     *	Add or remove reception of all multicast frames to a device. While the
     *	count in the device remains above zero the interface remains listening
     *	to all interfaces. Once it hits zero the device reverts back to normal
     *	filtering operation. A negative @inc value is used to drop the counter
     *	when releasing a resource needing all multicasts.
    
     *	Return 0 if successful or a negative errno code on error.
    
    int dev_set_allmulti(struct net_device *dev, int inc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned short old_flags = dev->flags;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	dev->flags |= IFF_ALLMULTI;
    
    	dev->allmulti += inc;
    	if (dev->allmulti == 0) {
    		/*
    		 * Avoid overflow.
    		 * If inc causes overflow, untouch allmulti and return error.
    		 */
    		if (inc < 0)
    			dev->flags &= ~IFF_ALLMULTI;
    		else {
    			dev->allmulti -= inc;
    			printk(KERN_WARNING "%s: allmulti touches roof, "
    				"set allmulti failed, allmulti feature of "
    				"device might be broken.\n", dev->name);
    			return -EOVERFLOW;
    		}
    	}
    
    	if (dev->flags ^ old_flags) {
    
    		dev_change_rx_flags(dev, IFF_ALLMULTI);
    
    		dev_set_rx_mode(dev);
    
    EXPORT_SYMBOL(dev_set_allmulti);
    
    
    /*
     *	Upload unicast and multicast address lists to device and
     *	configure RX filtering. When the device doesn't support unicast
    
     *	filtering it is put in promiscuous mode while unicast addresses
    
     *	are present.
     */
    void __dev_set_rx_mode(struct net_device *dev)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    
    	/* dev_open will call this function so the list will stay sane. */
    	if (!(dev->flags&IFF_UP))
    		return;
    
    	if (!netif_device_present(dev))
    
    	if (ops->ndo_set_rx_mode)
    		ops->ndo_set_rx_mode(dev);
    
    	else {
    		/* Unicast addresses changes may only happen under the rtnl,
    		 * therefore calling __dev_set_promiscuity here is safe.
    		 */
    
    		if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
    
    			__dev_set_promiscuity(dev, 1);
    			dev->uc_promisc = 1;
    
    		} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
    
    			__dev_set_promiscuity(dev, -1);
    			dev->uc_promisc = 0;
    		}
    
    
    		if (ops->ndo_set_multicast_list)
    			ops->ndo_set_multicast_list(dev);
    
    	}
    }
    
    void dev_set_rx_mode(struct net_device *dev)
    {
    
    	netif_addr_lock_bh(dev);
    
    	__dev_set_rx_mode(dev);
    
    	netif_addr_unlock_bh(dev);
    
    /**
     *	dev_get_flags - get flags reported to userspace
     *	@dev: device
     *
     *	Get the combination of flag bits exported through APIs to userspace.
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    unsigned dev_get_flags(const struct net_device *dev)
    {
    	unsigned flags;
    
    	flags = (dev->flags & ~(IFF_PROMISC |
    				IFF_ALLMULTI |
    
    				IFF_RUNNING |
    				IFF_LOWER_UP |
    				IFF_DORMANT)) |
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		(dev->gflags & (IFF_PROMISC |
    				IFF_ALLMULTI));
    
    
    	if (netif_running(dev)) {
    		if (netif_oper_up(dev))
    			flags |= IFF_RUNNING;
    		if (netif_carrier_ok(dev))
    			flags |= IFF_LOWER_UP;
    		if (netif_dormant(dev))
    			flags |= IFF_DORMANT;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return flags;
    }
    
    EXPORT_SYMBOL(dev_get_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    int __dev_change_flags(struct net_device *dev, unsigned int flags)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/*
    	 *	Set the flags on our device.
    	 */
    
    	dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
    			       IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
    			       IFF_AUTOMEDIA)) |
    		     (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
    				    IFF_ALLMULTI));
    
    	/*
    	 *	Load in the correct multicast list now the flags have changed.
    	 */
    
    
    	if ((old_flags ^ flags) & IFF_MULTICAST)
    		dev_change_rx_flags(dev, IFF_MULTICAST);
    
    	dev_set_rx_mode(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 *	Have we downed the interface. We handle IFF_UP ourselves
    	 *	according to user attempts to set it, rather than blindly
    	 *	setting it.
    	 */
    
    	ret = 0;
    	if ((old_flags ^ flags) & IFF_UP) {	/* Bit is different  ? */
    
    		ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		if (!ret)
    
    			dev_set_rx_mode(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	if ((flags ^ dev->gflags) & IFF_PROMISC) {
    
    		int inc = (flags & IFF_PROMISC) ? 1 : -1;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev->gflags ^= IFF_PROMISC;
    		dev_set_promiscuity(dev, inc);
    	}
    
    	/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
    	   is important. Some (broken) drivers set IFF_PROMISC, when
    	   IFF_ALLMULTI is requested not asking us and not reporting.
    	 */
    	if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
    
    		int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev->gflags ^= IFF_ALLMULTI;
    		dev_set_allmulti(dev, inc);
    	}
    
    
    	return ret;
    }
    
    void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
    {
    	unsigned int changes = dev->flags ^ old_flags;
    
    	if (changes & IFF_UP) {
    		if (dev->flags & IFF_UP)
    			call_netdevice_notifiers(NETDEV_UP, dev);
    		else
    			call_netdevice_notifiers(NETDEV_DOWN, dev);
    	}
    
    	if (dev->flags & IFF_UP &&
    	    (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
    		call_netdevice_notifiers(NETDEV_CHANGE, dev);
    }
    
    /**
     *	dev_change_flags - change device settings
     *	@dev: device
     *	@flags: device state flags
     *
     *	Change settings on device based state flags. The flags are
     *	in the userspace exported format.
     */
    int dev_change_flags(struct net_device *dev, unsigned flags)
    {
    	int ret, changes;
    	int old_flags = dev->flags;
    
    	ret = __dev_change_flags(dev, flags);
    	if (ret < 0)
    		return ret;
    
    	changes = old_flags ^ dev->flags;
    
    	if (changes)
    		rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	__dev_notify_flags(dev, old_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return ret;
    }
    
    EXPORT_SYMBOL(dev_change_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	dev_set_mtu - Change maximum transfer unit
     *	@dev: device
     *	@new_mtu: new transfer unit
     *
     *	Change the maximum transfer size of the network device.
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int dev_set_mtu(struct net_device *dev, int new_mtu)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int err;
    
    	if (new_mtu == dev->mtu)
    		return 0;
    
    	/*	MTU must be positive.	 */
    	if (new_mtu < 0)
    		return -EINVAL;
    
    	if (!netif_device_present(dev))
    		return -ENODEV;
    
    	err = 0;
    
    	if (ops->ndo_change_mtu)
    		err = ops->ndo_change_mtu(dev, new_mtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	else
    		dev->mtu = new_mtu;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!err && dev->flags & IFF_UP)
    
    		call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return err;
    }
    
    EXPORT_SYMBOL(dev_set_mtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	dev_set_mac_address - Change Media Access Control Address
     *	@dev: device
     *	@sa: new address
     *
     *	Change the hardware (MAC) address of the device
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int err;
    
    
    	if (!ops->ndo_set_mac_address)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EOPNOTSUPP;
    	if (sa->sa_family != dev->type)
    		return -EINVAL;
    	if (!netif_device_present(dev))
    		return -ENODEV;
    
    	err = ops->ndo_set_mac_address(dev, sa);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!err)
    
    		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return err;
    }
    
    EXPORT_SYMBOL(dev_set_mac_address);
    
     *	Perform the SIOCxIFxxx calls, inside rcu_read_lock()
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int err;
    
    	struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (!dev)
    		return -ENODEV;
    
    	switch (cmd) {
    
    	case SIOCGIFFLAGS:	/* Get interface flags */
    		ifr->ifr_flags = (short) dev_get_flags(dev);
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCGIFMETRIC:	/* Get the metric on the interface
    				   (currently unused) */
    		ifr->ifr_metric = 0;
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCGIFMTU:	/* Get the MTU of a device */
    		ifr->ifr_mtu = dev->mtu;
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCGIFHWADDR:
    		if (!dev->addr_len)
    			memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
    		else
    			memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
    			       min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
    		ifr->ifr_hwaddr.sa_family = dev->type;
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCGIFSLAVE:
    		err = -EINVAL;
    		break;
    
    	case SIOCGIFMAP:
    		ifr->ifr_map.mem_start = dev->mem_start;
    		ifr->ifr_map.mem_end   = dev->mem_end;
    		ifr->ifr_map.base_addr = dev->base_addr;
    		ifr->ifr_map.irq       = dev->irq;
    		ifr->ifr_map.dma       = dev->dma;
    		ifr->ifr_map.port      = dev->if_port;
    		return 0;
    
    	case SIOCGIFINDEX:
    		ifr->ifr_ifindex = dev->ifindex;
    		return 0;
    
    	case SIOCGIFTXQLEN:
    		ifr->ifr_qlen = dev->tx_queue_len;
    		return 0;
    
    	default:
    		/* dev_ioctl() should ensure this case
    		 * is never reached
    		 */
    		WARN_ON(1);
    		err = -EINVAL;
    		break;
    
    
    	}
    	return err;
    }
    
    /*
     *	Perform the SIOCxIFxxx calls, inside rtnl_lock()
     */
    static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
    {
    	int err;
    	struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
    
    	const struct net_device_ops *ops;
    
    	ops = dev->netdev_ops;
    
    
    	case SIOCSIFFLAGS:	/* Set interface flags */
    		return dev_change_flags(dev, ifr->ifr_flags);
    
    	case SIOCSIFMETRIC:	/* Set the metric on the interface
    				   (currently unused) */
    		return -EOPNOTSUPP;
    
    	case SIOCSIFMTU:	/* Set the MTU of a device */
    		return dev_set_mtu(dev, ifr->ifr_mtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCSIFHWADDR:
    		return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCSIFHWBROADCAST:
    		if (ifr->ifr_hwaddr.sa_family != dev->type)
    			return -EINVAL;
    		memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
    		       min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
    		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCSIFMAP:
    		if (ops->ndo_set_config) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			if (!netif_device_present(dev))
    				return -ENODEV;
    
    			return ops->ndo_set_config(dev, &ifr->ifr_map);
    		}
    		return -EOPNOTSUPP;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCADDMULTI:
    		if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
    		    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
    			return -EINVAL;
    		if (!netif_device_present(dev))
    			return -ENODEV;
    
    		return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
    
    
    	case SIOCDELMULTI:
    		if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
    		    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
    			return -EINVAL;
    		if (!netif_device_present(dev))
    			return -ENODEV;
    
    		return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCSIFTXQLEN:
    		if (ifr->ifr_qlen < 0)
    			return -EINVAL;
    		dev->tx_queue_len = ifr->ifr_qlen;
    		return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCSIFNAME:
    		ifr->ifr_newname[IFNAMSIZ-1] = '\0';
    		return dev_change_name(dev, ifr->ifr_newname);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/*
    	 *	Unknown or private ioctl
    	 */
    	default:
    		if ((cmd >= SIOCDEVPRIVATE &&
    		    cmd <= SIOCDEVPRIVATE + 15) ||
    		    cmd == SIOCBONDENSLAVE ||
    		    cmd == SIOCBONDRELEASE ||
    		    cmd == SIOCBONDSETHWADDR ||
    		    cmd == SIOCBONDSLAVEINFOQUERY ||
    		    cmd == SIOCBONDINFOQUERY ||
    		    cmd == SIOCBONDCHANGEACTIVE ||
    		    cmd == SIOCGMIIPHY ||
    		    cmd == SIOCGMIIREG ||
    		    cmd == SIOCSMIIREG ||
    		    cmd == SIOCBRADDIF ||
    		    cmd == SIOCBRDELIF ||
    		    cmd == SIOCSHWTSTAMP ||
    		    cmd == SIOCWANDEV) {
    			err = -EOPNOTSUPP;
    			if (ops->ndo_do_ioctl) {
    				if (netif_device_present(dev))
    					err = ops->ndo_do_ioctl(dev, ifr, cmd);
    				else
    					err = -ENODEV;
    			}
    		} else
    			err = -EINVAL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	}
    	return err;
    }
    
    /*
     *	This function handles all "interface"-type I/O control requests. The actual
     *	'doing' part of this is dev_ifsioc above.
     */
    
    /**
     *	dev_ioctl	-	network device ioctl
    
     *	@net: the applicable net namespace
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	@cmd: command to issue
     *	@arg: pointer to a struct ifreq in user space
     *
     *	Issue ioctl functions to devices. This is normally called by the
     *	user space syscall interfaces but can sometimes be useful for
     *	other purposes. The return value is the return from the syscall if
     *	positive or a negative errno code on error.
     */
    
    
    int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct ifreq ifr;
    	int ret;
    	char *colon;
    
    	/* One special case: SIOCGIFCONF takes ifconf argument
    	   and requires shared lock, because it sleeps writing
    	   to user space.
    	 */
    
    	if (cmd == SIOCGIFCONF) {
    
    		rtnl_lock();
    
    		ret = dev_ifconf(net, (char __user *) arg);
    
    		rtnl_unlock();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return ret;
    	}
    	if (cmd == SIOCGIFNAME)
    
    		return dev_ifname(net, (struct ifreq __user *)arg);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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;
    
    	/*
    	 *	See which interface the caller is talking about.
    	 */
    
    	switch (cmd) {
    
    	/*
    	 *	These ioctl calls:
    	 *	- can be done by all.
    	 *	- atomic and do not require locking.
    	 *	- return a value
    	 */
    	case SIOCGIFFLAGS:
    	case SIOCGIFMETRIC:
    	case SIOCGIFMTU:
    	case SIOCGIFHWADDR:
    	case SIOCGIFSLAVE:
    	case SIOCGIFMAP:
    	case SIOCGIFINDEX:
    	case SIOCGIFTXQLEN:
    		dev_load(net, ifr.ifr_name);
    
    		rcu_read_lock();
    
    		ret = dev_ifsioc_locked(net, &ifr, cmd);
    
    		rcu_read_unlock();
    
    		if (!ret) {
    			if (colon)
    				*colon = ':';
    			if (copy_to_user(arg, &ifr,
    					 sizeof(struct ifreq)))
    				ret = -EFAULT;
    		}
    		return ret;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	case SIOCETHTOOL:
    		dev_load(net, ifr.ifr_name);
    		rtnl_lock();
    		ret = dev_ethtool(net, &ifr);
    		rtnl_unlock();
    		if (!ret) {
    			if (colon)
    				*colon = ':';
    			if (copy_to_user(arg, &ifr,
    					 sizeof(struct ifreq)))
    				ret = -EFAULT;
    		}
    		return ret;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/*
    	 *	These ioctl calls:
    	 *	- require superuser power.
    	 *	- require strict serialization.
    	 *	- return a value
    	 */
    	case SIOCGMIIPHY:
    	case SIOCGMIIREG:
    	case SIOCSIFNAME:
    		if (!capable(CAP_NET_ADMIN))
    			return -EPERM;
    		dev_load(net, ifr.ifr_name);
    		rtnl_lock();
    		ret = dev_ifsioc(net, &ifr, cmd);
    		rtnl_unlock();
    		if (!ret) {
    			if (colon)
    				*colon = ':';
    			if (copy_to_user(arg, &ifr,
    					 sizeof(struct ifreq)))
    				ret = -EFAULT;
    		}
    		return ret;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/*
    	 *	These ioctl calls:
    	 *	- require superuser power.
    	 *	- require strict serialization.
    	 *	- do not return a value
    	 */
    	case SIOCSIFFLAGS:
    	case SIOCSIFMETRIC:
    	case SIOCSIFMTU:
    	case SIOCSIFMAP:
    	case SIOCSIFHWADDR:
    	case SIOCSIFSLAVE:
    	case SIOCADDMULTI:
    	case SIOCDELMULTI:
    	case SIOCSIFHWBROADCAST:
    	case SIOCSIFTXQLEN:
    	case SIOCSMIIREG:
    	case SIOCBONDENSLAVE:
    	case SIOCBONDRELEASE:
    	case SIOCBONDSETHWADDR:
    	case SIOCBONDCHANGEACTIVE:
    	case SIOCBRADDIF:
    	case SIOCBRDELIF:
    	case SIOCSHWTSTAMP:
    		if (!capable(CAP_NET_ADMIN))
    			return -EPERM;
    		/* fall through */
    	case SIOCBONDSLAVEINFOQUERY:
    	case SIOCBONDINFOQUERY:
    		dev_load(net, ifr.ifr_name);
    		rtnl_lock();
    		ret = dev_ifsioc(net, &ifr, cmd);
    		rtnl_unlock();
    		return ret;
    
    	case SIOCGIFMEM:
    		/* Get the per device memory space. We can add this but
    		 * currently do not support it */
    	case SIOCSIFMEM:
    		/* Set the per device memory buffer space.
    		 * Not applicable in our case */
    	case SIOCSIFLINK:
    		return -EINVAL;
    
    	/*
    	 *	Unknown or private ioctl.
    	 */
    	default:
    		if (cmd == SIOCWANDEV ||
    		    (cmd >= SIOCDEVPRIVATE &&
    		     cmd <= SIOCDEVPRIVATE + 15)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			rtnl_lock();
    
    			ret = dev_ifsioc(net, &ifr, cmd);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			rtnl_unlock();
    
    			if (!ret && copy_to_user(arg, &ifr,
    						 sizeof(struct ifreq)))
    				ret = -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return ret;
    
    		}
    		/* Take care of Wireless Extensions */
    		if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
    			return wext_handle_ioctl(net, &ifr, cmd, arg);
    		return -EINVAL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    }
    
    
    /**
     *	dev_new_index	-	allocate an ifindex
    
     *	@net: the applicable net namespace
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	Returns a suitable unique value for a new device interface
     *	number.  The caller must hold the rtnl semaphore or the
     *	dev_base_lock to be sure it remains unique.
     */
    
    static int dev_new_index(struct net *net)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	static int ifindex;
    	for (;;) {
    		if (++ifindex <= 0)
    			ifindex = 1;
    
    		if (!__dev_get_by_index(net, ifindex))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return ifindex;
    	}
    }
    
    /* Delayed registration/unregisteration */
    
    static LIST_HEAD(net_todo_list);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static void net_set_todo(struct net_device *dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	list_add_tail(&dev->todo_list, &net_todo_list);
    }
    
    
    static void rollback_registered_many(struct list_head *head)
    
    	struct net_device *dev, *tmp;
    
    	BUG_ON(dev_boot_phase);
    	ASSERT_RTNL();
    
    
    	list_for_each_entry_safe(dev, tmp, head, unreg_list) {
    
    		/* Some devices call without registering
    
    		 * for initialization unwind. Remove those
    		 * devices and proceed with the remaining.
    
    		 */
    		if (dev->reg_state == NETREG_UNINITIALIZED) {
    			pr_debug("unregister_netdevice: device %s/%p never "
    				 "was registered\n", dev->name, dev);
    
    			list_del(&dev->unreg_list);
    			continue;
    
    		BUG_ON(dev->reg_state != NETREG_REGISTERED);
    
    		/* If device is running, close it first. */
    		dev_close(dev);
    
    		/* And unlink it from device chain. */
    		unlist_netdevice(dev);
    
    		dev->reg_state = NETREG_UNREGISTERING;
    	}
    
    	list_for_each_entry(dev, head, unreg_list) {
    		/* Shutdown queueing discipline. */
    		dev_shutdown(dev);
    
    		/* Notify protocols, that we are about to destroy
    		   this device. They should clean all the things.
    		*/
    		call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
    
    		if (!dev->rtnl_link_ops ||
    		    dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
    			rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
    
    
    		/*
    		 *	Flush the unicast and multicast chains
    		 */
    
    		dev_mc_flush(dev);
    
    		if (dev->netdev_ops->ndo_uninit)
    			dev->netdev_ops->ndo_uninit(dev);
    
    		/* Notifier chain MUST detach us from master device. */
    		WARN_ON(dev->master);
    
    		/* Remove entries from kobject tree */
    		netdev_unregister_kobject(dev);
    	}
    
    	/* Process any work delayed until the end of the batch */
    
    	dev = list_first_entry(head, struct net_device, unreg_list);
    
    	call_netdevice_notifiers(NETDEV_UNREGISTER_BATCH, dev);
    
    	list_for_each_entry(dev, head, unreg_list)
    
    		dev_put(dev);
    }
    
    static void rollback_registered(struct net_device *dev)
    {
    	LIST_HEAD(single);
    
    	list_add(&dev->unreg_list, &single);
    	rollback_registered_many(&single);
    
    static void __netdev_init_queue_locks_one(struct net_device *dev,
    					  struct netdev_queue *dev_queue,
    					  void *_unused)
    
    {
    	spin_lock_init(&dev_queue->_xmit_lock);
    
    	netdev_set_xmit_lockdep_class(&dev_queue->_xmit_lock, dev->type);
    
    	dev_queue->xmit_lock_owner = -1;
    }
    
    static void netdev_init_queue_locks(struct net_device *dev)
    {
    
    	netdev_for_each_tx_queue(dev, __netdev_init_queue_locks_one, NULL);
    	__netdev_init_queue_locks_one(dev, &dev->rx_queue, NULL);
    
    unsigned long netdev_fix_features(unsigned long features, const char *name)
    {
    	/* Fix illegal SG+CSUM combinations. */
    	if ((features & NETIF_F_SG) &&
    	    !(features & NETIF_F_ALL_CSUM)) {
    		if (name)
    			printk(KERN_NOTICE "%s: Dropping NETIF_F_SG since no "
    			       "checksum feature.\n", name);
    		features &= ~NETIF_F_SG;
    	}
    
    	/* TSO requires that SG is present as well. */
    	if ((features & NETIF_F_TSO) && !(features & NETIF_F_SG)) {
    		if (name)
    			printk(KERN_NOTICE "%s: Dropping NETIF_F_TSO since no "
    			       "SG feature.\n", name);
    		features &= ~NETIF_F_TSO;
    	}
    
    	if (features & NETIF_F_UFO) {
    		if (!(features & NETIF_F_GEN_CSUM)) {
    			if (name)
    				printk(KERN_ERR "%s: Dropping NETIF_F_UFO "
    				       "since no NETIF_F_HW_CSUM feature.\n",
    				       name);
    			features &= ~NETIF_F_UFO;
    		}
    
    		if (!(features & NETIF_F_SG)) {
    			if (name)
    				printk(KERN_ERR "%s: Dropping NETIF_F_UFO "
    				       "since no NETIF_F_SG feature.\n", name);
    			features &= ~NETIF_F_UFO;
    		}
    	}
    
    	return features;
    }
    EXPORT_SYMBOL(netdev_fix_features);
    
    
    /**
     *	netif_stacked_transfer_operstate -	transfer operstate
     *	@rootdev: the root or lower level device to transfer state from
     *	@dev: the device to transfer operstate to
     *
     *	Transfer operational state from root to device. This is normally
     *	called when a stacking relationship exists between the root
     *	device and the device(a leaf device).
     */
    void netif_stacked_transfer_operstate(const struct net_device *rootdev,
    					struct net_device *dev)
    {
    	if (rootdev->operstate == IF_OPER_DORMANT)
    		netif_dormant_on(dev);
    	else
    		netif_dormant_off(dev);
    
    	if (netif_carrier_ok(rootdev)) {
    		if (!netif_carrier_ok(dev))
    			netif_carrier_on(dev);
    	} else {
    		if (netif_carrier_ok(dev))
    			netif_carrier_off(dev);
    	}
    }
    EXPORT_SYMBOL(netif_stacked_transfer_operstate);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	register_netdevice	- register a network device
     *	@dev: device to register
     *
     *	Take a completed network device structure and add it to the kernel
     *	interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
     *	chain. 0 is returned on success. A negative errno code is returned
     *	on a failure to set up the device, or if the name is a duplicate.
     *
     *	Callers must hold the rtnl semaphore. You may want
     *	register_netdev() instead of this.
     *
     *	BUGS:
     *	The locking appears insufficient to guarantee two parallel registers
     *	will not get the same name.
     */
    
    int register_netdevice(struct net_device *dev)
    {
    	int ret;
    
    	struct net *net = dev_net(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	BUG_ON(dev_boot_phase);
    	ASSERT_RTNL();
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* When net_device's are persistent, this will be fatal. */
    	BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	spin_lock_init(&dev->addr_list_lock);
    
    	netdev_set_addr_lockdep_class(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	dev->iflink = -1;
    
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    #ifdef CONFIG_RPS
    
    	if (!dev->num_rx_queues) {
    		/*
    		 * Allocate a single RX queue if driver never called
    		 * alloc_netdev_mq
    		 */
    
    		dev->_rx = kzalloc(sizeof(struct netdev_rx_queue), GFP_KERNEL);
    		if (!dev->_rx) {
    			ret = -ENOMEM;
    			goto out;
    		}
    
    		dev->_rx->first = dev->_rx;
    		atomic_set(&dev->_rx->count, 1);
    		dev->num_rx_queues = 1;
    	}
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    #endif
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* Init, if this function is available */
    
    	if (dev->netdev_ops->ndo_init) {
    		ret = dev->netdev_ops->ndo_init(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (ret) {
    			if (ret > 0)
    				ret = -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    	}
    
    	ret = dev_get_valid_name(dev, dev->name, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	dev->ifindex = dev_new_index(net);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (dev->iflink == -1)
    		dev->iflink = dev->ifindex;
    
    
    	/* Fix illegal checksum combinations */
    	if ((dev->features & NETIF_F_HW_CSUM) &&
    	    (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
    		printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n",
    		       dev->name);
    		dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
    	}