xref: /linux/net/netlabel/netlabel_domainhash.c (revision 60e13231561b3a4c5269bfa1ef6c0569ad6f28ec)
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