ALSA: usb-audio: add support for Akai MPD16
[safe/jmp/linux-2.6] / net / netlabel / netlabel_unlabeled.c
index e8a5c32..a3d64aa 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/notifier.h>
 #include <linux/netdevice.h>
 #include <linux/security.h>
+#include <linux/slab.h>
 #include <net/sock.h>
 #include <net/netlink.h>
 #include <net/genetlink.h>
@@ -114,6 +115,9 @@ struct netlbl_unlhsh_walk_arg {
 /* updates should be so rare that having one spinlock for the entire
  * hash table should be okay */
 static DEFINE_SPINLOCK(netlbl_unlhsh_lock);
+#define netlbl_unlhsh_rcu_deref(p) \
+       rcu_dereference_check(p, rcu_read_lock_held() || \
+                                lockdep_is_held(&netlbl_unlhsh_lock))
 static struct netlbl_unlhsh_tbl *netlbl_unlhsh = NULL;
 static struct netlbl_unlhsh_iface *netlbl_unlhsh_def = NULL;
 
@@ -235,15 +239,13 @@ static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
  * Description:
  * This is the hashing function for the unlabeled hash table, it returns the
  * bucket number for the given device/interface.  The caller is responsible for
- * calling the rcu_read_[un]lock() functions.
+ * ensuring that the hash table is protected with either a RCU read lock or
+ * the hash table lock.
  *
  */
 static u32 netlbl_unlhsh_hash(int ifindex)
 {
-       /* this is taken _almost_ directly from
-        * security/selinux/netif.c:sel_netif_hasfn() as they do pretty much
-        * the same thing */
-       return ifindex & (rcu_dereference(netlbl_unlhsh)->size - 1);
+       return ifindex & (netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->size - 1);
 }
 
 /**
@@ -253,7 +255,8 @@ static u32 netlbl_unlhsh_hash(int ifindex)
  * Description:
  * Searches the unlabeled connection hash table and returns a pointer to the
  * interface entry which matches @ifindex, otherwise NULL is returned.  The
- * caller is responsible for calling the rcu_read_[un]lock() functions.
+ * caller is responsible for ensuring that the hash table is protected with
+ * either a RCU read lock or the hash table lock.
  *
  */
 static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
@@ -263,7 +266,7 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
        struct netlbl_unlhsh_iface *iter;
 
        bkt = netlbl_unlhsh_hash(ifindex);
-       bkt_list = &rcu_dereference(netlbl_unlhsh)->tbl[bkt];
+       bkt_list = &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt];
        list_for_each_entry_rcu(iter, bkt_list, list)
                if (iter->valid && iter->ifindex == ifindex)
                        return iter;
@@ -272,33 +275,6 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
 }
 
 /**
- * netlbl_unlhsh_search_iface_def - Search for a matching interface entry
- * @ifindex: the network interface
- *
- * Description:
- * Searches the unlabeled connection hash table and returns a pointer to the
- * interface entry which matches @ifindex.  If an exact match can not be found
- * and there is a valid default entry, the default entry is returned, otherwise
- * NULL is returned.  The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
- *
- */
-static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface_def(int ifindex)
-{
-       struct netlbl_unlhsh_iface *entry;
-
-       entry = netlbl_unlhsh_search_iface(ifindex);
-       if (entry != NULL)
-               return entry;
-
-       entry = rcu_dereference(netlbl_unlhsh_def);
-       if (entry != NULL && entry->valid)
-               return entry;
-
-       return NULL;
-}
-
-/**
  * netlbl_unlhsh_add_addr4 - Add a new IPv4 address entry to the hash table
  * @iface: the associated interface entry
  * @addr: IPv4 address in network byte order
@@ -308,8 +284,7 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface_def(int ifindex)
  * Description:
  * Add a new address entry into the unlabeled connection hash table using the
  * interface entry specified by @iface.  On success zero is returned, otherwise
- * a negative value is returned.  The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
+ * a negative value is returned.
  *
  */
 static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
@@ -327,7 +302,6 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
        entry->list.addr = addr->s_addr & mask->s_addr;
        entry->list.mask = mask->s_addr;
        entry->list.valid = 1;
-       INIT_RCU_HEAD(&entry->rcu);
        entry->secid = secid;
 
        spin_lock(&netlbl_unlhsh_lock);
@@ -350,8 +324,7 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
  * Description:
  * Add a new address entry into the unlabeled connection hash table using the
  * interface entry specified by @iface.  On success zero is returned, otherwise
- * a negative value is returned.  The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
+ * a negative value is returned.
  *
  */
 static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
