162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NetLabel Domain Hash Table
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file manages the domain hash table that NetLabel uses to determine
662306a36Sopenharmony_ci * which network labeling protocol to use for a given domain.  The NetLabel
762306a36Sopenharmony_ci * system manages static and dynamic label mappings for network protocols such
862306a36Sopenharmony_ci * as CIPSO and RIPSO.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Author: Paul Moore <paul@paul-moore.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/rculist.h>
1962306a36Sopenharmony_ci#include <linux/skbuff.h>
2062306a36Sopenharmony_ci#include <linux/spinlock.h>
2162306a36Sopenharmony_ci#include <linux/string.h>
2262306a36Sopenharmony_ci#include <linux/audit.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <net/netlabel.h>
2562306a36Sopenharmony_ci#include <net/cipso_ipv4.h>
2662306a36Sopenharmony_ci#include <net/calipso.h>
2762306a36Sopenharmony_ci#include <asm/bug.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "netlabel_mgmt.h"
3062306a36Sopenharmony_ci#include "netlabel_addrlist.h"
3162306a36Sopenharmony_ci#include "netlabel_calipso.h"
3262306a36Sopenharmony_ci#include "netlabel_domainhash.h"
3362306a36Sopenharmony_ci#include "netlabel_user.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct netlbl_domhsh_tbl {
3662306a36Sopenharmony_ci	struct list_head *tbl;
3762306a36Sopenharmony_ci	u32 size;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Domain hash table */
4162306a36Sopenharmony_ci/* updates should be so rare that having one spinlock for the entire hash table
4262306a36Sopenharmony_ci * should be okay */
4362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(netlbl_domhsh_lock);
4462306a36Sopenharmony_ci#define netlbl_domhsh_rcu_deref(p) \
4562306a36Sopenharmony_ci	rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
4662306a36Sopenharmony_cistatic struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
4762306a36Sopenharmony_cistatic struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
4862306a36Sopenharmony_cistatic struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Domain Hash Table Helper Functions
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/**
5562306a36Sopenharmony_ci * netlbl_domhsh_free_entry - Frees a domain hash table entry
5662306a36Sopenharmony_ci * @entry: the entry's RCU field
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Description:
5962306a36Sopenharmony_ci * This function is designed to be used as a callback to the call_rcu()
6062306a36Sopenharmony_ci * function so that the memory allocated to a hash table entry can be released
6162306a36Sopenharmony_ci * safely.
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic void netlbl_domhsh_free_entry(struct rcu_head *entry)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct netlbl_dom_map *ptr;
6762306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
6862306a36Sopenharmony_ci	struct netlbl_af4list *tmp4;
6962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7062306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
7162306a36Sopenharmony_ci	struct netlbl_af6list *tmp6;
7262306a36Sopenharmony_ci#endif /* IPv6 */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	ptr = container_of(entry, struct netlbl_dom_map, rcu);
7562306a36Sopenharmony_ci	if (ptr->def.type == NETLBL_NLTYPE_ADDRSELECT) {
7662306a36Sopenharmony_ci		netlbl_af4list_foreach_safe(iter4, tmp4,
7762306a36Sopenharmony_ci					    &ptr->def.addrsel->list4) {
7862306a36Sopenharmony_ci			netlbl_af4list_remove_entry(iter4);
7962306a36Sopenharmony_ci			kfree(netlbl_domhsh_addr4_entry(iter4));
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
8262306a36Sopenharmony_ci		netlbl_af6list_foreach_safe(iter6, tmp6,
8362306a36Sopenharmony_ci					    &ptr->def.addrsel->list6) {
8462306a36Sopenharmony_ci			netlbl_af6list_remove_entry(iter6);
8562306a36Sopenharmony_ci			kfree(netlbl_domhsh_addr6_entry(iter6));
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci#endif /* IPv6 */
8862306a36Sopenharmony_ci		kfree(ptr->def.addrsel);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	kfree(ptr->domain);
9162306a36Sopenharmony_ci	kfree(ptr);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * netlbl_domhsh_hash - Hashing function for the domain hash table
9662306a36Sopenharmony_ci * @key: the domain name to hash
9762306a36Sopenharmony_ci *
9862306a36Sopenharmony_ci * Description:
9962306a36Sopenharmony_ci * This is the hashing function for the domain hash table, it returns the
10062306a36Sopenharmony_ci * correct bucket number for the domain.  The caller is responsible for
10162306a36Sopenharmony_ci * ensuring that the hash table is protected with either a RCU read lock or the
10262306a36Sopenharmony_ci * hash table lock.
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic u32 netlbl_domhsh_hash(const char *key)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	u32 iter;
10862306a36Sopenharmony_ci	u32 val;
10962306a36Sopenharmony_ci	u32 len;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* This is taken (with slight modification) from
11262306a36Sopenharmony_ci	 * security/selinux/ss/symtab.c:symhash() */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	for (iter = 0, val = 0, len = strlen(key); iter < len; iter++)
11562306a36Sopenharmony_ci		val = (val << 4 | (val >> (8 * sizeof(u32) - 4))) ^ key[iter];
11662306a36Sopenharmony_ci	return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic bool netlbl_family_match(u16 f1, u16 f2)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * netlbl_domhsh_search - Search for a domain entry
12662306a36Sopenharmony_ci * @domain: the domain
12762306a36Sopenharmony_ci * @family: the address family
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Description:
13062306a36Sopenharmony_ci * Searches the domain hash table and returns a pointer to the hash table
13162306a36Sopenharmony_ci * entry if found, otherwise NULL is returned.  @family may be %AF_UNSPEC
13262306a36Sopenharmony_ci * which matches any address family entries.  The caller is responsible for
13362306a36Sopenharmony_ci * ensuring that the hash table is protected with either a RCU read lock or the
13462306a36Sopenharmony_ci * hash table lock.
13562306a36Sopenharmony_ci *
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic struct netlbl_dom_map *netlbl_domhsh_search(const char *domain,
13862306a36Sopenharmony_ci						   u16 family)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	u32 bkt;
14162306a36Sopenharmony_ci	struct list_head *bkt_list;
14262306a36Sopenharmony_ci	struct netlbl_dom_map *iter;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (domain != NULL) {
14562306a36Sopenharmony_ci		bkt = netlbl_domhsh_hash(domain);
14662306a36Sopenharmony_ci		bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
14762306a36Sopenharmony_ci		list_for_each_entry_rcu(iter, bkt_list, list,
14862306a36Sopenharmony_ci					lockdep_is_held(&netlbl_domhsh_lock))
14962306a36Sopenharmony_ci			if (iter->valid &&
15062306a36Sopenharmony_ci			    netlbl_family_match(iter->family, family) &&
15162306a36Sopenharmony_ci			    strcmp(iter->domain, domain) == 0)
15262306a36Sopenharmony_ci				return iter;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return NULL;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * netlbl_domhsh_search_def - Search for a domain entry
16062306a36Sopenharmony_ci * @domain: the domain
16162306a36Sopenharmony_ci * @family: the address family
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * Description:
16462306a36Sopenharmony_ci * Searches the domain hash table and returns a pointer to the hash table
16562306a36Sopenharmony_ci * entry if an exact match is found, if an exact match is not present in the
16662306a36Sopenharmony_ci * hash table then the default entry is returned if valid otherwise NULL is
16762306a36Sopenharmony_ci * returned.  @family may be %AF_UNSPEC which matches any address family
16862306a36Sopenharmony_ci * entries.  The caller is responsible ensuring that the hash table is
16962306a36Sopenharmony_ci * protected with either a RCU read lock or the hash table lock.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain,
17362306a36Sopenharmony_ci						       u16 family)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct netlbl_dom_map *entry;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	entry = netlbl_domhsh_search(domain, family);
17862306a36Sopenharmony_ci	if (entry != NULL)
17962306a36Sopenharmony_ci		return entry;
18062306a36Sopenharmony_ci	if (family == AF_INET || family == AF_UNSPEC) {
18162306a36Sopenharmony_ci		entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4);
18262306a36Sopenharmony_ci		if (entry != NULL && entry->valid)
18362306a36Sopenharmony_ci			return entry;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	if (family == AF_INET6 || family == AF_UNSPEC) {
18662306a36Sopenharmony_ci		entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6);
18762306a36Sopenharmony_ci		if (entry != NULL && entry->valid)
18862306a36Sopenharmony_ci			return entry;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return NULL;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/**
19562306a36Sopenharmony_ci * netlbl_domhsh_audit_add - Generate an audit entry for an add event
19662306a36Sopenharmony_ci * @entry: the entry being added
19762306a36Sopenharmony_ci * @addr4: the IPv4 address information
19862306a36Sopenharmony_ci * @addr6: the IPv6 address information
19962306a36Sopenharmony_ci * @result: the result code
20062306a36Sopenharmony_ci * @audit_info: NetLabel audit information
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * Description:
20362306a36Sopenharmony_ci * Generate an audit record for adding a new NetLabel/LSM mapping entry with
20462306a36Sopenharmony_ci * the given information.  Caller is responsible for holding the necessary
20562306a36Sopenharmony_ci * locks.
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
20962306a36Sopenharmony_ci				    struct netlbl_af4list *addr4,
21062306a36Sopenharmony_ci				    struct netlbl_af6list *addr6,
21162306a36Sopenharmony_ci				    int result,
21262306a36Sopenharmony_ci				    struct netlbl_audit *audit_info)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct audit_buffer *audit_buf;
21562306a36Sopenharmony_ci	struct cipso_v4_doi *cipsov4 = NULL;
21662306a36Sopenharmony_ci	struct calipso_doi *calipso = NULL;
21762306a36Sopenharmony_ci	u32 type;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
22062306a36Sopenharmony_ci	if (audit_buf != NULL) {
22162306a36Sopenharmony_ci		audit_log_format(audit_buf, " nlbl_domain=%s",
22262306a36Sopenharmony_ci				 entry->domain ? entry->domain : "(default)");
22362306a36Sopenharmony_ci		if (addr4 != NULL) {
22462306a36Sopenharmony_ci			struct netlbl_domaddr4_map *map4;
22562306a36Sopenharmony_ci			map4 = netlbl_domhsh_addr4_entry(addr4);
22662306a36Sopenharmony_ci			type = map4->def.type;
22762306a36Sopenharmony_ci			cipsov4 = map4->def.cipso;
22862306a36Sopenharmony_ci			netlbl_af4list_audit_addr(audit_buf, 0, NULL,
22962306a36Sopenharmony_ci						  addr4->addr, addr4->mask);
23062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
23162306a36Sopenharmony_ci		} else if (addr6 != NULL) {
23262306a36Sopenharmony_ci			struct netlbl_domaddr6_map *map6;
23362306a36Sopenharmony_ci			map6 = netlbl_domhsh_addr6_entry(addr6);
23462306a36Sopenharmony_ci			type = map6->def.type;
23562306a36Sopenharmony_ci			calipso = map6->def.calipso;
23662306a36Sopenharmony_ci			netlbl_af6list_audit_addr(audit_buf, 0, NULL,
23762306a36Sopenharmony_ci						  &addr6->addr, &addr6->mask);
23862306a36Sopenharmony_ci#endif /* IPv6 */
23962306a36Sopenharmony_ci		} else {
24062306a36Sopenharmony_ci			type = entry->def.type;
24162306a36Sopenharmony_ci			cipsov4 = entry->def.cipso;
24262306a36Sopenharmony_ci			calipso = entry->def.calipso;
24362306a36Sopenharmony_ci		}
24462306a36Sopenharmony_ci		switch (type) {
24562306a36Sopenharmony_ci		case NETLBL_NLTYPE_UNLABELED:
24662306a36Sopenharmony_ci			audit_log_format(audit_buf, " nlbl_protocol=unlbl");
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci		case NETLBL_NLTYPE_CIPSOV4:
24962306a36Sopenharmony_ci			BUG_ON(cipsov4 == NULL);
25062306a36Sopenharmony_ci			audit_log_format(audit_buf,
25162306a36Sopenharmony_ci					 " nlbl_protocol=cipsov4 cipso_doi=%u",
25262306a36Sopenharmony_ci					 cipsov4->doi);
25362306a36Sopenharmony_ci			break;
25462306a36Sopenharmony_ci		case NETLBL_NLTYPE_CALIPSO:
25562306a36Sopenharmony_ci			BUG_ON(calipso == NULL);
25662306a36Sopenharmony_ci			audit_log_format(audit_buf,
25762306a36Sopenharmony_ci					 " nlbl_protocol=calipso calipso_doi=%u",
25862306a36Sopenharmony_ci					 calipso->doi);
25962306a36Sopenharmony_ci			break;
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci		audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
26262306a36Sopenharmony_ci		audit_log_end(audit_buf);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/**
26762306a36Sopenharmony_ci * netlbl_domhsh_validate - Validate a new domain mapping entry
26862306a36Sopenharmony_ci * @entry: the entry to validate
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * This function validates the new domain mapping entry to ensure that it is
27162306a36Sopenharmony_ci * a valid entry.  Returns zero on success, negative values on failure.
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
27762306a36Sopenharmony_ci	struct netlbl_domaddr4_map *map4;
27862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
27962306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
28062306a36Sopenharmony_ci	struct netlbl_domaddr6_map *map6;
28162306a36Sopenharmony_ci#endif /* IPv6 */
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (entry == NULL)
28462306a36Sopenharmony_ci		return -EINVAL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (entry->family != AF_INET && entry->family != AF_INET6 &&
28762306a36Sopenharmony_ci	    (entry->family != AF_UNSPEC ||
28862306a36Sopenharmony_ci	     entry->def.type != NETLBL_NLTYPE_UNLABELED))
28962306a36Sopenharmony_ci		return -EINVAL;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	switch (entry->def.type) {
29262306a36Sopenharmony_ci	case NETLBL_NLTYPE_UNLABELED:
29362306a36Sopenharmony_ci		if (entry->def.cipso != NULL || entry->def.calipso != NULL ||
29462306a36Sopenharmony_ci		    entry->def.addrsel != NULL)
29562306a36Sopenharmony_ci			return -EINVAL;
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci	case NETLBL_NLTYPE_CIPSOV4:
29862306a36Sopenharmony_ci		if (entry->family != AF_INET ||
29962306a36Sopenharmony_ci		    entry->def.cipso == NULL)
30062306a36Sopenharmony_ci			return -EINVAL;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	case NETLBL_NLTYPE_CALIPSO:
30362306a36Sopenharmony_ci		if (entry->family != AF_INET6 ||
30462306a36Sopenharmony_ci		    entry->def.calipso == NULL)
30562306a36Sopenharmony_ci			return -EINVAL;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case NETLBL_NLTYPE_ADDRSELECT:
30862306a36Sopenharmony_ci		netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) {
30962306a36Sopenharmony_ci			map4 = netlbl_domhsh_addr4_entry(iter4);
31062306a36Sopenharmony_ci			switch (map4->def.type) {
31162306a36Sopenharmony_ci			case NETLBL_NLTYPE_UNLABELED:
31262306a36Sopenharmony_ci				if (map4->def.cipso != NULL)
31362306a36Sopenharmony_ci					return -EINVAL;
31462306a36Sopenharmony_ci				break;
31562306a36Sopenharmony_ci			case NETLBL_NLTYPE_CIPSOV4:
31662306a36Sopenharmony_ci				if (map4->def.cipso == NULL)
31762306a36Sopenharmony_ci					return -EINVAL;
31862306a36Sopenharmony_ci				break;
31962306a36Sopenharmony_ci			default:
32062306a36Sopenharmony_ci				return -EINVAL;
32162306a36Sopenharmony_ci			}
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32462306a36Sopenharmony_ci		netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) {
32562306a36Sopenharmony_ci			map6 = netlbl_domhsh_addr6_entry(iter6);
32662306a36Sopenharmony_ci			switch (map6->def.type) {
32762306a36Sopenharmony_ci			case NETLBL_NLTYPE_UNLABELED:
32862306a36Sopenharmony_ci				if (map6->def.calipso != NULL)
32962306a36Sopenharmony_ci					return -EINVAL;
33062306a36Sopenharmony_ci				break;
33162306a36Sopenharmony_ci			case NETLBL_NLTYPE_CALIPSO:
33262306a36Sopenharmony_ci				if (map6->def.calipso == NULL)
33362306a36Sopenharmony_ci					return -EINVAL;
33462306a36Sopenharmony_ci				break;
33562306a36Sopenharmony_ci			default:
33662306a36Sopenharmony_ci				return -EINVAL;
33762306a36Sopenharmony_ci			}
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci#endif /* IPv6 */
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		return -EINVAL;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/*
34962306a36Sopenharmony_ci * Domain Hash Table Functions
35062306a36Sopenharmony_ci */
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/**
35362306a36Sopenharmony_ci * netlbl_domhsh_init - Init for the domain hash
35462306a36Sopenharmony_ci * @size: the number of bits to use for the hash buckets
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * Description:
35762306a36Sopenharmony_ci * Initializes the domain hash table, should be called only by
35862306a36Sopenharmony_ci * netlbl_user_init() during initialization.  Returns zero on success, non-zero
35962306a36Sopenharmony_ci * values on error.
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_ciint __init netlbl_domhsh_init(u32 size)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	u32 iter;
36562306a36Sopenharmony_ci	struct netlbl_domhsh_tbl *hsh_tbl;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (size == 0)
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
37162306a36Sopenharmony_ci	if (hsh_tbl == NULL)
37262306a36Sopenharmony_ci		return -ENOMEM;
37362306a36Sopenharmony_ci	hsh_tbl->size = 1 << size;
37462306a36Sopenharmony_ci	hsh_tbl->tbl = kcalloc(hsh_tbl->size,
37562306a36Sopenharmony_ci			       sizeof(struct list_head),
37662306a36Sopenharmony_ci			       GFP_KERNEL);
37762306a36Sopenharmony_ci	if (hsh_tbl->tbl == NULL) {
37862306a36Sopenharmony_ci		kfree(hsh_tbl);
37962306a36Sopenharmony_ci		return -ENOMEM;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	for (iter = 0; iter < hsh_tbl->size; iter++)
38262306a36Sopenharmony_ci		INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	spin_lock(&netlbl_domhsh_lock);
38562306a36Sopenharmony_ci	rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
38662306a36Sopenharmony_ci	spin_unlock(&netlbl_domhsh_lock);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/**
39262306a36Sopenharmony_ci * netlbl_domhsh_add - Adds a entry to the domain hash table
39362306a36Sopenharmony_ci * @entry: the entry to add
39462306a36Sopenharmony_ci * @audit_info: NetLabel audit information
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * Description:
39762306a36Sopenharmony_ci * Adds a new entry to the domain hash table and handles any updates to the
39862306a36Sopenharmony_ci * lower level protocol handler (i.e. CIPSO).  @entry->family may be set to
39962306a36Sopenharmony_ci * %AF_UNSPEC which will add an entry that matches all address families.  This
40062306a36Sopenharmony_ci * is only useful for the unlabelled type and will only succeed if there is no
40162306a36Sopenharmony_ci * existing entry for any address family with the same domain.  Returns zero
40262306a36Sopenharmony_ci * on success, negative on failure.
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_ciint netlbl_domhsh_add(struct netlbl_dom_map *entry,
40662306a36Sopenharmony_ci		      struct netlbl_audit *audit_info)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int ret_val = 0;
40962306a36Sopenharmony_ci	struct netlbl_dom_map *entry_old, *entry_b;
41062306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
41162306a36Sopenharmony_ci	struct netlbl_af4list *tmp4;
41262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
41362306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
41462306a36Sopenharmony_ci	struct netlbl_af6list *tmp6;
41562306a36Sopenharmony_ci#endif /* IPv6 */
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	ret_val = netlbl_domhsh_validate(entry);
41862306a36Sopenharmony_ci	if (ret_val != 0)
41962306a36Sopenharmony_ci		return ret_val;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* XXX - we can remove this RCU read lock as the spinlock protects the
42262306a36Sopenharmony_ci	 *       entire function, but before we do we need to fixup the
42362306a36Sopenharmony_ci	 *       netlbl_af[4,6]list RCU functions to do "the right thing" with
42462306a36Sopenharmony_ci	 *       respect to rcu_dereference() when only a spinlock is held. */
42562306a36Sopenharmony_ci	rcu_read_lock();
42662306a36Sopenharmony_ci	spin_lock(&netlbl_domhsh_lock);
42762306a36Sopenharmony_ci	if (entry->domain != NULL)
42862306a36Sopenharmony_ci		entry_old = netlbl_domhsh_search(entry->domain, entry->family);
42962306a36Sopenharmony_ci	else
43062306a36Sopenharmony_ci		entry_old = netlbl_domhsh_search_def(entry->domain,
43162306a36Sopenharmony_ci						     entry->family);
43262306a36Sopenharmony_ci	if (entry_old == NULL) {
43362306a36Sopenharmony_ci		entry->valid = 1;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (entry->domain != NULL) {
43662306a36Sopenharmony_ci			u32 bkt = netlbl_domhsh_hash(entry->domain);
43762306a36Sopenharmony_ci			list_add_tail_rcu(&entry->list,
43862306a36Sopenharmony_ci				    &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
43962306a36Sopenharmony_ci		} else {
44062306a36Sopenharmony_ci			INIT_LIST_HEAD(&entry->list);
44162306a36Sopenharmony_ci			switch (entry->family) {
44262306a36Sopenharmony_ci			case AF_INET:
44362306a36Sopenharmony_ci				rcu_assign_pointer(netlbl_domhsh_def_ipv4,
44462306a36Sopenharmony_ci						   entry);
44562306a36Sopenharmony_ci				break;
44662306a36Sopenharmony_ci			case AF_INET6:
44762306a36Sopenharmony_ci				rcu_assign_pointer(netlbl_domhsh_def_ipv6,
44862306a36Sopenharmony_ci						   entry);
44962306a36Sopenharmony_ci				break;
45062306a36Sopenharmony_ci			case AF_UNSPEC:
45162306a36Sopenharmony_ci				if (entry->def.type !=
45262306a36Sopenharmony_ci				    NETLBL_NLTYPE_UNLABELED) {
45362306a36Sopenharmony_ci					ret_val = -EINVAL;
45462306a36Sopenharmony_ci					goto add_return;
45562306a36Sopenharmony_ci				}
45662306a36Sopenharmony_ci				entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC);
45762306a36Sopenharmony_ci				if (entry_b == NULL) {
45862306a36Sopenharmony_ci					ret_val = -ENOMEM;
45962306a36Sopenharmony_ci					goto add_return;
46062306a36Sopenharmony_ci				}
46162306a36Sopenharmony_ci				entry_b->family = AF_INET6;
46262306a36Sopenharmony_ci				entry_b->def.type = NETLBL_NLTYPE_UNLABELED;
46362306a36Sopenharmony_ci				entry_b->valid = 1;
46462306a36Sopenharmony_ci				entry->family = AF_INET;
46562306a36Sopenharmony_ci				rcu_assign_pointer(netlbl_domhsh_def_ipv4,
46662306a36Sopenharmony_ci						   entry);
46762306a36Sopenharmony_ci				rcu_assign_pointer(netlbl_domhsh_def_ipv6,
46862306a36Sopenharmony_ci						   entry_b);
46962306a36Sopenharmony_ci				break;
47062306a36Sopenharmony_ci			default:
47162306a36Sopenharmony_ci				/* Already checked in
47262306a36Sopenharmony_ci				 * netlbl_domhsh_validate(). */
47362306a36Sopenharmony_ci				ret_val = -EINVAL;
47462306a36Sopenharmony_ci				goto add_return;
47562306a36Sopenharmony_ci			}
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
47962306a36Sopenharmony_ci			netlbl_af4list_foreach_rcu(iter4,
48062306a36Sopenharmony_ci						   &entry->def.addrsel->list4)
48162306a36Sopenharmony_ci				netlbl_domhsh_audit_add(entry, iter4, NULL,
48262306a36Sopenharmony_ci							ret_val, audit_info);
48362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
48462306a36Sopenharmony_ci			netlbl_af6list_foreach_rcu(iter6,
48562306a36Sopenharmony_ci						   &entry->def.addrsel->list6)
48662306a36Sopenharmony_ci				netlbl_domhsh_audit_add(entry, NULL, iter6,
48762306a36Sopenharmony_ci							ret_val, audit_info);
48862306a36Sopenharmony_ci#endif /* IPv6 */
48962306a36Sopenharmony_ci		} else
49062306a36Sopenharmony_ci			netlbl_domhsh_audit_add(entry, NULL, NULL,
49162306a36Sopenharmony_ci						ret_val, audit_info);
49262306a36Sopenharmony_ci	} else if (entry_old->def.type == NETLBL_NLTYPE_ADDRSELECT &&
49362306a36Sopenharmony_ci		   entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
49462306a36Sopenharmony_ci		struct list_head *old_list4;
49562306a36Sopenharmony_ci		struct list_head *old_list6;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		old_list4 = &entry_old->def.addrsel->list4;
49862306a36Sopenharmony_ci		old_list6 = &entry_old->def.addrsel->list6;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		/* we only allow the addition of address selectors if all of
50162306a36Sopenharmony_ci		 * the selectors do not exist in the existing domain map */
50262306a36Sopenharmony_ci		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4)
50362306a36Sopenharmony_ci			if (netlbl_af4list_search_exact(iter4->addr,
50462306a36Sopenharmony_ci							iter4->mask,
50562306a36Sopenharmony_ci							old_list4)) {
50662306a36Sopenharmony_ci				ret_val = -EEXIST;
50762306a36Sopenharmony_ci				goto add_return;
50862306a36Sopenharmony_ci			}
50962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
51062306a36Sopenharmony_ci		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6)
51162306a36Sopenharmony_ci			if (netlbl_af6list_search_exact(&iter6->addr,
51262306a36Sopenharmony_ci							&iter6->mask,
51362306a36Sopenharmony_ci							old_list6)) {
51462306a36Sopenharmony_ci				ret_val = -EEXIST;
51562306a36Sopenharmony_ci				goto add_return;
51662306a36Sopenharmony_ci			}
51762306a36Sopenharmony_ci#endif /* IPv6 */
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		netlbl_af4list_foreach_safe(iter4, tmp4,
52062306a36Sopenharmony_ci					    &entry->def.addrsel->list4) {
52162306a36Sopenharmony_ci			netlbl_af4list_remove_entry(iter4);
52262306a36Sopenharmony_ci			iter4->valid = 1;
52362306a36Sopenharmony_ci			ret_val = netlbl_af4list_add(iter4, old_list4);
52462306a36Sopenharmony_ci			netlbl_domhsh_audit_add(entry_old, iter4, NULL,
52562306a36Sopenharmony_ci						ret_val, audit_info);
52662306a36Sopenharmony_ci			if (ret_val != 0)
52762306a36Sopenharmony_ci				goto add_return;
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
53062306a36Sopenharmony_ci		netlbl_af6list_foreach_safe(iter6, tmp6,
53162306a36Sopenharmony_ci					    &entry->def.addrsel->list6) {
53262306a36Sopenharmony_ci			netlbl_af6list_remove_entry(iter6);
53362306a36Sopenharmony_ci			iter6->valid = 1;
53462306a36Sopenharmony_ci			ret_val = netlbl_af6list_add(iter6, old_list6);
53562306a36Sopenharmony_ci			netlbl_domhsh_audit_add(entry_old, NULL, iter6,
53662306a36Sopenharmony_ci						ret_val, audit_info);
53762306a36Sopenharmony_ci			if (ret_val != 0)
53862306a36Sopenharmony_ci				goto add_return;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci#endif /* IPv6 */
54162306a36Sopenharmony_ci		/* cleanup the new entry since we've moved everything over */
54262306a36Sopenharmony_ci		netlbl_domhsh_free_entry(&entry->rcu);
54362306a36Sopenharmony_ci	} else
54462306a36Sopenharmony_ci		ret_val = -EINVAL;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ciadd_return:
54762306a36Sopenharmony_ci	spin_unlock(&netlbl_domhsh_lock);
54862306a36Sopenharmony_ci	rcu_read_unlock();
54962306a36Sopenharmony_ci	return ret_val;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci/**
55362306a36Sopenharmony_ci * netlbl_domhsh_add_default - Adds the default entry to the domain hash table
55462306a36Sopenharmony_ci * @entry: the entry to add
55562306a36Sopenharmony_ci * @audit_info: NetLabel audit information
55662306a36Sopenharmony_ci *
55762306a36Sopenharmony_ci * Description:
55862306a36Sopenharmony_ci * Adds a new default entry to the domain hash table and handles any updates
55962306a36Sopenharmony_ci * to the lower level protocol handler (i.e. CIPSO).  Returns zero on success,
56062306a36Sopenharmony_ci * negative on failure.
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_ciint netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
56462306a36Sopenharmony_ci			      struct netlbl_audit *audit_info)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	return netlbl_domhsh_add(entry, audit_info);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
57162306a36Sopenharmony_ci * @entry: the entry to remove
57262306a36Sopenharmony_ci * @audit_info: NetLabel audit information
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci * Description:
57562306a36Sopenharmony_ci * Removes an entry from the domain hash table and handles any updates to the
57662306a36Sopenharmony_ci * lower level protocol handler (i.e. CIPSO).  Caller is responsible for
57762306a36Sopenharmony_ci * ensuring that the RCU read lock is held.  Returns zero on success, negative
57862306a36Sopenharmony_ci * on failure.
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ciint netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
58262306a36Sopenharmony_ci			       struct netlbl_audit *audit_info)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	int ret_val = 0;
58562306a36Sopenharmony_ci	struct audit_buffer *audit_buf;
58662306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
58762306a36Sopenharmony_ci	struct netlbl_domaddr4_map *map4;
58862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
58962306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
59062306a36Sopenharmony_ci	struct netlbl_domaddr6_map *map6;
59162306a36Sopenharmony_ci#endif /* IPv6 */
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (entry == NULL)
59462306a36Sopenharmony_ci		return -ENOENT;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	spin_lock(&netlbl_domhsh_lock);
59762306a36Sopenharmony_ci	if (entry->valid) {
59862306a36Sopenharmony_ci		entry->valid = 0;
59962306a36Sopenharmony_ci		if (entry == rcu_dereference(netlbl_domhsh_def_ipv4))
60062306a36Sopenharmony_ci			RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL);
60162306a36Sopenharmony_ci		else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6))
60262306a36Sopenharmony_ci			RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL);
60362306a36Sopenharmony_ci		else
60462306a36Sopenharmony_ci			list_del_rcu(&entry->list);
60562306a36Sopenharmony_ci	} else
60662306a36Sopenharmony_ci		ret_val = -ENOENT;
60762306a36Sopenharmony_ci	spin_unlock(&netlbl_domhsh_lock);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (ret_val)
61062306a36Sopenharmony_ci		return ret_val;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
61362306a36Sopenharmony_ci	if (audit_buf != NULL) {
61462306a36Sopenharmony_ci		audit_log_format(audit_buf,
61562306a36Sopenharmony_ci				 " nlbl_domain=%s res=1",
61662306a36Sopenharmony_ci				 entry->domain ? entry->domain : "(default)");
61762306a36Sopenharmony_ci		audit_log_end(audit_buf);
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	switch (entry->def.type) {
62162306a36Sopenharmony_ci	case NETLBL_NLTYPE_ADDRSELECT:
62262306a36Sopenharmony_ci		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) {
62362306a36Sopenharmony_ci			map4 = netlbl_domhsh_addr4_entry(iter4);
62462306a36Sopenharmony_ci			cipso_v4_doi_putdef(map4->def.cipso);
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
62762306a36Sopenharmony_ci		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) {
62862306a36Sopenharmony_ci			map6 = netlbl_domhsh_addr6_entry(iter6);
62962306a36Sopenharmony_ci			calipso_doi_putdef(map6->def.calipso);
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci#endif /* IPv6 */
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci	case NETLBL_NLTYPE_CIPSOV4:
63462306a36Sopenharmony_ci		cipso_v4_doi_putdef(entry->def.cipso);
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
63762306a36Sopenharmony_ci	case NETLBL_NLTYPE_CALIPSO:
63862306a36Sopenharmony_ci		calipso_doi_putdef(entry->def.calipso);
63962306a36Sopenharmony_ci		break;
64062306a36Sopenharmony_ci#endif /* IPv6 */
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return ret_val;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci/**
64862306a36Sopenharmony_ci * netlbl_domhsh_remove_af4 - Removes an address selector entry
64962306a36Sopenharmony_ci * @domain: the domain
65062306a36Sopenharmony_ci * @addr: IPv4 address
65162306a36Sopenharmony_ci * @mask: IPv4 address mask
65262306a36Sopenharmony_ci * @audit_info: NetLabel audit information
65362306a36Sopenharmony_ci *
65462306a36Sopenharmony_ci * Description:
65562306a36Sopenharmony_ci * Removes an individual address selector from a domain mapping and potentially
65662306a36Sopenharmony_ci * the entire mapping if it is empty.  Returns zero on success, negative values
65762306a36Sopenharmony_ci * on failure.
65862306a36Sopenharmony_ci *
65962306a36Sopenharmony_ci */
66062306a36Sopenharmony_ciint netlbl_domhsh_remove_af4(const char *domain,
66162306a36Sopenharmony_ci			     const struct in_addr *addr,
66262306a36Sopenharmony_ci			     const struct in_addr *mask,
66362306a36Sopenharmony_ci			     struct netlbl_audit *audit_info)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct netlbl_dom_map *entry_map;
66662306a36Sopenharmony_ci	struct netlbl_af4list *entry_addr;
66762306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
66862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
66962306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
67062306a36Sopenharmony_ci#endif /* IPv6 */
67162306a36Sopenharmony_ci	struct netlbl_domaddr4_map *entry;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	rcu_read_lock();
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (domain)
67662306a36Sopenharmony_ci		entry_map = netlbl_domhsh_search(domain, AF_INET);
67762306a36Sopenharmony_ci	else
67862306a36Sopenharmony_ci		entry_map = netlbl_domhsh_search_def(domain, AF_INET);
67962306a36Sopenharmony_ci	if (entry_map == NULL ||
68062306a36Sopenharmony_ci	    entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
68162306a36Sopenharmony_ci		goto remove_af4_failure;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	spin_lock(&netlbl_domhsh_lock);
68462306a36Sopenharmony_ci	entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
68562306a36Sopenharmony_ci					   &entry_map->def.addrsel->list4);
68662306a36Sopenharmony_ci	spin_unlock(&netlbl_domhsh_lock);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (entry_addr == NULL)
68962306a36Sopenharmony_ci		goto remove_af4_failure;
69062306a36Sopenharmony_ci	netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
69162306a36Sopenharmony_ci		goto remove_af4_single_addr;
69262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
69362306a36Sopenharmony_ci	netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
69462306a36Sopenharmony_ci		goto remove_af4_single_addr;
69562306a36Sopenharmony_ci#endif /* IPv6 */
69662306a36Sopenharmony_ci	/* the domain mapping is empty so remove it from the mapping table */
69762306a36Sopenharmony_ci	netlbl_domhsh_remove_entry(entry_map, audit_info);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ciremove_af4_single_addr:
70062306a36Sopenharmony_ci	rcu_read_unlock();
70162306a36Sopenharmony_ci	/* yick, we can't use call_rcu here because we don't have a rcu head
70262306a36Sopenharmony_ci	 * pointer but hopefully this should be a rare case so the pause
70362306a36Sopenharmony_ci	 * shouldn't be a problem */
70462306a36Sopenharmony_ci	synchronize_rcu();
70562306a36Sopenharmony_ci	entry = netlbl_domhsh_addr4_entry(entry_addr);
70662306a36Sopenharmony_ci	cipso_v4_doi_putdef(entry->def.cipso);
70762306a36Sopenharmony_ci	kfree(entry);
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ciremove_af4_failure:
71162306a36Sopenharmony_ci	rcu_read_unlock();
71262306a36Sopenharmony_ci	return -ENOENT;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
71662306a36Sopenharmony_ci/**
71762306a36Sopenharmony_ci * netlbl_domhsh_remove_af6 - Removes an address selector entry
71862306a36Sopenharmony_ci * @domain: the domain
71962306a36Sopenharmony_ci * @addr: IPv6 address
72062306a36Sopenharmony_ci * @mask: IPv6 address mask
72162306a36Sopenharmony_ci * @audit_info: NetLabel audit information
72262306a36Sopenharmony_ci *
72362306a36Sopenharmony_ci * Description:
72462306a36Sopenharmony_ci * Removes an individual address selector from a domain mapping and potentially
72562306a36Sopenharmony_ci * the entire mapping if it is empty.  Returns zero on success, negative values
72662306a36Sopenharmony_ci * on failure.
72762306a36Sopenharmony_ci *
72862306a36Sopenharmony_ci */
72962306a36Sopenharmony_ciint netlbl_domhsh_remove_af6(const char *domain,
73062306a36Sopenharmony_ci			     const struct in6_addr *addr,
73162306a36Sopenharmony_ci			     const struct in6_addr *mask,
73262306a36Sopenharmony_ci			     struct netlbl_audit *audit_info)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct netlbl_dom_map *entry_map;
73562306a36Sopenharmony_ci	struct netlbl_af6list *entry_addr;
73662306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
73762306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
73862306a36Sopenharmony_ci	struct netlbl_domaddr6_map *entry;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	rcu_read_lock();
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (domain)
74362306a36Sopenharmony_ci		entry_map = netlbl_domhsh_search(domain, AF_INET6);
74462306a36Sopenharmony_ci	else
74562306a36Sopenharmony_ci		entry_map = netlbl_domhsh_search_def(domain, AF_INET6);
74662306a36Sopenharmony_ci	if (entry_map == NULL ||
74762306a36Sopenharmony_ci	    entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
74862306a36Sopenharmony_ci		goto remove_af6_failure;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	spin_lock(&netlbl_domhsh_lock);
75162306a36Sopenharmony_ci	entry_addr = netlbl_af6list_remove(addr, mask,
75262306a36Sopenharmony_ci					   &entry_map->def.addrsel->list6);
75362306a36Sopenharmony_ci	spin_unlock(&netlbl_domhsh_lock);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (entry_addr == NULL)
75662306a36Sopenharmony_ci		goto remove_af6_failure;
75762306a36Sopenharmony_ci	netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
75862306a36Sopenharmony_ci		goto remove_af6_single_addr;
75962306a36Sopenharmony_ci	netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
76062306a36Sopenharmony_ci		goto remove_af6_single_addr;
76162306a36Sopenharmony_ci	/* the domain mapping is empty so remove it from the mapping table */
76262306a36Sopenharmony_ci	netlbl_domhsh_remove_entry(entry_map, audit_info);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ciremove_af6_single_addr:
76562306a36Sopenharmony_ci	rcu_read_unlock();
76662306a36Sopenharmony_ci	/* yick, we can't use call_rcu here because we don't have a rcu head
76762306a36Sopenharmony_ci	 * pointer but hopefully this should be a rare case so the pause
76862306a36Sopenharmony_ci	 * shouldn't be a problem */
76962306a36Sopenharmony_ci	synchronize_rcu();
77062306a36Sopenharmony_ci	entry = netlbl_domhsh_addr6_entry(entry_addr);
77162306a36Sopenharmony_ci	calipso_doi_putdef(entry->def.calipso);
77262306a36Sopenharmony_ci	kfree(entry);
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ciremove_af6_failure:
77662306a36Sopenharmony_ci	rcu_read_unlock();
77762306a36Sopenharmony_ci	return -ENOENT;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci#endif /* IPv6 */
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci/**
78262306a36Sopenharmony_ci * netlbl_domhsh_remove - Removes an entry from the domain hash table
78362306a36Sopenharmony_ci * @domain: the domain to remove
78462306a36Sopenharmony_ci * @family: address family
78562306a36Sopenharmony_ci * @audit_info: NetLabel audit information
78662306a36Sopenharmony_ci *
78762306a36Sopenharmony_ci * Description:
78862306a36Sopenharmony_ci * Removes an entry from the domain hash table and handles any updates to the
78962306a36Sopenharmony_ci * lower level protocol handler (i.e. CIPSO).  @family may be %AF_UNSPEC which
79062306a36Sopenharmony_ci * removes all address family entries.  Returns zero on success, negative on
79162306a36Sopenharmony_ci * failure.
79262306a36Sopenharmony_ci *
79362306a36Sopenharmony_ci */
79462306a36Sopenharmony_ciint netlbl_domhsh_remove(const char *domain, u16 family,
79562306a36Sopenharmony_ci			 struct netlbl_audit *audit_info)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	int ret_val = -EINVAL;
79862306a36Sopenharmony_ci	struct netlbl_dom_map *entry;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	rcu_read_lock();
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	if (family == AF_INET || family == AF_UNSPEC) {
80362306a36Sopenharmony_ci		if (domain)
80462306a36Sopenharmony_ci			entry = netlbl_domhsh_search(domain, AF_INET);
80562306a36Sopenharmony_ci		else
80662306a36Sopenharmony_ci			entry = netlbl_domhsh_search_def(domain, AF_INET);
80762306a36Sopenharmony_ci		ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
80862306a36Sopenharmony_ci		if (ret_val && ret_val != -ENOENT)
80962306a36Sopenharmony_ci			goto done;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci	if (family == AF_INET6 || family == AF_UNSPEC) {
81262306a36Sopenharmony_ci		int ret_val2;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		if (domain)
81562306a36Sopenharmony_ci			entry = netlbl_domhsh_search(domain, AF_INET6);
81662306a36Sopenharmony_ci		else
81762306a36Sopenharmony_ci			entry = netlbl_domhsh_search_def(domain, AF_INET6);
81862306a36Sopenharmony_ci		ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
81962306a36Sopenharmony_ci		if (ret_val2 != -ENOENT)
82062306a36Sopenharmony_ci			ret_val = ret_val2;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_cidone:
82362306a36Sopenharmony_ci	rcu_read_unlock();
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return ret_val;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci/**
82962306a36Sopenharmony_ci * netlbl_domhsh_remove_default - Removes the default entry from the table
83062306a36Sopenharmony_ci * @family: address family
83162306a36Sopenharmony_ci * @audit_info: NetLabel audit information
83262306a36Sopenharmony_ci *
83362306a36Sopenharmony_ci * Description:
83462306a36Sopenharmony_ci * Removes/resets the default entry corresponding to @family from the domain
83562306a36Sopenharmony_ci * hash table and handles any updates to the lower level protocol handler
83662306a36Sopenharmony_ci * (i.e. CIPSO).  @family may be %AF_UNSPEC which removes all address family
83762306a36Sopenharmony_ci * entries.  Returns zero on success, negative on failure.
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_ciint netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	return netlbl_domhsh_remove(NULL, family, audit_info);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/**
84662306a36Sopenharmony_ci * netlbl_domhsh_getentry - Get an entry from the domain hash table
84762306a36Sopenharmony_ci * @domain: the domain name to search for
84862306a36Sopenharmony_ci * @family: address family
84962306a36Sopenharmony_ci *
85062306a36Sopenharmony_ci * Description:
85162306a36Sopenharmony_ci * Look through the domain hash table searching for an entry to match @domain,
85262306a36Sopenharmony_ci * with address family @family, return a pointer to a copy of the entry or
85362306a36Sopenharmony_ci * NULL.  The caller is responsible for ensuring that rcu_read_[un]lock() is
85462306a36Sopenharmony_ci * called.
85562306a36Sopenharmony_ci *
85662306a36Sopenharmony_ci */
85762306a36Sopenharmony_cistruct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	if (family == AF_UNSPEC)
86062306a36Sopenharmony_ci		return NULL;
86162306a36Sopenharmony_ci	return netlbl_domhsh_search_def(domain, family);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
86662306a36Sopenharmony_ci * @domain: the domain name to search for
86762306a36Sopenharmony_ci * @addr: the IP address to search for
86862306a36Sopenharmony_ci *
86962306a36Sopenharmony_ci * Description:
87062306a36Sopenharmony_ci * Look through the domain hash table searching for an entry to match @domain
87162306a36Sopenharmony_ci * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
87262306a36Sopenharmony_ci * responsible for ensuring that rcu_read_[un]lock() is called.
87362306a36Sopenharmony_ci *
87462306a36Sopenharmony_ci */
87562306a36Sopenharmony_cistruct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
87662306a36Sopenharmony_ci						     __be32 addr)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	struct netlbl_dom_map *dom_iter;
87962306a36Sopenharmony_ci	struct netlbl_af4list *addr_iter;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
88262306a36Sopenharmony_ci	if (dom_iter == NULL)
88362306a36Sopenharmony_ci		return NULL;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
88662306a36Sopenharmony_ci		return &dom_iter->def;
88762306a36Sopenharmony_ci	addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
88862306a36Sopenharmony_ci	if (addr_iter == NULL)
88962306a36Sopenharmony_ci		return NULL;
89062306a36Sopenharmony_ci	return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
89462306a36Sopenharmony_ci/**
89562306a36Sopenharmony_ci * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
89662306a36Sopenharmony_ci * @domain: the domain name to search for
89762306a36Sopenharmony_ci * @addr: the IP address to search for
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci * Description:
90062306a36Sopenharmony_ci * Look through the domain hash table searching for an entry to match @domain
90162306a36Sopenharmony_ci * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
90262306a36Sopenharmony_ci * responsible for ensuring that rcu_read_[un]lock() is called.
90362306a36Sopenharmony_ci *
90462306a36Sopenharmony_ci */
90562306a36Sopenharmony_cistruct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
90662306a36Sopenharmony_ci						   const struct in6_addr *addr)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	struct netlbl_dom_map *dom_iter;
90962306a36Sopenharmony_ci	struct netlbl_af6list *addr_iter;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
91262306a36Sopenharmony_ci	if (dom_iter == NULL)
91362306a36Sopenharmony_ci		return NULL;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
91662306a36Sopenharmony_ci		return &dom_iter->def;
91762306a36Sopenharmony_ci	addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
91862306a36Sopenharmony_ci	if (addr_iter == NULL)
91962306a36Sopenharmony_ci		return NULL;
92062306a36Sopenharmony_ci	return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci#endif /* IPv6 */
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * netlbl_domhsh_walk - Iterate through the domain mapping hash table
92662306a36Sopenharmony_ci * @skip_bkt: the number of buckets to skip at the start
92762306a36Sopenharmony_ci * @skip_chain: the number of entries to skip in the first iterated bucket
92862306a36Sopenharmony_ci * @callback: callback for each entry
92962306a36Sopenharmony_ci * @cb_arg: argument for the callback function
93062306a36Sopenharmony_ci *
93162306a36Sopenharmony_ci * Description:
93262306a36Sopenharmony_ci * Iterate over the domain mapping hash table, skipping the first @skip_bkt
93362306a36Sopenharmony_ci * buckets and @skip_chain entries.  For each entry in the table call
93462306a36Sopenharmony_ci * @callback, if @callback returns a negative value stop 'walking' through the
93562306a36Sopenharmony_ci * table and return.  Updates the values in @skip_bkt and @skip_chain on
93662306a36Sopenharmony_ci * return.  Returns zero on success, negative values on failure.
93762306a36Sopenharmony_ci *
93862306a36Sopenharmony_ci */
93962306a36Sopenharmony_ciint netlbl_domhsh_walk(u32 *skip_bkt,
94062306a36Sopenharmony_ci		     u32 *skip_chain,
94162306a36Sopenharmony_ci		     int (*callback) (struct netlbl_dom_map *entry, void *arg),
94262306a36Sopenharmony_ci		     void *cb_arg)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	int ret_val = -ENOENT;
94562306a36Sopenharmony_ci	u32 iter_bkt;
94662306a36Sopenharmony_ci	struct list_head *iter_list;
94762306a36Sopenharmony_ci	struct netlbl_dom_map *iter_entry;
94862306a36Sopenharmony_ci	u32 chain_cnt = 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	rcu_read_lock();
95162306a36Sopenharmony_ci	for (iter_bkt = *skip_bkt;
95262306a36Sopenharmony_ci	     iter_bkt < rcu_dereference(netlbl_domhsh)->size;
95362306a36Sopenharmony_ci	     iter_bkt++, chain_cnt = 0) {
95462306a36Sopenharmony_ci		iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
95562306a36Sopenharmony_ci		list_for_each_entry_rcu(iter_entry, iter_list, list)
95662306a36Sopenharmony_ci			if (iter_entry->valid) {
95762306a36Sopenharmony_ci				if (chain_cnt++ < *skip_chain)
95862306a36Sopenharmony_ci					continue;
95962306a36Sopenharmony_ci				ret_val = callback(iter_entry, cb_arg);
96062306a36Sopenharmony_ci				if (ret_val < 0) {
96162306a36Sopenharmony_ci					chain_cnt--;
96262306a36Sopenharmony_ci					goto walk_return;
96362306a36Sopenharmony_ci				}
96462306a36Sopenharmony_ci			}
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ciwalk_return:
96862306a36Sopenharmony_ci	rcu_read_unlock();
96962306a36Sopenharmony_ci	*skip_bkt = iter_bkt;
97062306a36Sopenharmony_ci	*skip_chain = chain_cnt;
97162306a36Sopenharmony_ci	return ret_val;
97262306a36Sopenharmony_ci}
973