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