Skip to content
Snippets Groups Projects
dev.c 160 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    		for (i = 0; i < NPROTO; i++) {
    			if (gifconf_list[i]) {
    				int done;
    				if (!pos)
    					done = gifconf_list[i](dev, NULL, 0);
    				else
    					done = gifconf_list[i](dev, pos + total,
    							       len - total);
    				if (done < 0)
    					return -EFAULT;
    				total += done;
    			}
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 *	All done.  Write the updated control block back to the caller.
    	 */
    	ifc.ifc_len = total;
    
    	/*
    	 * 	Both BSD and Solaris return 0 here, so we do too.
    	 */
    	return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0;
    }
    
    #ifdef CONFIG_PROC_FS
    
    #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1)
    
    
    #define get_bucket(x) ((x) >> BUCKET_SPACE)
    #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
    #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
    
    
    static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
    
    {
    	struct net *net = seq_file_net(seq);
    	struct net_device *dev;
    	struct hlist_node *p;
    	struct hlist_head *h;
    
    	unsigned int count = 0, offset = get_offset(*pos);
    
    	h = &net->dev_name_head[get_bucket(*pos)];
    
    	hlist_for_each_entry_rcu(dev, p, h, name_hlist) {
    
    		if (++count == offset)
    
    static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos)
    
    {
    	struct net_device *dev;
    	unsigned int bucket;
    
    	do {
    
    		dev = dev_from_same_bucket(seq, pos);
    
    		bucket = get_bucket(*pos) + 1;
    		*pos = set_bucket_offset(bucket, 1);
    
    	} while (bucket < NETDEV_HASHENTRIES);
    
    	return NULL;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     *	This is invoked by the /proc filesystem handler to display a device
     *	in detail.
     */
    
    void *dev_seq_start(struct seq_file *seq, loff_t *pos)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!*pos)
    		return SEQ_START_TOKEN;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (get_bucket(*pos) >= NETDEV_HASHENTRIES)
    
    		return NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	return dev_from_bucket(seq, pos);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    
    	return dev_from_bucket(seq, pos);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    void dev_seq_stop(struct seq_file *seq, void *v)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	rcu_read_unlock();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
    {
    
    	struct rtnl_link_stats64 temp;
    	const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
    		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
    
    		   dev->name, stats->rx_bytes, stats->rx_packets,
    		   stats->rx_errors,
    		   stats->rx_dropped + stats->rx_missed_errors,
    		   stats->rx_fifo_errors,
    		   stats->rx_length_errors + stats->rx_over_errors +
    		    stats->rx_crc_errors + stats->rx_frame_errors,
    		   stats->rx_compressed, stats->multicast,
    		   stats->tx_bytes, stats->tx_packets,
    		   stats->tx_errors, stats->tx_dropped,
    		   stats->tx_fifo_errors, stats->collisions,
    		   stats->tx_carrier_errors +
    		    stats->tx_aborted_errors +
    		    stats->tx_window_errors +
    		    stats->tx_heartbeat_errors,
    		   stats->tx_compressed);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     *	Called from the PROCfs module. This now uses the new arbitrary sized
     *	/proc/net interface to create /proc/net/dev
     */
    static int dev_seq_show(struct seq_file *seq, void *v)
    {
    	if (v == SEQ_START_TOKEN)
    		seq_puts(seq, "Inter-|   Receive                            "
    			      "                    |  Transmit\n"
    			      " face |bytes    packets errs drop fifo frame "
    			      "compressed multicast|bytes    packets errs "
    			      "drop fifo colls carrier compressed\n");
    	else
    		dev_seq_printf_stats(seq, v);
    	return 0;
    }
    
    
    Changli Gao's avatar
    Changli Gao committed
    static struct softnet_data *softnet_get_online(loff_t *pos)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Changli Gao's avatar
    Changli Gao committed
    	struct softnet_data *sd = NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	while (*pos < nr_cpu_ids)
    
    		if (cpu_online(*pos)) {
    
    Changli Gao's avatar
    Changli Gao committed
    			sd = &per_cpu(softnet_data, *pos);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    		} else
    			++*pos;
    
    Changli Gao's avatar
    Changli Gao committed
    	return sd;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
    {
    	return softnet_get_online(pos);
    }
    
    static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    	++*pos;
    	return softnet_get_online(pos);
    }
    
    static void softnet_seq_stop(struct seq_file *seq, void *v)
    {
    }
    
    static int softnet_seq_show(struct seq_file *seq, void *v)
    {
    
    Changli Gao's avatar
    Changli Gao committed
    	struct softnet_data *sd = v;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
    
    Changli Gao's avatar
    Changli Gao committed
    		   sd->processed, sd->dropped, sd->time_squeeze, 0,
    
    		   0, 0, 0, 0, /* was fastroute */
    
    Changli Gao's avatar
    Changli Gao committed
    		   sd->cpu_collision, sd->received_rps);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    
    
    static const struct seq_operations dev_seq_ops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.start = dev_seq_start,
    	.next  = dev_seq_next,
    	.stop  = dev_seq_stop,
    	.show  = dev_seq_show,
    };
    
    static int dev_seq_open(struct inode *inode, struct file *file)
    {
    
    	return seq_open_net(inode, file, &dev_seq_ops,
    
    			    sizeof(struct seq_net_private));
    
    static const struct file_operations dev_seq_fops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.owner	 = THIS_MODULE,
    	.open    = dev_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    
    static const struct seq_operations softnet_seq_ops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.start = softnet_seq_start,
    	.next  = softnet_seq_next,
    	.stop  = softnet_seq_stop,
    	.show  = softnet_seq_show,
    };
    
    static int softnet_seq_open(struct inode *inode, struct file *file)
    {
    	return seq_open(file, &softnet_seq_ops);
    }
    
    
    static const struct file_operations softnet_seq_fops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.owner	 = THIS_MODULE,
    	.open    = softnet_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    	.release = seq_release,
    };
    
    
    static void *ptype_get_idx(loff_t pos)
    {
    	struct packet_type *pt = NULL;
    	loff_t i = 0;
    	int t;
    
    	list_for_each_entry_rcu(pt, &ptype_all, list) {
    		if (i == pos)
    			return pt;
    		++i;
    	}
    
    
    	for (t = 0; t < PTYPE_HASH_SIZE; t++) {
    
    		list_for_each_entry_rcu(pt, &ptype_base[t], list) {
    			if (i == pos)
    				return pt;
    			++i;
    		}
    	}
    	return NULL;
    }
    
    static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
    
    {
    	rcu_read_lock();
    	return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN;
    }
    
    static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    	struct packet_type *pt;
    	struct list_head *nxt;
    	int hash;
    
    	++*pos;
    	if (v == SEQ_START_TOKEN)
    		return ptype_get_idx(0);
    
    	pt = v;
    	nxt = pt->list.next;
    	if (pt->type == htons(ETH_P_ALL)) {
    		if (nxt != &ptype_all)
    			goto found;
    		hash = 0;
    		nxt = ptype_base[0].next;
    	} else
    
    		hash = ntohs(pt->type) & PTYPE_HASH_MASK;
    
    
    	while (nxt == &ptype_base[hash]) {
    
    		if (++hash >= PTYPE_HASH_SIZE)
    
    			return NULL;
    		nxt = ptype_base[hash].next;
    	}
    found:
    	return list_entry(nxt, struct packet_type, list);
    }
    
    static void ptype_seq_stop(struct seq_file *seq, void *v)
    
    {
    	rcu_read_unlock();
    }
    
    static int ptype_seq_show(struct seq_file *seq, void *v)
    {
    	struct packet_type *pt = v;
    
    	if (v == SEQ_START_TOKEN)
    		seq_puts(seq, "Type Device      Function\n");
    
    	else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) {
    
    		if (pt->type == htons(ETH_P_ALL))
    			seq_puts(seq, "ALL ");
    		else
    			seq_printf(seq, "%04x", ntohs(pt->type));
    
    
    		seq_printf(seq, " %-8s %pF\n",
    			   pt->dev ? pt->dev->name : "", pt->func);
    
    	}
    
    	return 0;
    }
    
    static const struct seq_operations ptype_seq_ops = {
    	.start = ptype_seq_start,
    	.next  = ptype_seq_next,
    	.stop  = ptype_seq_stop,
    	.show  = ptype_seq_show,
    };
    
    static int ptype_seq_open(struct inode *inode, struct file *file)
    {
    
    	return seq_open_net(inode, file, &ptype_seq_ops,
    			sizeof(struct seq_net_private));
    
    }
    
    static const struct file_operations ptype_seq_fops = {
    	.owner	 = THIS_MODULE,
    	.open    = ptype_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    
    	.release = seq_release_net,
    
    static int __net_init dev_proc_net_init(struct net *net)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int rc = -ENOMEM;
    
    
    	if (!proc_net_fops_create(net, "dev", S_IRUGO, &dev_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out;
    
    	if (!proc_net_fops_create(net, "softnet_stat", S_IRUGO, &softnet_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out_dev;
    
    	if (!proc_net_fops_create(net, "ptype", S_IRUGO, &ptype_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	rc = 0;
    out:
    	return rc;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out_softnet:
    
    	proc_net_remove(net, "softnet_stat");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out_dev:
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	goto out;
    }
    
    static void __net_exit dev_proc_net_exit(struct net *net)
    
    {
    	wext_proc_exit(net);
    
    	proc_net_remove(net, "ptype");
    	proc_net_remove(net, "softnet_stat");
    	proc_net_remove(net, "dev");
    }
    
    
    static struct pernet_operations __net_initdata dev_proc_ops = {
    
    	.init = dev_proc_net_init,
    	.exit = dev_proc_net_exit,
    };
    
    static int __init dev_proc_init(void)
    {
    	return register_pernet_subsys(&dev_proc_ops);
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #else
    #define dev_proc_init() 0
    #endif	/* CONFIG_PROC_FS */
    
    
    /**
    
     *	netdev_set_master	-	set up master pointer
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	@slave: slave device
     *	@master: new master device
     *
     *	Changes the master device of the slave. Pass %NULL to break the
     *	bonding. The caller must hold the RTNL semaphore. On a failure
     *	a negative errno code is returned. On success the reference counts
    
     *	are adjusted and the function returns zero.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    int netdev_set_master(struct net_device *slave, struct net_device *master)
    {
    	struct net_device *old = slave->master;
    
    	ASSERT_RTNL();
    
    	if (master) {
    		if (old)
    			return -EBUSY;
    		dev_hold(master);
    	}
    
    	slave->master = master;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev_put(old);
    
    	return 0;
    }
    EXPORT_SYMBOL(netdev_set_master);
    
    /**
     *	netdev_set_bond_master	-	set up bonding master/slave pair
     *	@slave: slave device
     *	@master: new master device
     *
     *	Changes the master device of the slave. Pass %NULL to break the
     *	bonding. The caller must hold the RTNL semaphore. On a failure
     *	a negative errno code is returned. On success %RTM_NEWLINK is sent
     *	to the routing socket and the function returns zero.
     */
    int netdev_set_bond_master(struct net_device *slave, struct net_device *master)
    {
    	int err;
    
    	ASSERT_RTNL();
    
    	err = netdev_set_master(slave, master);
    	if (err)
    		return err;
    
    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_bond_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 int 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;
    
    			pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n",
    				dev->name);
    
    		pr_info("device %s %s promiscuous mode\n",
    			dev->name,
    			dev->flags & IFF_PROMISC ? "entered" : "left");
    
    		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 int 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 int old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    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;
    
    			pr_warn("%s: allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n",
    				dev->name);
    
    	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 (!(dev->priv_flags & IFF_UNICAST_FLT)) {
    
    		/* 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 = true;
    
    		} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
    
    			__dev_set_promiscuity(dev, -1);
    
    			dev->uc_promisc = false;
    
    
    	if (ops->ndo_set_rx_mode)
    		ops->ndo_set_rx_mode(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.
     */
    
    unsigned int dev_get_flags(const struct net_device *dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned int flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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
    {
    
    	unsigned 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 int flags)
    
    	int ret;
    	unsigned int changes, 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_group - Change group this device belongs to
     *	@dev: device
     *	@new_group: group this device should belong to
     */
    void dev_set_group(struct net_device *dev, int new_group)
    {
    	dev->group = new_group;
    }
    EXPORT_SYMBOL(dev_set_group);
    
    
    /**
     *	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);
    
    		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_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_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
    
    
    	case SIOCSHWTSTAMP:
    		err = net_hwtstamp_validate(ifr);
    		if (err)
    			return err;
    		/* fall through */
    
    
    	/*
    	 *	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.
    	 */