tun: socket filter support
[safe/jmp/linux-2.6] / drivers / net / tun.c
index 5adb3d1..ce1efa4 100644 (file)
@@ -61,6 +61,7 @@
 #include <linux/crc32.h>
 #include <linux/nsproxy.h>
 #include <linux/virtio_net.h>
+#include <linux/rcupdate.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 #include <net/rtnetlink.h>
@@ -366,6 +367,10 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!check_filter(&tun->txflt, skb))
                goto drop;
 
+       if (tun->socket.sk->sk_filter &&
+           sk_filter(tun->socket.sk, skb))
+               goto drop;
+
        if (skb_queue_len(&tun->socket.sk->sk_receive_queue) >= dev->tx_queue_len) {
                if (!(tun->flags & TUN_ONE_QUEUE)) {
                        /* Normal queueing mode. */
@@ -1162,6 +1167,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
        struct tun_file *tfile = file->private_data;
        struct tun_struct *tun;
        void __user* argp = (void __user*)arg;
+       struct sock_fprog fprog;
        struct ifreq ifr;
        int sndbuf;
        int ret;
@@ -1309,6 +1315,26 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                tun->socket.sk->sk_sndbuf = sndbuf;
                break;
 
+       case TUNATTACHFILTER:
+               /* Can be set only for TAPs */
+               ret = -EINVAL;
+               if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
+                       break;
+               ret = -EFAULT;
+               if (copy_from_user(&fprog, argp, sizeof(fprog)))
+                       break;
+
+               ret = sk_attach_filter(&fprog, tun->socket.sk);
+               break;
+
+       case TUNDETACHFILTER:
+               /* Can be set only for TAPs */
+               ret = -EINVAL;
+               if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
+                       break;
+               ret = sk_detach_filter(tun->socket.sk);
+               break;
+
        default:
                ret = -EINVAL;
                break;