Fix AF_PACKET ABI breakage in 4.2
[deliverable/linux.git] / net / packet / af_packet.c
index ed458b315ef4153233f65fa2af0bb0db55225bf8..aa4b15c3588445ae67568d35bddbeed6a3508cbd 100644 (file)
@@ -92,6 +92,7 @@
 #ifdef CONFIG_INET
 #include <net/inet_common.h>
 #endif
+#include <linux/bpf.h>
 
 #include "internal.h"
 
@@ -229,6 +230,8 @@ struct packet_skb_cb {
        } sa;
 };
 
+#define vio_le() virtio_legacy_is_little_endian()
+
 #define PACKET_SKB_CB(__skb)   ((struct packet_skb_cb *)((__skb)->cb))
 
 #define GET_PBDQC_FROM_RB(x)   ((struct tpacket_kbdq_core *)(&(x)->prb_bdqc))
@@ -518,13 +521,11 @@ static void prb_del_retire_blk_timer(struct tpacket_kbdq_core *pkc)
 }
 
 static void prb_shutdown_retire_blk_timer(struct packet_sock *po,
-               int tx_ring,
                struct sk_buff_head *rb_queue)
 {
        struct tpacket_kbdq_core *pkc;
 
-       pkc = tx_ring ? GET_PBDQC_FROM_RB(&po->tx_ring) :
-                       GET_PBDQC_FROM_RB(&po->rx_ring);
+       pkc = GET_PBDQC_FROM_RB(&po->rx_ring);
 
        spin_lock_bh(&rb_queue->lock);
        pkc->delete_blk_timer = 1;
@@ -1412,6 +1413,22 @@ static unsigned int fanout_demux_qm(struct packet_fanout *f,
        return skb_get_queue_mapping(skb) % num;
 }
 
+static unsigned int fanout_demux_bpf(struct packet_fanout *f,
+                                    struct sk_buff *skb,
+                                    unsigned int num)
+{
+       struct bpf_prog *prog;
+       unsigned int ret = 0;
+
+       rcu_read_lock();
+       prog = rcu_dereference(f->bpf_prog);
+       if (prog)
+               ret = BPF_PROG_RUN(prog, skb) % num;
+       rcu_read_unlock();
+
+       return ret;
+}
+
 static bool fanout_has_flag(struct packet_fanout *f, u16 flag)
 {
        return f->flags & (flag >> 8);
@@ -1456,6 +1473,10 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
        case PACKET_FANOUT_ROLLOVER:
                idx = fanout_demux_rollover(f, skb, 0, false, num);
                break;
+       case PACKET_FANOUT_CBPF:
+       case PACKET_FANOUT_EBPF:
+               idx = fanout_demux_bpf(f, skb, num);
+               break;
        }
 
        if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER))
@@ -1504,6 +1525,103 @@ static bool match_fanout_group(struct packet_type *ptype, struct sock *sk)
        return false;
 }
 
+static void fanout_init_data(struct packet_fanout *f)
+{
+       switch (f->type) {
+       case PACKET_FANOUT_LB:
+               atomic_set(&f->rr_cur, 0);
+               break;
+       case PACKET_FANOUT_CBPF:
+       case PACKET_FANOUT_EBPF:
+               RCU_INIT_POINTER(f->bpf_prog, NULL);
+               break;
+       }
+}
+
+static void __fanout_set_data_bpf(struct packet_fanout *f, struct bpf_prog *new)
+{
+       struct bpf_prog *old;
+
+       spin_lock(&f->lock);
+       old = rcu_dereference_protected(f->bpf_prog, lockdep_is_held(&f->lock));
+       rcu_assign_pointer(f->bpf_prog, new);
+       spin_unlock(&f->lock);
+
+       if (old) {
+               synchronize_net();
+               bpf_prog_destroy(old);
+       }
+}
+
+static int fanout_set_data_cbpf(struct packet_sock *po, char __user *data,
+                               unsigned int len)
+{
+       struct bpf_prog *new;
+       struct sock_fprog fprog;
+       int ret;
+
+       if (sock_flag(&po->sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+       if (len != sizeof(fprog))
+               return -EINVAL;
+       if (copy_from_user(&fprog, data, len))
+               return -EFAULT;
+
+       ret = bpf_prog_create_from_user(&new, &fprog, NULL);
+       if (ret)
+               return ret;
+
+       __fanout_set_data_bpf(po->fanout, new);
+       return 0;
+}
+
+static int fanout_set_data_ebpf(struct packet_sock *po, char __user *data,
+                               unsigned int len)
+{
+       struct bpf_prog *new;
+       u32 fd;
+
+       if (sock_flag(&po->sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+       if (len != sizeof(fd))
+               return -EINVAL;
+       if (copy_from_user(&fd, data, len))
+               return -EFAULT;
+
+       new = bpf_prog_get(fd);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
+       if (new->type != BPF_PROG_TYPE_SOCKET_FILTER) {
+               bpf_prog_put(new);
+               return -EINVAL;
+       }
+
+       __fanout_set_data_bpf(po->fanout, new);
+       return 0;
+}
+
+static int fanout_set_data(struct packet_sock *po, char __user *data,
+                          unsigned int len)
+{
+       switch (po->fanout->type) {
+       case PACKET_FANOUT_CBPF:
+               return fanout_set_data_cbpf(po, data, len);
+       case PACKET_FANOUT_EBPF:
+               return fanout_set_data_ebpf(po, data, len);
+       default:
+               return -EINVAL;
+       };
+}
+
+static void fanout_release_data(struct packet_fanout *f)
+{
+       switch (f->type) {
+       case PACKET_FANOUT_CBPF:
+       case PACKET_FANOUT_EBPF:
+               __fanout_set_data_bpf(f, NULL);
+       };
+}
+
 static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
 {
        struct packet_sock *po = pkt_sk(sk);
@@ -1521,6 +1639,8 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
        case PACKET_FANOUT_CPU:
        case PACKET_FANOUT_RND:
        case PACKET_FANOUT_QM:
+       case PACKET_FANOUT_CBPF:
+       case PACKET_FANOUT_EBPF:
                break;
        default:
                return -EINVAL;
@@ -1563,10 +1683,10 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
                match->id = id;
                match->type = type;
                match->flags = flags;
-               atomic_set(&match->rr_cur, 0);
                INIT_LIST_HEAD(&match->list);
                spin_lock_init(&match->lock);
                atomic_set(&match->sk_ref, 0);
+               fanout_init_data(match);
                match->prot_hook.type = po->prot_hook.type;
                match->prot_hook.dev = po->prot_hook.dev;
                match->prot_hook.func = packet_rcv_fanout;
@@ -1612,6 +1732,7 @@ static void fanout_release(struct sock *sk)
        if (atomic_dec_and_test(&f->sk_ref)) {
                list_del(&f->list);
                dev_remove_pack(&f->prot_hook);
+               fanout_release_data(f);
                kfree(f);
        }
        mutex_unlock(&fanout_mutex);
@@ -2561,15 +2682,15 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
                        goto out_unlock;
 
                if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
-                   (__virtio16_to_cpu(false, vnet_hdr.csum_start) +
-                    __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2 >
-                     __virtio16_to_cpu(false, vnet_hdr.hdr_len)))
-                       vnet_hdr.hdr_len = __cpu_to_virtio16(false,
-                                __virtio16_to_cpu(false, vnet_hdr.csum_start) +
-                               __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2);
+                   (__virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) +
+                    __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2 >
+                     __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len)))
+                       vnet_hdr.hdr_len = __cpu_to_virtio16(vio_le(),
+                                __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) +
+                               __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2);
 
                err = -EINVAL;
