linux/net/netlabel/netlabel_domainhash.c
<<
>>
Prefs
   1/*
   2 * NetLabel Domain Hash Table
   3 *
   4 * This file manages the domain hash table that NetLabel uses to determine
   5 * which network labeling protocol to use for a given domain.  The NetLabel
   6 * system manages static and dynamic label mappings for network protocols such
   7 * as CIPSO and RIPSO.
   8 *
   9 * Author: Paul Moore <paul.moore@hp.com>
  10 *
  11 */
  12
  13/*
  14 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  15 *
  16 * This program is free software;  you can redistribute it and/or modify
  17 * it under the terms of the GNU General Public License as published by
  18 * the Free Software Foundation; either version 2 of the License, or
  19 * (at your option) any later version.
  20 *
  21 * This program is distributed in the hope that it will be useful,
  22 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
  23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  24 * the GNU General Public License for more details.
  25 *
  26 * You should have received a copy of the GNU General Public License
  27 * along with this program;  if not, write to the Free Software
  28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29 *
  30 */
  31
  32#include <linux/types.h>
  33#include <linux/rculist.h>
  34#include <linux/skbuff.h>
  35#include <linux/spinlock.h>
  36#include <linux/string.h>
  37#include <linux/audit.h>
  38#include <net/netlabel.h>
  39#include <net/cipso_ipv4.h>
  40#include <asm/bug.h>
  41
  42#include "netlabel_mgmt.h"
  43#include "netlabel_addrlist.h"
  44#include "netlabel_domainhash.h"
  45#include "netlabel_user.h"
  46
  47struct netlbl_domhsh_tbl {
  48        struct list_head *tbl;
  49        u32 size;
  50};
  51
  52/* Domain hash table */
  53/* XXX - updates should be so rare that having one spinlock for the entire
  54 * hash table should be okay */
  55static DEFINE_SPINLOCK(netlbl_domhsh_lock);
  56static struct netlbl_domhsh_tbl *netlbl_domhsh = NULL;
  57static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
  58
  59/*
  60 * Domain Hash Table Helper Functions
  61 */
  62
  63/**
  64 * netlbl_domhsh_free_entry - Frees a domain hash table entry
  65 * @entry: the entry's RCU field
  66 *
  67 * Description:
  68 * This function is designed to be used as a callback to the call_rcu()
  69 * function so that the memory allocated to a hash table entry can be released
  70 * safely.
  71 *
  72 */
  73static void netlbl_domhsh_free_entry(struct rcu_head *entry)
  74{
  75        struct netlbl_dom_map *ptr;
  76        struct netlbl_af4list *iter4;
  77        struct netlbl_af4list *tmp4;
  78#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
  79        struct netlbl_af6list *iter6;
  80        struct netlbl_af6list *tmp6;
  81#endif /* IPv6 */
  82
  83        ptr = container_of(entry, struct netlbl_dom_map, rcu);
  84        if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
  85                netlbl_af4list_foreach_safe(iter4, tmp4,
  86                                            &ptr->type_def.addrsel->list4) {
  87                        netlbl_af4list_remove_entry(iter4);
  88                        kfree(netlbl_domhsh_addr4_entry(iter4));
  89                }
  90#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
  91                netlbl_af6list_foreach_safe(iter6, tmp6,
  92                                            &ptr->type_def.addrsel->list6) {
  93                        netlbl_af6list_remove_entry(iter6);
  94                        kfree(netlbl_domhsh_addr6_entry(iter6));
  95                }
  96#endif /* IPv6 */
  97        }
  98        kfree(ptr->domain);
  99        kfree(ptr);
 100}
 101
 102/**
 103 * netlbl_domhsh_hash - Hashing function for the domain hash table
 104 * @domain: the domain name to hash
 105 *
 106 * Description:
 107 * This is the hashing function for the domain hash table, it returns the
 108 * correct bucket number for the domain.  The caller is responsibile for
 109 * calling the rcu_read_[un]lock() functions.
 110 *
 111 */
 112static u32 netlbl_domhsh_hash(const char *key)
 113{
 114        u32 iter;
 115        u32 val;
 116        u32 len;
 117
 118        /* This is taken (with slight modification) from
 119         * security/selinux/ss/symtab.c:symhash() */
 120
 121        for (iter = 0, val = 0, len = strlen(key); iter < len; iter++)
 122                val = (val << 4 | (val >> (8 * sizeof(u32) - 4))) ^ key[iter];
 123        return val & (rcu_dereference(netlbl_domhsh)->size - 1);
 124}
 125
 126/**
 127 * netlbl_domhsh_search - Search for a domain entry
 128 * @domain: the domain
 129 *
 130 * Description:
 131 * Searches the domain hash table and returns a pointer to the hash table
 132 * entry if found, otherwise NULL is returned.  The caller is responsibile for
 133 * the rcu hash table locks (i.e. the caller much call rcu_read_[un]lock()).
 134 *
 135 */
 136static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
 137{
 138        u32 bkt;
 139        struct list_head *bkt_list;
 140        struct netlbl_dom_map *iter;
 141
 142        if (domain != NULL) {
 143                bkt = netlbl_domhsh_hash(domain);
 144                bkt_list = &rcu_dereference(netlbl_domhsh)->tbl[bkt];
 145                list_for_each_entry_rcu(iter, bkt_list, list)
 146                        if (iter->valid && strcmp(iter->domain, domain) == 0)
 147                                return iter;
 148        }
 149
 150        return NULL;
 151}
 152
 153/**
 154 * netlbl_domhsh_search_def - Search for a domain entry
 155 * @domain: the domain
 156 * @def: return default if no match is found
 157 *
 158 * Description:
 159 * Searches the domain hash table and returns a pointer to the hash table
 160 * entry if an exact match is found, if an exact match is not present in the
 161 * hash table then the default entry is returned if valid otherwise NULL is
 162 * returned.  The caller is responsibile for the rcu hash table locks
 163 * (i.e. the caller much call rcu_read_[un]lock()).
 164 *
 165 */
 166static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
 167{
 168        struct netlbl_dom_map *entry;
 169
 170        entry = netlbl_domhsh_search(domain);
 171        if (entry == NULL) {
 172                entry = rcu_dereference(netlbl_domhsh_def);
 173                if (entry != NULL && !entry->valid)
 174                        entry = NULL;
 175        }
 176
 177        return entry;
 178}
 179
 180/**
 181 * netlbl_domhsh_audit_add - Generate an audit entry for an add event
 182 * @entry: the entry being added
 183 * @addr4: the IPv4 address information
 184 * @addr6: the IPv6 address information
 185 * @result: the result code
 186 * @audit_info: NetLabel audit information
 187 *
 188 * Description:
 189 * Generate an audit record for adding a new NetLabel/LSM mapping entry with
 190 * the given information.  Caller is responsibile for holding the necessary
 191 * locks.
 192 *
 193 */
 194static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
 195                                    struct netlbl_af4list *addr4,
 196                                    struct netlbl_af6list *addr6,
 197                                    int result,
 198                                    struct netlbl_audit *audit_info)
 199{
 200        struct audit_buffer *audit_buf;
 201        struct cipso_v4_doi *cipsov4 = NULL;
 202        u32 type;
 203
 204        audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
 205        if (audit_buf != NULL) {
 206                audit_log_format(audit_buf, " nlbl_domain=%s",
 207                                 entry->domain ? entry->domain : "(default)");
 208                if (addr4 != NULL) {
 209                        struct netlbl_domaddr4_map *map4;
 210                        map4 = netlbl_domhsh_addr4_entry(addr4);
 211                        type = map4->type;
 212                        cipsov4 = map4->type_def.cipsov4;
 213                        netlbl_af4list_audit_addr(audit_buf, 0, NULL,
 214                                                  addr4->addr, addr4->mask);
 215#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 216                } else if (addr6 != NULL) {
 217                        struct netlbl_domaddr6_map *map6;
 218                        map6 = netlbl_domhsh_addr6_entry(addr6);
 219                        type = map6->type;
 220                        netlbl_af6list_audit_addr(audit_buf, 0, NULL,
 221                                                  &addr6->addr, &addr6->mask);
 222#endif /* IPv6 */
 223                } else {
 224                        type = entry->type;
 225                        cipsov4 = entry->type_def.cipsov4;
 226                }
 227                switch (type) {
 228                case NETLBL_NLTYPE_UNLABELED:
 229                        audit_log_format(audit_buf, " nlbl_protocol=unlbl");
 230                        break;
 231                case NETLBL_NLTYPE_CIPSOV4:
 232                        BUG_ON(cipsov4 == NULL);
 233                        audit_log_format(audit_buf,
 234                                         " nlbl_protocol=cipsov4 cipso_doi=%u",
 235                                         cipsov4->doi);
 236                        break;
 237                }
 238                audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
 239                audit_log_end(audit_buf);
 240        }
 241}
 242
 243/*
 244 * Domain Hash Table Functions
 245 */
 246
 247/**
 248 * netlbl_domhsh_init - Init for the domain hash
 249 * @size: the number of bits to use for the hash buckets
 250 *
 251 * Description:
 252 * Initializes the domain hash table, should be called only by
 253 * netlbl_user_init() during initialization.  Returns zero on success, non-zero
 254 * values on error.
 255 *
 256 */
 257int __init netlbl_domhsh_init(u32 size)
 258{
 259        u32 iter;
 260        struct netlbl_domhsh_tbl *hsh_tbl;
 261
 262        if (size == 0)
 263                return -EINVAL;
 264
 265        hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
 266        if (hsh_tbl == NULL)
 267                return -ENOMEM;
 268        hsh_tbl->size = 1 << size;
 269        hsh_tbl->tbl = kcalloc(hsh_tbl->size,
 270                               sizeof(struct list_head),
 271                               GFP_KERNEL);
 272        if (hsh_tbl->tbl == NULL) {
 273                kfree(hsh_tbl);
 274                return -ENOMEM;
 275        }
 276        for (iter = 0; iter < hsh_tbl->size; iter++)
 277                INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
 278
 279        spin_lock(&netlbl_domhsh_lock);
 280        rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
 281        spin_unlock(&netlbl_domhsh_lock);
 282
 283        return 0;
 284}
 285
 286/**
 287 * netlbl_domhsh_add - Adds a entry to the domain hash table
 288 * @entry: the entry to add
 289 * @audit_info: NetLabel audit information
 290 *
 291 * Description:
 292 * Adds a new entry to the domain hash table and handles any updates to the
 293 * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
 294 * negative on failure.
 295 *
 296 */
 297int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 298                      struct netlbl_audit *audit_info)
 299{
 300        int ret_val = 0;
 301        struct netlbl_dom_map *entry_old;
 302        struct netlbl_af4list *iter4;
 303        struct netlbl_af4list *tmp4;
 304#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 305        struct netlbl_af6list *iter6;
 306        struct netlbl_af6list *tmp6;
 307#endif /* IPv6 */
 308
 309        rcu_read_lock();
 310
 311        spin_lock(&netlbl_domhsh_lock);
 312        if (entry->domain != NULL)
 313                entry_old = netlbl_domhsh_search(entry->domain);
 314        else
 315                entry_old = netlbl_domhsh_search_def(entry->domain);
 316        if (entry_old == NULL) {
 317                entry->valid = 1;
 318                INIT_RCU_HEAD(&entry->rcu);
 319
 320                if (entry->domain != NULL) {
 321                        u32 bkt = netlbl_domhsh_hash(entry->domain);
 322                        list_add_tail_rcu(&entry->list,
 323                                    &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
 324                } else {
 325                        INIT_LIST_HEAD(&entry->list);
 326                        rcu_assign_pointer(netlbl_domhsh_def, entry);
 327                }
 328
 329                if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
 330                        netlbl_af4list_foreach_rcu(iter4,
 331                                               &entry->type_def.addrsel->list4)
 332                                netlbl_domhsh_audit_add(entry, iter4, NULL,
 333                                                        ret_val, audit_info);
 334#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 335                        netlbl_af6list_foreach_rcu(iter6,
 336                                               &entry->type_def.addrsel->list6)
 337                                netlbl_domhsh_audit_add(entry, NULL, iter6,
 338                                                        ret_val, audit_info);
 339#endif /* IPv6 */
 340                } else
 341                        netlbl_domhsh_audit_add(entry, NULL, NULL,
 342                                                ret_val, audit_info);
 343        } else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
 344                   entry->type == NETLBL_NLTYPE_ADDRSELECT) {
 345                struct list_head *old_list4;
 346                struct list_head *old_list6;
 347
 348                old_list4 = &entry_old->type_def.addrsel->list4;
 349                old_list6 = &entry_old->type_def.addrsel->list6;
 350
 351                /* we only allow the addition of address selectors if all of
 352                 * the selectors do not exist in the existing domain map */
 353                netlbl_af4list_foreach_rcu(iter4,
 354                                           &entry->type_def.addrsel->list4)
 355                        if (netlbl_af4list_search_exact(iter4->addr,
 356                                                        iter4->mask,
 357                                                        old_list4)) {
 358                                ret_val = -EEXIST;
 359                                goto add_return;
 360                        }
 361#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 362                netlbl_af6list_foreach_rcu(iter6,
 363                                           &entry->type_def.addrsel->list6)
 364                        if (netlbl_af6list_search_exact(&iter6->addr,
 365                                                        &iter6->mask,
 366                                                        old_list6)) {
 367                                ret_val = -EEXIST;
 368                                goto add_return;
 369                        }
 370#endif /* IPv6 */
 371
 372                netlbl_af4list_foreach_safe(iter4, tmp4,
 373                                            &entry->type_def.addrsel->list4) {
 374                        netlbl_af4list_remove_entry(iter4);
 375                        iter4->valid = 1;
 376                        ret_val = netlbl_af4list_add(iter4, old_list4);
 377                        netlbl_domhsh_audit_add(entry_old, iter4, NULL,
 378                                                ret_val, audit_info);
 379                        if (ret_val != 0)
 380                                goto add_return;
 381                }
 382#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 383                netlbl_af6list_foreach_safe(iter6, tmp6,
 384                                            &entry->type_def.addrsel->list6) {
 385                        netlbl_af6list_remove_entry(iter6);
 386                        iter6->valid = 1;
 387                        ret_val = netlbl_af6list_add(iter6, old_list6);
 388                        netlbl_domhsh_audit_add(entry_old, NULL, iter6,
 389                                                ret_val, audit_info);
 390                        if (ret_val != 0)
 391                                goto add_return;
 392                }
 393#endif /* IPv6 */
 394        } else
 395                ret_val = -EINVAL;
 396
 397add_return:
 398        spin_unlock(&netlbl_domhsh_lock);
 399        rcu_read_unlock();
 400        return ret_val;
 401}
 402
 403/**
 404 * netlbl_domhsh_add_default - Adds the default entry to the domain hash table
 405 * @entry: the entry to add
 406 * @audit_info: NetLabel audit information
 407 *
 408 * Description:
 409 * Adds a new default entry to the domain hash table and handles any updates
 410 * to the lower level protocol handler (i.e. CIPSO).  Returns zero on success,
 411 * negative on failure.
 412 *
 413 */
 414int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
 415                              struct netlbl_audit *audit_info)
 416{
 417        return netlbl_domhsh_add(entry, audit_info);
 418}
 419
 420/**
 421 * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
 422 * @entry: the entry to remove
 423 * @audit_info: NetLabel audit information
 424 *
 425 * Description:
 426 * Removes an entry from the domain hash table and handles any updates to the
 427 * lower level protocol handler (i.e. CIPSO).  Caller is responsible for
 428 * ensuring that the RCU read lock is held.  Returns zero on success, negative
 429 * on failure.
 430 *
 431 */
 432int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
 433                               struct netlbl_audit *audit_info)
 434{
 435        int ret_val = 0;
 436        struct audit_buffer *audit_buf;
 437
 438        if (entry == NULL)
 439                return -ENOENT;
 440
 441        spin_lock(&netlbl_domhsh_lock);
 442        if (entry->valid) {
 443                entry->valid = 0;
 444                if (entry != rcu_dereference(netlbl_domhsh_def))
 445                        list_del_rcu(&entry->list);
 446                else
 447                        rcu_assign_pointer(netlbl_domhsh_def, NULL);
 448        } else
 449                ret_val = -ENOENT;
 450        spin_unlock(&netlbl_domhsh_lock);
 451
 452        audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
 453        if (audit_buf != NULL) {
 454                audit_log_format(audit_buf,
 455                                 " nlbl_domain=%s res=%u",
 456                                 entry->domain ? entry->domain : "(default)",
 457                                 ret_val == 0 ? 1 : 0);
 458                audit_log_end(audit_buf);
 459        }
 460
 461        if (ret_val == 0) {
 462                struct netlbl_af4list *iter4;
 463                struct netlbl_domaddr4_map *map4;
 464
 465                switch (entry->type) {
 466                case NETLBL_NLTYPE_ADDRSELECT:
 467                        netlbl_af4list_foreach_rcu(iter4,
 468                                             &entry->type_def.addrsel->list4) {
 469                                map4 = netlbl_domhsh_addr4_entry(iter4);
 470                                cipso_v4_doi_putdef(map4->type_def.cipsov4);
 471                        }
 472                        /* no need to check the IPv6 list since we currently
 473                         * support only unlabeled protocols for IPv6 */
 474                        break;
 475                case NETLBL_NLTYPE_CIPSOV4:
 476                        cipso_v4_doi_putdef(entry->type_def.cipsov4);
 477                        break;
 478                }
 479                call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
 480        }
 481
 482        return ret_val;
 483}
 484
 485/**
 486 * netlbl_domhsh_remove_af4 - Removes an address selector entry
 487 * @domain: the domain
 488 * @addr: IPv4 address
 489 * @mask: IPv4 address mask
 490 * @audit_info: NetLabel audit information
 491 *
 492 * Description:
 493 * Removes an individual address selector from a domain mapping and potentially
 494 * the entire mapping if it is empty.  Returns zero on success, negative values
 495 * on failure.
 496 *
 497 */
 498int netlbl_domhsh_remove_af4(const char *domain,
 499                             const struct in_addr *addr,
 500                             const struct in_addr *mask,
 501                             struct netlbl_audit *audit_info)
 502{
 503        struct netlbl_dom_map *entry_map;
 504        struct netlbl_af4list *entry_addr;
 505        struct netlbl_af4list *iter4;
 506#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 507        struct netlbl_af6list *iter6;
 508#endif /* IPv6 */
 509        struct netlbl_domaddr4_map *entry;
 510
 511        rcu_read_lock();
 512
 513        if (domain)
 514                entry_map = netlbl_domhsh_search(domain);
 515        else
 516                entry_map = netlbl_domhsh_search_def(domain);
 517        if (entry_map == NULL || entry_map->type != NETLBL_NLTYPE_ADDRSELECT)
 518                goto remove_af4_failure;
 519
 520        spin_lock(&netlbl_domhsh_lock);
 521        entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
 522                                           &entry_map->type_def.addrsel->list4);
 523        spin_unlock(&netlbl_domhsh_lock);
 524
 525        if (entry_addr == NULL)
 526                goto remove_af4_failure;
 527        netlbl_af4list_foreach_rcu(iter4, &entry_map->type_def.addrsel->list4)
 528                goto remove_af4_single_addr;
 529#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 530        netlbl_af6list_foreach_rcu(iter6, &entry_map->type_def.addrsel->list6)
 531                goto remove_af4_single_addr;
 532#endif /* IPv6 */
 533        /* the domain mapping is empty so remove it from the mapping table */
 534        netlbl_domhsh_remove_entry(entry_map, audit_info);
 535
 536remove_af4_single_addr:
 537        rcu_read_unlock();
 538        /* yick, we can't use call_rcu here because we don't have a rcu head
 539         * pointer but hopefully this should be a rare case so the pause
 540         * shouldn't be a problem */
 541        synchronize_rcu();
 542        entry = netlbl_domhsh_addr4_entry(entry_addr);
 543        cipso_v4_doi_putdef(entry->type_def.cipsov4);
 544        kfree(entry);
 545        return 0;
 546
 547remove_af4_failure:
 548        rcu_read_unlock();
 549        return -ENOENT;
 550}
 551
 552/**
 553 * netlbl_domhsh_remove - Removes an entry from the domain hash table
 554 * @domain: the domain to remove
 555 * @audit_info: NetLabel audit information
 556 *
 557 * Description:
 558 * Removes an entry from the domain hash table and handles any updates to the
 559 * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
 560 * negative on failure.
 561 *
 562 */
 563int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 564{
 565        int ret_val;
 566        struct netlbl_dom_map *entry;
 567
 568        rcu_read_lock();
 569        if (domain)
 570                entry = netlbl_domhsh_search(domain);
 571        else
 572                entry = netlbl_domhsh_search_def(domain);
 573        ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
 574        rcu_read_unlock();
 575
 576        return ret_val;
 577}
 578
 579/**
 580 * netlbl_domhsh_remove_default - Removes the default entry from the table
 581 * @audit_info: NetLabel audit information
 582 *
 583 * Description:
 584 * Removes/resets the default entry for the domain hash table and handles any
 585 * updates to the lower level protocol handler (i.e. CIPSO).  Returns zero on
 586 * success, non-zero on failure.
 587 *
 588 */
 589int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
 590{
 591        return netlbl_domhsh_remove(NULL, audit_info);
 592}
 593
 594/**
 595 * netlbl_domhsh_getentry - Get an entry from the domain hash table
 596 * @domain: the domain name to search for
 597 *
 598 * Description:
 599 * Look through the domain hash table searching for an entry to match @domain,
 600 * return a pointer to a copy of the entry or NULL.  The caller is responsibile
 601 * for ensuring that rcu_read_[un]lock() is called.
 602 *
 603 */
 604struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
 605{
 606        return netlbl_domhsh_search_def(domain);
 607}
 608
 609/**
 610 * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
 611 * @domain: the domain name to search for
 612 * @addr: the IP address to search for
 613 *
 614 * Description:
 615 * Look through the domain hash table searching for an entry to match @domain
 616 * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
 617 * responsible for ensuring that rcu_read_[un]lock() is called.
 618 *
 619 */
 620struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
 621                                                       __be32 addr)
 622{
 623        struct netlbl_dom_map *dom_iter;
 624        struct netlbl_af4list *addr_iter;
 625
 626        dom_iter = netlbl_domhsh_search_def(domain);
 627        if (dom_iter == NULL)
 628                return NULL;
 629        if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
 630                return NULL;
 631
 632        addr_iter = netlbl_af4list_search(addr,
 633                                          &dom_iter->type_def.addrsel->list4);
 634        if (addr_iter == NULL)
 635                return NULL;
 636
 637        return netlbl_domhsh_addr4_entry(addr_iter);
 638}
 639
 640#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 641/**
 642 * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
 643 * @domain: the domain name to search for
 644 * @addr: the IP address to search for
 645 *
 646 * Description:
 647 * Look through the domain hash table searching for an entry to match @domain
 648 * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
 649 * responsible for ensuring that rcu_read_[un]lock() is called.
 650 *
 651 */
 652struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
 653                                                   const struct in6_addr *addr)
 654{
 655        struct netlbl_dom_map *dom_iter;
 656        struct netlbl_af6list *addr_iter;
 657
 658        dom_iter = netlbl_domhsh_search_def(domain);
 659        if (dom_iter == NULL)
 660                return NULL;
 661        if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
 662                return NULL;
 663
 664        addr_iter = netlbl_af6list_search(addr,
 665                                          &dom_iter->type_def.addrsel->list6);
 666        if (addr_iter == NULL)
 667                return NULL;
 668
 669        return netlbl_domhsh_addr6_entry(addr_iter);
 670}
 671#endif /* IPv6 */
 672
 673/**
 674 * netlbl_domhsh_walk - Iterate through the domain mapping hash table
 675 * @skip_bkt: the number of buckets to skip at the start
 676 * @skip_chain: the number of entries to skip in the first iterated bucket
 677 * @callback: callback for each entry
 678 * @cb_arg: argument for the callback function
 679 *
 680 * Description:
 681 * Interate over the domain mapping hash table, skipping the first @skip_bkt
 682 * buckets and @skip_chain entries.  For each entry in the table call
 683 * @callback, if @callback returns a negative value stop 'walking' through the
 684 * table and return.  Updates the values in @skip_bkt and @skip_chain on
 685 * return.  Returns zero on succcess, negative values on failure.
 686 *
 687 */
 688int netlbl_domhsh_walk(u32 *skip_bkt,
 689                     u32 *skip_chain,
 690                     int (*callback) (struct netlbl_dom_map *entry, void *arg),
 691                     void *cb_arg)
 692{
 693        int ret_val = -ENOENT;
 694        u32 iter_bkt;
 695        struct list_head *iter_list;
 696        struct netlbl_dom_map *iter_entry;
 697        u32 chain_cnt = 0;
 698
 699        rcu_read_lock();
 700        for (iter_bkt = *skip_bkt;
 701             iter_bkt < rcu_dereference(netlbl_domhsh)->size;
 702             iter_bkt++, chain_cnt = 0) {
 703                iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
 704                list_for_each_entry_rcu(iter_entry, iter_list, list)
 705                        if (iter_entry->valid) {
 706                                if (chain_cnt++ < *skip_chain)
 707                                        continue;
 708                                ret_val = callback(iter_entry, cb_arg);
 709                                if (ret_val < 0) {
 710                                        chain_cnt--;
 711                                        goto walk_return;
 712                                }
 713                        }
 714        }
 715
 716walk_return:
 717        rcu_read_unlock();
 718        *skip_bkt = iter_bkt;
 719        *skip_chain = chain_cnt;
 720        return ret_val;
 721}
 722