@@ -373,7 +346,6 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
        entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
        ipv6_addr_copy(&entry->list.mask, mask);
        entry->list.valid = 1;
-       INIT_RCU_HEAD(&entry->rcu);
        entry->secid = secid;
 
        spin_lock(&netlbl_unlhsh_lock);
@@ -393,8 +365,7 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
  * Description:
  * Add a new, empty, interface entry into the unlabeled connection hash table.
  * On success a pointer to the new interface entry is returned, on failure NULL
- * is returned.  The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
+ * is returned.
  *
  */
 static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
@@ -410,7 +381,6 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
        INIT_LIST_HEAD(&iface->addr4_list);
        INIT_LIST_HEAD(&iface->addr6_list);
        iface->valid = 1;
-       INIT_RCU_HEAD(&iface->rcu);
 
        spin_lock(&netlbl_unlhsh_lock);
        if (ifindex > 0) {
@@ -418,10 +388,10 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
                if (netlbl_unlhsh_search_iface(ifindex) != NULL)
                        goto add_iface_failure;
                list_add_tail_rcu(&iface->list,
-                                 &rcu_dereference(netlbl_unlhsh)->tbl[bkt]);
+                            &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt]);
        } else {
                INIT_LIST_HEAD(&iface->list);
-               if (rcu_dereference(netlbl_unlhsh_def) != NULL)
+               if (netlbl_unlhsh_rcu_deref(netlbl_unlhsh_def) != NULL)
                        goto add_iface_failure;
                rcu_assign_pointer(netlbl_unlhsh_def, iface);
        }
@@ -450,13 +420,13 @@ add_iface_failure:
  * success, negative values on failure.
  *
  */
-static int netlbl_unlhsh_add(struct net *net,
-                            const char *dev_name,
-                            const void *addr,
-                            const void *mask,
-                            u32 addr_len,
-                            u32 secid,
-                            struct netlbl_audit *audit_info)
+int netlbl_unlhsh_add(struct net *net,
+                     const char *dev_name,
+                     const void *addr,
+                     const void *mask,
+                     u32 addr_len,
+                     u32 secid,
+                     struct netlbl_audit *audit_info)
 {
        int ret_val;
        int ifindex;
@@ -472,13 +442,12 @@ static int netlbl_unlhsh_add(struct net *net,
 
        rcu_read_lock();
        if (dev_name != NULL) {
-               dev = dev_get_by_name(net, dev_name);
+               dev = dev_get_by_name_rcu(net, dev_name);
                if (dev == NULL) {
                        ret_val = -ENODEV;
                        goto unlhsh_add_return;
                }
                ifindex = dev->ifindex;
-               dev_put(dev);
                iface = netlbl_unlhsh_search_iface(ifindex);
        } else {
                ifindex = 0;
@@ -552,8 +521,7 @@ unlhsh_add_return:
  *
  * Description:
  * Remove an IP address entry from the unlabeled connection hash table.
- * Returns zero on success, negative values on failure.  The caller is
- * responsible for calling the rcu_read_[un]lock() functions.
+ * Returns zero on success, negative values on failure.
  *
  */
 static int netlbl_unlhsh_remove_addr4(struct net *net,
@@ -562,7 +530,6 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
                                      const struct in_addr *mask,
                                      struct netlbl_audit *audit_info)
 {
-       int ret_val = 0;
        struct netlbl_af4list *list_entry;
        struct netlbl_unlhsh_addr4 *entry;
        struct audit_buffer *audit_buf;
@@ -574,9 +541,10 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
        list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
                                           &iface->addr4_list);
        spin_unlock(&netlbl_unlhsh_lock);
-       if (list_entry == NULL)
-               ret_val = -ENOENT;
-       entry = netlbl_unlhsh_addr4_entry(list_entry);
+       if (list_entry != NULL)
+               entry = netlbl_unlhsh_addr4_entry(list_entry);
+       else
+               entry = NULL;
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
                                              audit_info);
@@ -587,19 +555,21 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
                                          addr->s_addr, mask->s_addr);
                if (dev != NULL)
                        dev_put(dev);
-               if (entry && security_secid_to_secctx(entry->secid,
-                                                     &secctx,
-                                                     &secctx_len) == 0) {
+               if (entry != NULL &&
+                   security_secid_to_secctx(entry->secid,
+                                            &secctx, &secctx_len) == 0) {
                        audit_log_format(audit_buf, " sec_obj=%s", secctx);
                        security_release_secctx(secctx, secctx_len);
                }
-               audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
+               audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
                audit_log_end(audit_buf);
        }
 
-       if (ret_val == 0)
-               call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4);
-       return ret_val;
+       if (entry == NULL)
+               return -ENOENT;
+
+       call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4);
+       return 0;
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -613,8 +583,7 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
  *
  * Description:
  * Remove an IP address entry from the unlabeled connection hash table.
