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