Allow changing the number of groups for a netlink family
after it has been created.

Signed-off-by: Johannes Berg <[EMAIL PROTECTED]>

---
 include/linux/netlink.h  |    1 
 net/netlink/af_netlink.c |   61 +++++++++++++++++++++++++++++++++++------------
 2 files changed, 47 insertions(+), 15 deletions(-)

--- wireless-dev.orig/net/netlink/af_netlink.c  2007-07-03 00:10:31.617889695 
+0200
+++ wireless-dev/net/netlink/af_netlink.c       2007-07-03 00:31:30.267889695 
+0200
@@ -316,8 +316,11 @@ netlink_update_listeners(struct sock *sk
 
        for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
                mask = 0;
-               sk_for_each_bound(sk, node, &tbl->mc_list)
-                       mask |= nlk_sk(sk)->groups[i];
+               sk_for_each_bound(sk, node, &tbl->mc_list) {
+                       if (nlk_sk(sk)->ngroups >=
+                           (i + 1) * sizeof(unsigned long))
+                               mask |= nlk_sk(sk)->groups[i];
+               }
                tbl->listeners[i] = mask;
        }
        /* this function is only called with the netlink table "grabbed", which
@@ -555,10 +558,11 @@ netlink_update_subscriptions(struct sock
        nlk->subscriptions = subscriptions;
 }
 
-static int netlink_alloc_groups(struct sock *sk)
+static int netlink_realloc_groups(struct sock *sk)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
        unsigned int groups;
+       unsigned long *new_groups;
        int err = 0;
 
        netlink_lock_table();
@@ -570,9 +574,15 @@ static int netlink_alloc_groups(struct s
        if (err)
                return err;
 
-       nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
-       if (nlk->groups == NULL)
+       if (nlk->ngroups >= groups)
+               return 0;
+
+       new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_KERNEL);
+       if (new_groups == NULL)
                return -ENOMEM;
+       memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0,
+              NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
+       nlk->groups = new_groups;
        nlk->ngroups = groups;
        return 0;
 }
@@ -591,11 +601,9 @@ static int netlink_bind(struct socket *s
        if (nladdr->nl_groups) {
                if (!netlink_capable(sock, NL_NONROOT_RECV))
                        return -EPERM;
-               if (nlk->groups == NULL) {
-                       err = netlink_alloc_groups(sk);
-                       if (err)
-                               return err;
-               }
+               err = netlink_realloc_groups(sk);
+               if (err)
+                       return err;
        }
 
        if (nlk->pid) {
@@ -1041,11 +1049,9 @@ static int netlink_setsockopt(struct soc
 
                if (!netlink_capable(sock, NL_NONROOT_RECV))
                        return -EPERM;
-               if (nlk->groups == NULL) {
-                       err = netlink_alloc_groups(sk);
-                       if (err)
-                               return err;
-               }
+               err = netlink_realloc_groups(sk);
+               if (err)
+                       return err;
                if (!val || val - 1 >= nlk->ngroups)
                        return -EINVAL;
                netlink_table_grab();
@@ -1332,6 +1338,31 @@ out_sock_release:
        return NULL;
 }
 
+int netlink_change_ngroups(int unit, unsigned int groups)
+{
+       unsigned long *listeners;
+       int err = 0;
+
+       netlink_table_grab();
+       if (NLGRPSZ(nl_table[unit].groups) < NLGRPSZ(groups)) {
+               listeners = krealloc(nl_table[unit].listeners,
+                                    NLGRPSZ(groups), GFP_KERNEL);
+               if (!listeners) {
+                       err = -ENOMEM;
+                       goto out_ungrab;
+               }
+               memset((char*) listeners + NLGRPSZ(nl_table[unit].groups),
+                      0, NLGRPSZ(groups) - NLGRPSZ(nl_table[unit].groups));
+               nl_table[unit].listeners = listeners;
+       }
+       nl_table[unit].groups = groups;
+
+ out_ungrab:
+       netlink_table_ungrab();
+       return err;
+}
+EXPORT_SYMBOL(netlink_change_ngroups);
+
 void netlink_set_nonroot(int protocol, unsigned int flags)
 {
        if ((unsigned int)protocol < MAX_LINKS)
--- wireless-dev.orig/include/linux/netlink.h   2007-07-03 00:09:13.047889695 
+0200
+++ wireless-dev/include/linux/netlink.h        2007-07-03 00:31:30.267889695 
+0200
@@ -161,6 +161,7 @@ extern struct sock *netlink_kernel_creat
                                          void (*input)(struct sock *sk, int 
len),
                                          struct mutex *cb_mutex,
                                          struct module *module);
+extern int netlink_change_ngroups(int unit, unsigned int groups);
 extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
 extern int netlink_has_listeners(struct sock *sk, unsigned int group);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, 
int nonblock);


-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to