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 IS_ENABLED(CONFIG_IPV6) 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->def.type == NETLBL_NLTYPE_ADDRSELECT) { 88 netlbl_af4list_foreach_safe(iter4, tmp4, 89 &ptr->def.addrsel->list4) { 90 netlbl_af4list_remove_entry(iter4); 91 kfree(netlbl_domhsh_addr4_entry(iter4)); 92 } 93 #if IS_ENABLED(CONFIG_IPV6) 94 netlbl_af6list_foreach_safe(iter6, tmp6, 95 &ptr->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->def.type; 217 cipsov4 = map4->def.cipso; 218 netlbl_af4list_audit_addr(audit_buf, 0, NULL, 219 addr4->addr, addr4->mask); 220 #if IS_ENABLED(CONFIG_IPV6) 221 } else if (addr6 != NULL) { 222 struct netlbl_domaddr6_map *map6; 223 map6 = netlbl_domhsh_addr6_entry(addr6); 224 type = map6->def.type; 225 netlbl_af6list_audit_addr(audit_buf, 0, NULL, 226 &addr6->addr, &addr6->mask); 227 #endif /* IPv6 */ 228 } else { 229 type = entry->def.type; 230 cipsov4 = entry->def.cipso; 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 * netlbl_domhsh_validate - Validate a new domain mapping entry 250 * @entry: the entry to validate 251 * 252 * This function validates the new domain mapping entry to ensure that it is 253 * a valid entry. Returns zero on success, negative values on failure. 254 * 255 */ 256 static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry) 257 { 258 struct netlbl_af4list *iter4; 259 struct netlbl_domaddr4_map *map4; 260 #if IS_ENABLED(CONFIG_IPV6) 261 struct netlbl_af6list *iter6; 262 struct netlbl_domaddr6_map *map6; 263 #endif /* IPv6 */ 264 265 if (entry == NULL) 266 return -EINVAL; 267 268 switch (entry->def.type) { 269 case NETLBL_NLTYPE_UNLABELED: 270 if (entry->def.cipso != NULL || entry->def.addrsel != NULL) 271 return -EINVAL; 272 break; 273 case NETLBL_NLTYPE_CIPSOV4: 274 if (entry->def.cipso == NULL) 275 return -EINVAL; 276 break; 277 case NETLBL_NLTYPE_ADDRSELECT: 278 netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) { 279 map4 = netlbl_domhsh_addr4_entry(iter4); 280 switch (map4->def.type) { 281 case NETLBL_NLTYPE_UNLABELED: 282 if (map4->def.cipso != NULL) 283 return -EINVAL; 284 break; 285 case NETLBL_NLTYPE_CIPSOV4: 286 if (map4->def.cipso == NULL) 287 return -EINVAL; 288 break; 289 default: 290 return -EINVAL; 291 } 292 } 293 #if IS_ENABLED(CONFIG_IPV6) 294 netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) { 295 map6 = netlbl_domhsh_addr6_entry(iter6); 296 switch (map6->def.type) { 297 case NETLBL_NLTYPE_UNLABELED: 298 break; 299 default: 300 return -EINVAL; 301 } 302 } 303 #endif /* IPv6 */ 304 break; 305 default: 306 return -EINVAL; 307 } 308 309 return 0; 310 } 311 312 /* 313 * Domain Hash Table Functions 314 */ 315 316 /** 317 * netlbl_domhsh_init - Init for the domain hash 318 * @size: the number of bits to use for the hash buckets 319 * 320 * Description: 321 * Initializes the domain hash table, should be called only by 322 * netlbl_user_init() during initialization. Returns zero on success, non-zero 323 * values on error. 324 * 325 */ 326 int __init netlbl_domhsh_init(u32 size) 327 { 328 u32 iter; 329 struct netlbl_domhsh_tbl *hsh_tbl; 330 331 if (size == 0) 332 return -EINVAL; 333 334 hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL); 335 if (hsh_tbl == NULL) 336 return -ENOMEM; 337 hsh_tbl->size = 1 << size; 338 hsh_tbl->tbl = kcalloc(hsh_tbl->size, 339 sizeof(struct list_head), 340 GFP_KERNEL); 341 if (hsh_tbl->tbl == NULL) { 342 kfree(hsh_tbl); 343 return -ENOMEM; 344 } 345 for (iter = 0; iter < hsh_tbl->size; iter++) 346 INIT_LIST_HEAD(&hsh_tbl->tbl[iter]); 347 348 spin_lock(&netlbl_domhsh_lock); 349 rcu_assign_pointer(netlbl_domhsh, hsh_tbl); 350 spin_unlock(&netlbl_domhsh_lock); 351 352 return 0; 353 } 354 355 /** 356 * netlbl_domhsh_add - Adds a entry to the domain hash table 357 * @entry: the entry to add 358 * @audit_info: NetLabel audit information 359 * 360 * Description: 361 * Adds a new entry to the domain hash table and handles any updates to the 362 * lower level protocol handler (i.e. CIPSO). Returns zero on success, 363 * negative on failure. 364 * 365 */ 366 int netlbl_domhsh_add(struct netlbl_dom_map *entry, 367 struct netlbl_audit *audit_info) 368 { 369 int ret_val = 0; 370 struct netlbl_dom_map *entry_old; 371 struct netlbl_af4list *iter4; 372 struct netlbl_af4list *tmp4; 373 #if IS_ENABLED(CONFIG_IPV6) 374 struct netlbl_af6list *iter6; 375 struct netlbl_af6list *tmp6; 376 #endif /* IPv6 */ 377 378 ret_val = netlbl_domhsh_validate(entry); 379 if (ret_val != 0) 380 return ret_val; 381 382 /* XXX - we can remove this RCU read lock as the spinlock protects the 383 * entire function, but before we do we need to fixup the 384 * netlbl_af[4,6]list RCU functions to do "the right thing" with 385 * respect to rcu_dereference() when only a spinlock is held. */ 386 rcu_read_lock(); 387 spin_lock(&netlbl_domhsh_lock); 388 if (entry->domain != NULL) 389 entry_old = netlbl_domhsh_search(entry->domain); 390 else 391 entry_old = netlbl_domhsh_search_def(entry->domain); 392 if (entry_old == NULL) { 393 entry->valid = 1; 394 395 if (entry->domain != NULL) { 396 u32 bkt = netlbl_domhsh_hash(entry->domain); 397 list_add_tail_rcu(&entry->list, 398 &rcu_dereference(netlbl_domhsh)->tbl[bkt]); 399 } else { 400 INIT_LIST_HEAD(&entry->list); 401 rcu_assign_pointer(netlbl_domhsh_def, entry); 402 } 403 404 if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) { 405 netlbl_af4list_foreach_rcu(iter4, 406 &entry->def.addrsel->list4) 407 netlbl_domhsh_audit_add(entry, iter4, NULL, 408 ret_val, audit_info); 409 #if IS_ENABLED(CONFIG_IPV6) 410 netlbl_af6list_foreach_rcu(iter6, 411 &entry->def.addrsel->list6) 412 netlbl_domhsh_audit_add(entry, NULL, iter6, 413 ret_val, audit_info); 414 #endif /* IPv6 */ 415 } else 416 netlbl_domhsh_audit_add(entry, NULL, NULL, 417 ret_val, audit_info); 418 } else if (entry_old->def.type == NETLBL_NLTYPE_ADDRSELECT && 419 entry->def.type == NETLBL_NLTYPE_ADDRSELECT) { 420 struct list_head *old_list4; 421 struct list_head *old_list6; 422 423 old_list4 = &entry_old->def.addrsel->list4; 424 old_list6 = &entry_old->def.addrsel->list6; 425 426 /* we only allow the addition of address selectors if all of 427 * the selectors do not exist in the existing domain map */ 428 netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) 429 if (netlbl_af4list_search_exact(iter4->addr, 430 iter4->mask, 431 old_list4)) { 432 ret_val = -EEXIST; 433 goto add_return; 434 } 435 #if IS_ENABLED(CONFIG_IPV6) 436 netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) 437 if (netlbl_af6list_search_exact(&iter6->addr, 438 &iter6->mask, 439 old_list6)) { 440 ret_val = -EEXIST; 441 goto add_return; 442 } 443 #endif /* IPv6 */ 444 445 netlbl_af4list_foreach_safe(iter4, tmp4, 446 &entry->def.addrsel->list4) { 447 netlbl_af4list_remove_entry(iter4); 448 iter4->valid = 1; 449 ret_val = netlbl_af4list_add(iter4, old_list4); 450 netlbl_domhsh_audit_add(entry_old, iter4, NULL, 451 ret_val, audit_info); 452 if (ret_val != 0) 453 goto add_return; 454 } 455 #if IS_ENABLED(CONFIG_IPV6) 456 netlbl_af6list_foreach_safe(iter6, tmp6, 457 &entry->def.addrsel->list6) { 458 netlbl_af6list_remove_entry(iter6); 459 iter6->valid = 1; 460 ret_val = netlbl_af6list_add(iter6, old_list6); 461 netlbl_domhsh_audit_add(entry_old, NULL, iter6, 462 ret_val, audit_info); 463 if (ret_val != 0) 464 goto add_return; 465 } 466 #endif /* IPv6 */ 467 } else 468 ret_val = -EINVAL; 469 470 add_return: 471 spin_unlock(&netlbl_domhsh_lock); 472 rcu_read_unlock(); 473 return ret_val; 474 } 475 476 /** 477 * netlbl_domhsh_add_default - Adds the default entry to the domain hash table 478 * @entry: the entry to add 479 * @audit_info: NetLabel audit information 480 * 481 * Description: 482 * Adds a new default entry to the domain hash table and handles any updates 483 * to the lower level protocol handler (i.e. CIPSO). Returns zero on success, 484 * negative on failure. 485 * 486 */ 487 int netlbl_domhsh_add_default(struct netlbl_dom_map *entry, 488 struct netlbl_audit *audit_info) 489 { 490 return netlbl_domhsh_add(entry, audit_info); 491 } 492 493 /** 494 * netlbl_domhsh_remove_entry - Removes a given entry from the domain table 495 * @entry: the entry to remove 496 * @audit_info: NetLabel audit information 497 * 498 * Description: 499 * Removes an entry from the domain hash table and handles any updates to the 500 * lower level protocol handler (i.e. CIPSO). Caller is responsible for 501 * ensuring that the RCU read lock is held. Returns zero on success, negative 502 * on failure. 503 * 504 */ 505 int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, 506 struct netlbl_audit *audit_info) 507 { 508 int ret_val = 0; 509 struct audit_buffer *audit_buf; 510 511 if (entry == NULL) 512 return -ENOENT; 513 514 spin_lock(&netlbl_domhsh_lock); 515 if (entry->valid) { 516 entry->valid = 0; 517 if (entry != rcu_dereference(netlbl_domhsh_def)) 518 list_del_rcu(&entry->list); 519 else 520 RCU_INIT_POINTER(netlbl_domhsh_def, NULL); 521 } else 522 ret_val = -ENOENT; 523 spin_unlock(&netlbl_domhsh_lock); 524 525 audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info); 526 if (audit_buf != NULL) { 527 audit_log_format(audit_buf, 528 " nlbl_domain=%s res=%u", 529 entry->domain ? entry->domain : "(default)", 530 ret_val == 0 ? 1 : 0); 531 audit_log_end(audit_buf); 532 } 533 534 if (ret_val == 0) { 535 struct netlbl_af4list *iter4; 536 struct netlbl_domaddr4_map *map4; 537 538 switch (entry->def.type) { 539 case NETLBL_NLTYPE_ADDRSELECT: 540 netlbl_af4list_foreach_rcu(iter4, 541 &entry->def.addrsel->list4) { 542 map4 = netlbl_domhsh_addr4_entry(iter4); 543 cipso_v4_doi_putdef(map4->def.cipso); 544 } 545 /* no need to check the IPv6 list since we currently 546 * support only unlabeled protocols for IPv6 */ 547 break; 548 case NETLBL_NLTYPE_CIPSOV4: 549 cipso_v4_doi_putdef(entry->def.cipso); 550 break; 551 } 552 call_rcu(&entry->rcu, netlbl_domhsh_free_entry); 553 } 554 555 return ret_val; 556 } 557 558 /** 559 * netlbl_domhsh_remove_af4 - Removes an address selector entry 560 * @domain: the domain 561 * @addr: IPv4 address 562 * @mask: IPv4 address mask 563 * @audit_info: NetLabel audit information 564 * 565 * Description: 566 * Removes an individual address selector from a domain mapping and potentially 567 * the entire mapping if it is empty. Returns zero on success, negative values 568 * on failure. 569 * 570 */ 571 int netlbl_domhsh_remove_af4(const char *domain, 572 const struct in_addr *addr, 573 const struct in_addr *mask, 574 struct netlbl_audit *audit_info) 575 { 576 struct netlbl_dom_map *entry_map; 577 struct netlbl_af4list *entry_addr; 578 struct netlbl_af4list *iter4; 579 #if IS_ENABLED(CONFIG_IPV6) 580 struct netlbl_af6list *iter6; 581 #endif /* IPv6 */ 582 struct netlbl_domaddr4_map *entry; 583 584 rcu_read_lock(); 585 586 if (domain) 587 entry_map = netlbl_domhsh_search(domain); 588 else 589 entry_map = netlbl_domhsh_search_def(domain); 590 if (entry_map == NULL || 591 entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT) 592 goto remove_af4_failure; 593 594 spin_lock(&netlbl_domhsh_lock); 595 entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr, 596 &entry_map->def.addrsel->list4); 597 spin_unlock(&netlbl_domhsh_lock); 598 599 if (entry_addr == NULL) 600 goto remove_af4_failure; 601 netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4) 602 goto remove_af4_single_addr; 603 #if IS_ENABLED(CONFIG_IPV6) 604 netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6) 605 goto remove_af4_single_addr; 606 #endif /* IPv6 */ 607 /* the domain mapping is empty so remove it from the mapping table */ 608 netlbl_domhsh_remove_entry(entry_map, audit_info); 609 610 remove_af4_single_addr: 611 rcu_read_unlock(); 612 /* yick, we can't use call_rcu here because we don't have a rcu head 613 * pointer but hopefully this should be a rare case so the pause 614 * shouldn't be a problem */ 615 synchronize_rcu(); 616 entry = netlbl_domhsh_addr4_entry(entry_addr); 617 cipso_v4_doi_putdef(entry->def.cipso); 618 kfree(entry); 619 return 0; 620 621 remove_af4_failure: 622 rcu_read_unlock(); 623 return -ENOENT; 624 } 625 626 /** 627 * netlbl_domhsh_remove - Removes an entry from the domain hash table 628 * @domain: the domain to remove 629 * @audit_info: NetLabel audit information 630 * 631 * Description: 632 * Removes an entry from the domain hash table and handles any updates to the 633 * lower level protocol handler (i.e. CIPSO). Returns zero on success, 634 * negative on failure. 635 * 636 */ 637 int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) 638 { 639 int ret_val; 640 struct netlbl_dom_map *entry; 641 642 rcu_read_lock(); 643 if (domain) 644 entry = netlbl_domhsh_search(domain); 645 else 646 entry = netlbl_domhsh_search_def(domain); 647 ret_val = netlbl_domhsh_remove_entry(entry, audit_info); 648 rcu_read_unlock(); 649 650 return ret_val; 651 } 652 653 /** 654 * netlbl_domhsh_remove_default - Removes the default entry from the table 655 * @audit_info: NetLabel audit information 656 * 657 * Description: 658 * Removes/resets the default entry for the domain hash table and handles any 659 * updates to the lower level protocol handler (i.e. CIPSO). Returns zero on 660 * success, non-zero on failure. 661 * 662 */ 663 int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info) 664 { 665 return netlbl_domhsh_remove(NULL, audit_info); 666 } 667 668 /** 669 * netlbl_domhsh_getentry - Get an entry from the domain hash table 670 * @domain: the domain name to search for 671 * 672 * Description: 673 * Look through the domain hash table searching for an entry to match @domain, 674 * return a pointer to a copy of the entry or NULL. The caller is responsible 675 * for ensuring that rcu_read_[un]lock() is called. 676 * 677 */ 678 struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain) 679 { 680 return netlbl_domhsh_search_def(domain); 681 } 682 683 /** 684 * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table 685 * @domain: the domain name to search for 686 * @addr: the IP address to search for 687 * 688 * Description: 689 * Look through the domain hash table searching for an entry to match @domain 690 * and @addr, return a pointer to a copy of the entry or NULL. The caller is 691 * responsible for ensuring that rcu_read_[un]lock() is called. 692 * 693 */ 694 struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain, 695 __be32 addr) 696 { 697 struct netlbl_dom_map *dom_iter; 698 struct netlbl_af4list *addr_iter; 699 700 dom_iter = netlbl_domhsh_search_def(domain); 701 if (dom_iter == NULL) 702 return NULL; 703 704 if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT) 705 return &dom_iter->def; 706 addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4); 707 if (addr_iter == NULL) 708 return NULL; 709 return &(netlbl_domhsh_addr4_entry(addr_iter)->def); 710 } 711 712 #if IS_ENABLED(CONFIG_IPV6) 713 /** 714 * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table 715 * @domain: the domain name to search for 716 * @addr: the IP address to search for 717 * 718 * Description: 719 * Look through the domain hash table searching for an entry to match @domain 720 * and @addr, return a pointer to a copy of the entry or NULL. The caller is 721 * responsible for ensuring that rcu_read_[un]lock() is called. 722 * 723 */ 724 struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain, 725 const struct in6_addr *addr) 726 { 727 struct netlbl_dom_map *dom_iter; 728 struct netlbl_af6list *addr_iter; 729 730 dom_iter = netlbl_domhsh_search_def(domain); 731 if (dom_iter == NULL) 732 return NULL; 733 734 if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT) 735 return &dom_iter->def; 736 addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6); 737 if (addr_iter == NULL) 738 return NULL; 739 return &(netlbl_domhsh_addr6_entry(addr_iter)->def); 740 } 741 #endif /* IPv6 */ 742 743 /** 744 * netlbl_domhsh_walk - Iterate through the domain mapping hash table 745 * @skip_bkt: the number of buckets to skip at the start 746 * @skip_chain: the number of entries to skip in the first iterated bucket 747 * @callback: callback for each entry 748 * @cb_arg: argument for the callback function 749 * 750 * Description: 751 * Interate over the domain mapping hash table, skipping the first @skip_bkt 752 * buckets and @skip_chain entries. For each entry in the table call 753 * @callback, if @callback returns a negative value stop 'walking' through the 754 * table and return. Updates the values in @skip_bkt and @skip_chain on 755 * return. Returns zero on success, negative values on failure. 756 * 757 */ 758 int netlbl_domhsh_walk(u32 *skip_bkt, 759 u32 *skip_chain, 760 int (*callback) (struct netlbl_dom_map *entry, void *arg), 761 void *cb_arg) 762 { 763 int ret_val = -ENOENT; 764 u32 iter_bkt; 765 struct list_head *iter_list; 766 struct netlbl_dom_map *iter_entry; 767 u32 chain_cnt = 0; 768 769 rcu_read_lock(); 770 for (iter_bkt = *skip_bkt; 771 iter_bkt < rcu_dereference(netlbl_domhsh)->size; 772 iter_bkt++, chain_cnt = 0) { 773 iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt]; 774 list_for_each_entry_rcu(iter_entry, iter_list, list) 775 if (iter_entry->valid) { 776 if (chain_cnt++ < *skip_chain) 777 continue; 778 ret_val = callback(iter_entry, cb_arg); 779 if (ret_val < 0) { 780 chain_cnt--; 781 goto walk_return; 782 } 783 } 784 } 785 786 walk_return: 787 rcu_read_unlock(); 788 *skip_bkt = iter_bkt; 789 *skip_chain = chain_cnt; 790 return ret_val; 791 } 792