]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/tun.c
Merge branch 'master' of hera.kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6
[linux-2.6-omap-h63xx.git] / drivers / net / tun.c
index 5643d1e84ed6752d8dc12164423354ba0725aa70..1f76446959765ef577382517e7039c0ab044d5c3 100644 (file)
 /*
  *  Changes:
  *
+ *  Brian Braunstein <linuxkernel@bristyle.com> 2007/03/23
+ *    Fixed hw address handling.  Now net_device.dev_addr is kept consistent
+ *    with tun.dev_addr when the address is set by this module.
+ *
  *  Mike Kershaw <dragorn@kismetwireless.net> 2005/08/14
  *    Add TUNSETLINK ioctl to set the link encapsulation
  *
@@ -58,6 +62,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_tun.h>
 #include <linux/crc32.h>
+#include <net/net_namespace.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -105,7 +110,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
 
                        /* We won't see all dropped packets individually, so overrun
                         * error is more appropriate. */
-                       tun->stats.tx_fifo_errors++;
+                       dev->stats.tx_fifo_errors++;
                } else {
                        /* Single queue mode.
                         * Driver handles dropping of all packets itself. */
@@ -124,7 +129,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        return 0;
 
 drop:
-       tun->stats.tx_dropped++;
+       dev->stats.tx_dropped++;
        kfree_skb(skb);
        return 0;
 }
@@ -154,23 +159,28 @@ tun_net_mclist(struct net_device *dev)
        struct tun_struct *tun = netdev_priv(dev);
        const struct dev_mc_list *mclist;
        int i;
+       DECLARE_MAC_BUF(mac);
        DBG(KERN_DEBUG "%s: tun_net_mclist: mc_count %d\n",
                        dev->name, dev->mc_count);
        memset(tun->chr_filter, 0, sizeof tun->chr_filter);
        for (i = 0, mclist = dev->mc_list; i < dev->mc_count && mclist != NULL;
                        i++, mclist = mclist->next) {
                add_multi(tun->net_filter, mclist->dmi_addr);
-               DBG(KERN_DEBUG "%s: tun_net_mclist: %x:%x:%x:%x:%x:%x\n",
-                               dev->name,
-                               mclist->dmi_addr[0], mclist->dmi_addr[1], mclist->dmi_addr[2],
-                               mclist->dmi_addr[3], mclist->dmi_addr[4], mclist->dmi_addr[5]);
+               DBG(KERN_DEBUG "%s: tun_net_mclist: %s\n",
+                   dev->name, print_mac(mac, mclist->dmi_addr));
        }
 }
 
-static struct net_device_stats *tun_net_stats(struct net_device *dev)
+#define MIN_MTU 68
+#define MAX_MTU 65535
+
+static int
+tun_net_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct tun_struct *tun = netdev_priv(dev);
-       return &tun->stats;
+       if (new_mtu < MIN_MTU || new_mtu + dev->hard_header_len > MAX_MTU)
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
 }
 
 /* Initialize net device. */
@@ -184,6 +194,7 @@ static void tun_net_init(struct net_device *dev)
                dev->hard_header_len = 0;
                dev->addr_len = 0;
                dev->mtu = 1500;
+               dev->change_mtu = tun_net_change_mtu;
 
                /* Zero header length */
                dev->type = ARPHRD_NONE;
@@ -196,7 +207,11 @@ static void tun_net_init(struct net_device *dev)
                dev->set_multicast_list = tun_net_mclist;
 
                ether_setup(dev);
-               random_ether_addr(dev->dev_addr);
+               dev->change_mtu = tun_net_change_mtu;
+
+               /* random address already created for us by tun_set_iff, use it */
+               memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) );
+
                dev->tx_queue_len = TUN_READQ_SIZE;  /* We prefer our own queue length */
                break;
        }