- * Returns zero on success, negative values on failure.  The caller is
- * responsible for calling the rcu_read_[un]lock() functions.
+ * Returns zero on success, negative values on failure.
  *
  */
 static int netlbl_unlhsh_remove_addr6(struct net *net,
@@ -623,7 +592,6 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
                                      const struct in6_addr *mask,
                                      struct netlbl_audit *audit_info)
 {
-       int ret_val = 0;
        struct netlbl_af6list *list_entry;
        struct netlbl_unlhsh_addr6 *entry;
        struct audit_buffer *audit_buf;
@@ -634,9 +602,10 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
        spin_lock(&netlbl_unlhsh_lock);
        list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list);
        spin_unlock(&netlbl_unlhsh_lock);
-       if (list_entry == NULL)
-               ret_val = -ENOENT;
-       entry = netlbl_unlhsh_addr6_entry(list_entry);
+       if (list_entry != NULL)
+               entry = netlbl_unlhsh_addr6_entry(list_entry);
+       else
+               entry = NULL;
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
                                              audit_info);
@@ -647,19 +616,21 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
                                          addr, mask);
                if (dev != NULL)
                        dev_put(dev);
-               if (entry && security_secid_to_secctx(entry->secid,
-                                                     &secctx,
-                                                     &secctx_len) == 0) {
+               if (entry != NULL &&
+                   security_secid_to_secctx(entry->secid,
+                                            &secctx, &secctx_len) == 0) {
                        audit_log_format(audit_buf, " sec_obj=%s", secctx);
                        security_release_secctx(secctx, secctx_len);
                }
-               audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
+               audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
                audit_log_end(audit_buf);
        }
 
-       if (ret_val == 0)
-               call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6);
-       return ret_val;
+       if (entry == NULL)
+               return -ENOENT;
+
+       call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6);
+       return 0;
 }
 #endif /* IPv6 */
 
@@ -716,12 +687,12 @@ unlhsh_condremove_failure:
  * Returns zero on success, negative values on failure.
  *
  */
-static int netlbl_unlhsh_remove(struct net *net,
-                               const char *dev_name,
-                               const void *addr,
-                               const void *mask,
-                               u32 addr_len,
-                               struct netlbl_audit *audit_info)
+int netlbl_unlhsh_remove(struct net *net,
+                        const char *dev_name,
+                        const void *addr,
+                        const void *mask,
+                        u32 addr_len,
+                        struct netlbl_audit *audit_info)
 {
        int ret_val;
        struct net_device *dev;
@@ -733,13 +704,12 @@ static int netlbl_unlhsh_remove(struct net *net,
 
        rcu_read_lock();
        if (dev_name != NULL) {
-               dev = dev_get_by_name(net, dev_name);
+               dev = dev_get_by_name_rcu(net, dev_name);
                if (dev == NULL) {
                        ret_val = -ENODEV;
                        goto unlhsh_remove_return;
                }
                iface = netlbl_unlhsh_search_iface(dev->ifindex);
-               dev_put(dev);
        } else
                iface = rcu_dereference(netlbl_unlhsh_def);
        if (iface == NULL) {
@@ -1474,20 +1444,8 @@ static struct genl_ops netlbl_unlabel_genl_ops[] = {
  */
 int __init netlbl_unlabel_genl_init(void)
 {
-       int ret_val, i;
-
-       ret_val = genl_register_family(&netlbl_unlabel_gnl_family);
-       if (ret_val != 0)
-               return ret_val;
-
-       for (i = 0; i < ARRAY_SIZE(netlbl_unlabel_genl_ops); i++) {
-               ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
-                               &netlbl_unlabel_genl_ops[i]);
-               if (ret_val != 0)
-                       return ret_val;
-       }
-
-       return 0;
+       return genl_register_family_with_ops(&netlbl_unlabel_gnl_family,
+               netlbl_unlabel_genl_ops, ARRAY_SIZE(netlbl_unlabel_genl_ops));
 }
 
 /*
@@ -1560,8 +1518,10 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
        struct netlbl_unlhsh_iface *iface;
 
        rcu_read_lock();
-       iface = netlbl_unlhsh_search_iface_def(skb->iif);
+       iface = netlbl_unlhsh_search_iface(skb->skb_iif);
        if (iface == NULL)
+               iface = rcu_dereference(netlbl_unlhsh_def);
+       if (iface == NULL || !iface->valid)
                goto unlabel_getattr_nolabel;
        switch (family) {
        case PF_INET: {