-               if (__virtio16_to_cpu(false, vnet_hdr.hdr_len) > len)
+               if (__virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len) > len)
                        goto out_unlock;
 
                if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
@@ -2612,7 +2733,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        hlen = LL_RESERVED_SPACE(dev);
        tlen = dev->needed_tailroom;
        skb = packet_alloc_skb(sk, hlen + tlen, hlen, len,
-                              __virtio16_to_cpu(false, vnet_hdr.hdr_len),
+                              __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len),
                               msg->msg_flags & MSG_DONTWAIT, &err);
        if (skb == NULL)
                goto out_unlock;
@@ -2659,8 +2780,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
 
        if (po->has_vnet_hdr) {
                if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
-                       u16 s = __virtio16_to_cpu(false, vnet_hdr.csum_start);
-                       u16 o = __virtio16_to_cpu(false, vnet_hdr.csum_offset);
+                       u16 s = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start);
+                       u16 o = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset);
                        if (!skb_partial_csum_set(skb, s, o)) {
                                err = -EINVAL;
                                goto out_free;
@@ -2668,7 +2789,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
                }
 
                skb_shinfo(skb)->gso_size =
-                       __virtio16_to_cpu(false, vnet_hdr.gso_size);
+                       __virtio16_to_cpu(vio_le(), vnet_hdr.gso_size);
                skb_shinfo(skb)->gso_type = gso_type;
 
                /* Header must be checked, and gso_segs computed. */
@@ -3042,9 +3163,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 
                        /* This is a hint as to how much should be linear. */
                        vnet_hdr.hdr_len =
-                               __cpu_to_virtio16(false, skb_headlen(skb));
+                               __cpu_to_virtio16(vio_le(), skb_headlen(skb));
                        vnet_hdr.gso_size =
-                               __cpu_to_virtio16(false, sinfo->gso_size);
+                               __cpu_to_virtio16(vio_le(), sinfo->gso_size);
                        if (sinfo->gso_type & SKB_GSO_TCPV4)
                                vnet_hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
                        else if (sinfo->gso_type & SKB_GSO_TCPV6)
@@ -3062,9 +3183,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 
                if (skb->ip_summed == CHECKSUM_PARTIAL) {
                        vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-                       vnet_hdr.csum_start = __cpu_to_virtio16(false,
+                       vnet_hdr.csum_start = __cpu_to_virtio16(vio_le(),
                                          skb_checksum_start_offset(skb));
-                       vnet_hdr.csum_offset = __cpu_to_virtio16(false,
+                       vnet_hdr.csum_offset = __cpu_to_virtio16(vio_le(),
                                                         skb->csum_offset);
                } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
                        vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID;
@@ -3531,6 +3652,13 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
 
                return fanout_add(sk, val & 0xffff, val >> 16);
        }
+       case PACKET_FANOUT_DATA:
+       {
+               if (!po->fanout)
+                       return -EINVAL;
+
+               return fanout_set_data(po, optval, optlen);
+       }
        case PACKET_TX_HAS_OFF:
        {
                unsigned int val;
@@ -4043,7 +4171,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
        if (closing && (po->tp_version > TPACKET_V2)) {
                /* Because we don't support block-based V3 on tx-ring */
                if (!tx_ring)
-                       prb_shutdown_retire_blk_timer(po, tx_ring, rb_queue);
+                       prb_shutdown_retire_blk_timer(po, rb_queue);
        }
        release_sock(sk);
 
This page took 0.031734 seconds and 5 git commands to generate.