@@ -242,23 +257,23 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
                align = NET_IP_ALIGN;
 
        if (!(skb = alloc_skb(len + align, GFP_KERNEL))) {
-               tun->stats.rx_dropped++;
+               tun->dev->stats.rx_dropped++;
                return -ENOMEM;
        }
 
        if (align)
                skb_reserve(skb, align);
        if (memcpy_fromiovec(skb_put(skb, len), iv, len)) {
-               tun->stats.rx_dropped++;
+               tun->dev->stats.rx_dropped++;
                kfree_skb(skb);
                return -EFAULT;
        }
 
-       skb->dev = tun->dev;
        switch (tun->flags & TUN_TYPE_MASK) {
        case TUN_TUN_DEV:
-               skb->mac.raw = skb->data;
+               skb_reset_mac_header(skb);
                skb->protocol = pi.proto;
+               skb->dev = tun->dev;
                break;
        case TUN_TAP_DEV:
                skb->protocol = eth_type_trans(skb, tun->dev);
@@ -271,8 +286,8 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
        netif_rx_ni(skb);
        tun->dev->last_rx = jiffies;
 
-       tun->stats.rx_packets++;
-       tun->stats.rx_bytes += len;
+       tun->dev->stats.rx_packets++;
+       tun->dev->stats.rx_bytes += len;
 
        return count;
 }
@@ -328,8 +343,8 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
        skb_copy_datagram_iovec(skb, 0, iv, len);
        total += len;
 
-       tun->stats.tx_packets++;
-       tun->stats.tx_bytes += len;
+       tun->dev->stats.tx_packets++;
+       tun->dev->stats.tx_bytes += len;
 
        return total;
 }
@@ -342,6 +357,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
        DECLARE_WAITQUEUE(wait, current);
        struct sk_buff *skb;
        ssize_t len, ret = 0;
+       DECLARE_MAC_BUF(mac);
 
        if (!tun)
                return -EBADFD;
@@ -386,8 +402,8 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                 *   - we are multicast promiscous.
                 *   - we belong to the multicast group.
                 */
-               memcpy(addr, skb->data,
-                      min_t(size_t, sizeof addr, skb->len));
+               skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr,
+                                                                  skb->len));
                bit_nr = ether_crc(sizeof addr, addr) >> 26;
                if ((tun->if_flags & IFF_PROMISC) ||
                                memcmp(addr, tun->dev_addr, sizeof addr) == 0 ||
@@ -396,16 +412,14 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                                  (addr[0] == 0x33 && addr[1] == 0x33)) &&
                                 ((tun->if_flags & IFF_ALLMULTI) ||
                                  (tun->chr_filter[bit_nr >> 5] & (1 << (bit_nr & 31)))))) {
-                       DBG(KERN_DEBUG "%s: tun_chr_readv: accepted: %x:%x:%x:%x:%x:%x\n",
-                                       tun->dev->name, addr[0], addr[1], addr[2],
-                                       addr[3], addr[4], addr[5]);
+                       DBG(KERN_DEBUG "%s: tun_chr_readv: accepted: %s\n",
+                                       tun->dev->name, print_mac(mac, addr));
                        ret = tun_put_user(tun, skb, (struct iovec *) iv, len);
                        kfree_skb(skb);
                        break;
                } else {
-                       DBG(KERN_DEBUG "%s: tun_chr_readv: rejected: %x:%x:%x:%x:%x:%x\n",
-                                       tun->dev->name, addr[0], addr[1], addr[2],
-                                       addr[3], addr[4], addr[5]);
+                       DBG(KERN_DEBUG "%s: tun_chr_readv: rejected: %s\n",
+                                       tun->dev->name, print_mac(mac, addr));
                        kfree_skb(skb);
                        continue;
                }
@@ -425,12 +439,11 @@ static void tun_setup(struct net_device *dev)
        init_waitqueue_head(&tun->read_wait);
 
        tun->owner = -1;
+       tun->group = -1;
 
-       SET_MODULE_OWNER(dev);
        dev->open = tun_net_open;
        dev->hard_start_xmit = tun_net_xmit;
        dev->stop = tun_net_close;
-       dev->get_stats = tun_net_stats;
        dev->ethtool_ops = &tun_ethtool_ops;
        dev->destructor = free_netdev;
 }
