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