Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux-2.6-microblaze.git] / net / packet / af_packet.c
index 7a18fff..a667b19 100644 (file)
@@ -46,6 +46,7 @@
  *                                     Copyright (C) 2011, <lokec@ccs.neu.edu>
  */
 
+#include <linux/ethtool.h>
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/capability.h>
@@ -1636,13 +1637,15 @@ static bool fanout_find_new_id(struct sock *sk, u16 *new_id)
        return false;
 }
 
-static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
+static int fanout_add(struct sock *sk, struct fanout_args *args)
 {
        struct packet_rollover *rollover = NULL;
        struct packet_sock *po = pkt_sk(sk);
+       u16 type_flags = args->type_flags;
        struct packet_fanout *f, *match;
        u8 type = type_flags & 0xff;
        u8 flags = type_flags >> 8;
+       u16 id = args->id;
        int err;
 
        switch (type) {
@@ -1700,11 +1703,21 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
                }
        }
        err = -EINVAL;
-       if (match && match->flags != flags)
-               goto out;
-       if (!match) {
+       if (match) {
+               if (match->flags != flags)
+                       goto out;
+               if (args->max_num_members &&
+                   args->max_num_members != match->max_num_members)
+                       goto out;
+       } else {
+               if (args->max_num_members > PACKET_FANOUT_MAX)
+                       goto out;
+               if (!args->max_num_members)
+                       /* legacy PACKET_FANOUT_MAX */
+                       args->max_num_members = 256;
                err = -ENOMEM;
-               match = kzalloc(sizeof(*match), GFP_KERNEL);
+               match = kvzalloc(struct_size(match, arr, args->max_num_members),
+                                GFP_KERNEL);
                if (!match)
                        goto out;
                write_pnet(&match->net, sock_net(sk));
@@ -1720,6 +1733,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
                match->prot_hook.func = packet_rcv_fanout;
                match->prot_hook.af_packet_priv = match;
                match->prot_hook.id_match = match_fanout_group;
+               match->max_num_members = args->max_num_members;
                list_add(&match->list, &fanout_list);
        }
        err = -EINVAL;
@@ -1730,7 +1744,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
            match->prot_hook.type == po->prot_hook.type &&
            match->prot_hook.dev == po->prot_hook.dev) {
                err = -ENOSPC;
-               if (refcount_read(&match->sk_ref) < PACKET_FANOUT_MAX) {
+               if (refcount_read(&match->sk_ref) < match->max_num_members) {
                        __dev_remove_pack(&po->prot_hook);
                        po->fanout = match;
                        po->rollover = rollover;
@@ -1744,7 +1758,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
 
        if (err && !refcount_read(&match->sk_ref)) {
                list_del(&match->list);
-               kfree(match);
+               kvfree(match);
        }
 
 out:
@@ -3075,7 +3089,7 @@ static int packet_release(struct socket *sock)
        kfree(po->rollover);
        if (f) {
                fanout_release_data(f);
-               kfree(f);
+               kvfree(f);
        }
        /*
         *      Now the socket is dead. No more input will appear.
@@ -3866,14 +3880,14 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval,
        }
        case PACKET_FANOUT:
        {
-               int val;
+               struct fanout_args args = { 0 };
 
-               if (optlen != sizeof(val))
+               if (optlen != sizeof(int) && optlen != sizeof(args))
                        return -EINVAL;
-               if (copy_from_sockptr(&val, optval, sizeof(val)))
+               if (copy_from_sockptr(&args, optval, optlen))
                        return -EFAULT;
 
-               return fanout_add(sk, val & 0xffff, val >> 16);
+               return fanout_add(sk, &args);
        }
        case PACKET_FANOUT_DATA:
        {