@@ -460,11 +473,14 @@ static int tun_set_iff(struct file *file, struct ifreq *ifr)
                        return -EBUSY;
 
                /* Check permissions */
-               if (tun->owner != -1 &&
-                   current->euid != tun->owner && !capable(CAP_NET_ADMIN))
+               if (((tun->owner != -1 &&
+                     current->euid != tun->owner) ||
+                    (tun->group != -1 &&
+                     current->egid != tun->group)) &&
+                    !capable(CAP_NET_ADMIN))
                        return -EPERM;
        }
-       else if (__dev_get_by_name(ifr->ifr_name))
+       else if (__dev_get_by_name(&init_net, ifr->ifr_name))
                return -EINVAL;
        else {
                char *name;
@@ -546,6 +562,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
        struct tun_struct *tun = file->private_data;
        void __user* argp = (void __user*)arg;
        struct ifreq ifr;
+       DECLARE_MAC_BUF(mac);
 
        if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
                if (copy_from_user(&ifr, argp, sizeof ifr))
@@ -603,6 +620,13 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
                DBG(KERN_INFO "%s: owner set to %d\n", tun->dev->name, tun->owner);
                break;
 
+       case TUNSETGROUP:
+               /* Set group of the device */
+               tun->group= (gid_t) arg;
+
+               DBG(KERN_INFO "%s: group set to %d\n", tun->dev->name, tun->group);
+               break;
+
        case TUNSETLINK:
                /* Only allow setting the type when the interface is down */
                if (tun->dev->flags & IFF_UP) {
@@ -636,6 +660,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
                return 0;
 
        case SIOCGIFHWADDR:
+               /* Note: the actual net device's address may be different */
                memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr,
                                min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
                if (copy_to_user( argp, &ifr, sizeof ifr))
@@ -643,37 +668,39 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
                return 0;
 
        case SIOCSIFHWADDR:
-               /** Set the character device's hardware address. This is used when
-                * filtering packets being sent from the network device to the character
-                * device. */
-               memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
-                               min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
-               DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
-                               tun->dev->name,
-                               tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
-                               tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
-               return 0;
+       {
+               /* try to set the actual net device's hw address */
+               int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+
+               if (ret == 0) {
+                       /** Set the character device's hardware address. This is used when
+                        * filtering packets being sent from the network device to the character
+                        * device. */
+                       memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
+                                       min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
+                       DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
+                                       tun->dev->name,
+                                       tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
+                                       tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
+               }
+
+               return  ret;
+       }
 
        case SIOCADDMULTI:
                /** Add the specified group to the character device's multicast filter
                 * list. */
                add_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);
-               DBG(KERN_DEBUG "%s: add multi: %x:%x:%x:%x:%x:%x\n",
-                               tun->dev->name,
-                               (u8)ifr.ifr_hwaddr.sa_data[0], (u8)ifr.ifr_hwaddr.sa_data[1],
-                               (u8)ifr.ifr_hwaddr.sa_data[2], (u8)ifr.ifr_hwaddr.sa_data[3],
-                               (u8)ifr.ifr_hwaddr.sa_data[4], (u8)ifr.ifr_hwaddr.sa_data[5]);
+               DBG(KERN_DEBUG "%s: add multi: %s\n",
+                   tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));
                return 0;
 
        case SIOCDELMULTI:
                /** Remove the specified group from the character device's multicast
                 * filter list. */
                del_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data);
-               DBG(KERN_DEBUG "%s: del multi: %x:%x:%x:%x:%x:%x\n",
-                               tun->dev->name,
-                               (u8)ifr.ifr_hwaddr.sa_data[0], (u8)ifr.ifr_hwaddr.sa_data[1],
-                               (u8)ifr.ifr_hwaddr.sa_data[2], (u8)ifr.ifr_hwaddr.sa_data[3],
-                               (u8)ifr.ifr_hwaddr.sa_data[4], (u8)ifr.ifr_hwaddr.sa_data[5]);
+               DBG(KERN_DEBUG "%s: del multi: %s\n",
+                   tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));
                return 0;
 
        default: