162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NetLabel Management Support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file defines the management functions for the NetLabel system.  The
662306a36Sopenharmony_ci * NetLabel system manages static and dynamic label mappings for network
762306a36Sopenharmony_ci * protocols such as CIPSO and RIPSO.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Paul Moore <paul@paul-moore.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/socket.h>
1862306a36Sopenharmony_ci#include <linux/string.h>
1962306a36Sopenharmony_ci#include <linux/skbuff.h>
2062306a36Sopenharmony_ci#include <linux/in.h>
2162306a36Sopenharmony_ci#include <linux/in6.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <net/sock.h>
2462306a36Sopenharmony_ci#include <net/netlink.h>
2562306a36Sopenharmony_ci#include <net/genetlink.h>
2662306a36Sopenharmony_ci#include <net/ip.h>
2762306a36Sopenharmony_ci#include <net/ipv6.h>
2862306a36Sopenharmony_ci#include <net/netlabel.h>
2962306a36Sopenharmony_ci#include <net/cipso_ipv4.h>
3062306a36Sopenharmony_ci#include <net/calipso.h>
3162306a36Sopenharmony_ci#include <linux/atomic.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "netlabel_calipso.h"
3462306a36Sopenharmony_ci#include "netlabel_domainhash.h"
3562306a36Sopenharmony_ci#include "netlabel_user.h"
3662306a36Sopenharmony_ci#include "netlabel_mgmt.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* NetLabel configured protocol counter */
3962306a36Sopenharmony_ciatomic_t netlabel_mgmt_protocount = ATOMIC_INIT(0);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Argument struct for netlbl_domhsh_walk() */
4262306a36Sopenharmony_cistruct netlbl_domhsh_walk_arg {
4362306a36Sopenharmony_ci	struct netlink_callback *nl_cb;
4462306a36Sopenharmony_ci	struct sk_buff *skb;
4562306a36Sopenharmony_ci	u32 seq;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* NetLabel Generic NETLINK CIPSOv4 family */
4962306a36Sopenharmony_cistatic struct genl_family netlbl_mgmt_gnl_family;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* NetLabel Netlink attribute policy */
5262306a36Sopenharmony_cistatic const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
5362306a36Sopenharmony_ci	[NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING },
5462306a36Sopenharmony_ci	[NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
5562306a36Sopenharmony_ci	[NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
5662306a36Sopenharmony_ci	[NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
5762306a36Sopenharmony_ci	[NLBL_MGMT_A_FAMILY] = { .type = NLA_U16 },
5862306a36Sopenharmony_ci	[NLBL_MGMT_A_CLPDOI] = { .type = NLA_U32 },
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * Helper Functions
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * netlbl_mgmt_add_common - Handle an ADD message
6762306a36Sopenharmony_ci * @info: the Generic NETLINK info block
6862306a36Sopenharmony_ci * @audit_info: NetLabel audit information
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * Description:
7162306a36Sopenharmony_ci * Helper function for the ADD and ADDDEF messages to add the domain mappings
7262306a36Sopenharmony_ci * from the message to the hash table.  See netlabel.h for a description of the
7362306a36Sopenharmony_ci * message format.  Returns zero on success, negative values on failure.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic int netlbl_mgmt_add_common(struct genl_info *info,
7762306a36Sopenharmony_ci				  struct netlbl_audit *audit_info)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	void *pmap = NULL;
8062306a36Sopenharmony_ci	int ret_val = -EINVAL;
8162306a36Sopenharmony_ci	struct netlbl_domaddr_map *addrmap = NULL;
8262306a36Sopenharmony_ci	struct cipso_v4_doi *cipsov4 = NULL;
8362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
8462306a36Sopenharmony_ci	struct calipso_doi *calipso = NULL;
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci	u32 tmp_val;
8762306a36Sopenharmony_ci	struct netlbl_dom_map *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!entry)
9062306a36Sopenharmony_ci		return -ENOMEM;
9162306a36Sopenharmony_ci	entry->def.type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
9262306a36Sopenharmony_ci	if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
9362306a36Sopenharmony_ci		size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
9462306a36Sopenharmony_ci		entry->domain = kmalloc(tmp_size, GFP_KERNEL);
9562306a36Sopenharmony_ci		if (entry->domain == NULL) {
9662306a36Sopenharmony_ci			ret_val = -ENOMEM;
9762306a36Sopenharmony_ci			goto add_free_entry;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci		nla_strscpy(entry->domain,
10062306a36Sopenharmony_ci			    info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* NOTE: internally we allow/use a entry->def.type value of
10462306a36Sopenharmony_ci	 *       NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
10562306a36Sopenharmony_ci	 *       to pass that as a protocol value because we need to know the
10662306a36Sopenharmony_ci	 *       "real" protocol */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	switch (entry->def.type) {
10962306a36Sopenharmony_ci	case NETLBL_NLTYPE_UNLABELED:
11062306a36Sopenharmony_ci		if (info->attrs[NLBL_MGMT_A_FAMILY])
11162306a36Sopenharmony_ci			entry->family =
11262306a36Sopenharmony_ci				nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
11362306a36Sopenharmony_ci		else
11462306a36Sopenharmony_ci			entry->family = AF_UNSPEC;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case NETLBL_NLTYPE_CIPSOV4:
11762306a36Sopenharmony_ci		if (!info->attrs[NLBL_MGMT_A_CV4DOI])
11862306a36Sopenharmony_ci			goto add_free_domain;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
12162306a36Sopenharmony_ci		cipsov4 = cipso_v4_doi_getdef(tmp_val);
12262306a36Sopenharmony_ci		if (cipsov4 == NULL)
12362306a36Sopenharmony_ci			goto add_free_domain;
12462306a36Sopenharmony_ci		entry->family = AF_INET;
12562306a36Sopenharmony_ci		entry->def.cipso = cipsov4;
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
12862306a36Sopenharmony_ci	case NETLBL_NLTYPE_CALIPSO:
12962306a36Sopenharmony_ci		if (!info->attrs[NLBL_MGMT_A_CLPDOI])
13062306a36Sopenharmony_ci			goto add_free_domain;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CLPDOI]);
13362306a36Sopenharmony_ci		calipso = calipso_doi_getdef(tmp_val);
13462306a36Sopenharmony_ci		if (calipso == NULL)
13562306a36Sopenharmony_ci			goto add_free_domain;
13662306a36Sopenharmony_ci		entry->family = AF_INET6;
13762306a36Sopenharmony_ci		entry->def.calipso = calipso;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci#endif /* IPv6 */
14062306a36Sopenharmony_ci	default:
14162306a36Sopenharmony_ci		goto add_free_domain;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if ((entry->family == AF_INET && info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
14562306a36Sopenharmony_ci	    (entry->family == AF_INET6 && info->attrs[NLBL_MGMT_A_IPV4ADDR]))
14662306a36Sopenharmony_ci		goto add_doi_put_def;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
14962306a36Sopenharmony_ci		struct in_addr *addr;
15062306a36Sopenharmony_ci		struct in_addr *mask;
15162306a36Sopenharmony_ci		struct netlbl_domaddr4_map *map;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
15462306a36Sopenharmony_ci		if (addrmap == NULL) {
15562306a36Sopenharmony_ci			ret_val = -ENOMEM;
15662306a36Sopenharmony_ci			goto add_doi_put_def;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci		INIT_LIST_HEAD(&addrmap->list4);
15962306a36Sopenharmony_ci		INIT_LIST_HEAD(&addrmap->list6);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
16262306a36Sopenharmony_ci		    sizeof(struct in_addr)) {
16362306a36Sopenharmony_ci			ret_val = -EINVAL;
16462306a36Sopenharmony_ci			goto add_free_addrmap;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci		if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
16762306a36Sopenharmony_ci		    sizeof(struct in_addr)) {
16862306a36Sopenharmony_ci			ret_val = -EINVAL;
16962306a36Sopenharmony_ci			goto add_free_addrmap;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci		addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
17262306a36Sopenharmony_ci		mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		map = kzalloc(sizeof(*map), GFP_KERNEL);
17562306a36Sopenharmony_ci		if (map == NULL) {
17662306a36Sopenharmony_ci			ret_val = -ENOMEM;
17762306a36Sopenharmony_ci			goto add_free_addrmap;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		pmap = map;
18062306a36Sopenharmony_ci		map->list.addr = addr->s_addr & mask->s_addr;
18162306a36Sopenharmony_ci		map->list.mask = mask->s_addr;
18262306a36Sopenharmony_ci		map->list.valid = 1;
18362306a36Sopenharmony_ci		map->def.type = entry->def.type;
18462306a36Sopenharmony_ci		if (cipsov4)
18562306a36Sopenharmony_ci			map->def.cipso = cipsov4;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
18862306a36Sopenharmony_ci		if (ret_val != 0)
18962306a36Sopenharmony_ci			goto add_free_map;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		entry->family = AF_INET;
19262306a36Sopenharmony_ci		entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
19362306a36Sopenharmony_ci		entry->def.addrsel = addrmap;
19462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
19562306a36Sopenharmony_ci	} else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
19662306a36Sopenharmony_ci		struct in6_addr *addr;
19762306a36Sopenharmony_ci		struct in6_addr *mask;
19862306a36Sopenharmony_ci		struct netlbl_domaddr6_map *map;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
20162306a36Sopenharmony_ci		if (addrmap == NULL) {
20262306a36Sopenharmony_ci			ret_val = -ENOMEM;
20362306a36Sopenharmony_ci			goto add_doi_put_def;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci		INIT_LIST_HEAD(&addrmap->list4);
20662306a36Sopenharmony_ci		INIT_LIST_HEAD(&addrmap->list6);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
20962306a36Sopenharmony_ci		    sizeof(struct in6_addr)) {
21062306a36Sopenharmony_ci			ret_val = -EINVAL;
21162306a36Sopenharmony_ci			goto add_free_addrmap;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci		if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
21462306a36Sopenharmony_ci		    sizeof(struct in6_addr)) {
21562306a36Sopenharmony_ci			ret_val = -EINVAL;
21662306a36Sopenharmony_ci			goto add_free_addrmap;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci		addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
21962306a36Sopenharmony_ci		mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		map = kzalloc(sizeof(*map), GFP_KERNEL);
22262306a36Sopenharmony_ci		if (map == NULL) {
22362306a36Sopenharmony_ci			ret_val = -ENOMEM;
22462306a36Sopenharmony_ci			goto add_free_addrmap;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci		pmap = map;
22762306a36Sopenharmony_ci		map->list.addr = *addr;
22862306a36Sopenharmony_ci		map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
22962306a36Sopenharmony_ci		map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
23062306a36Sopenharmony_ci		map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
23162306a36Sopenharmony_ci		map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
23262306a36Sopenharmony_ci		map->list.mask = *mask;
23362306a36Sopenharmony_ci		map->list.valid = 1;
23462306a36Sopenharmony_ci		map->def.type = entry->def.type;
23562306a36Sopenharmony_ci		if (calipso)
23662306a36Sopenharmony_ci			map->def.calipso = calipso;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
23962306a36Sopenharmony_ci		if (ret_val != 0)
24062306a36Sopenharmony_ci			goto add_free_map;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		entry->family = AF_INET6;
24362306a36Sopenharmony_ci		entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
24462306a36Sopenharmony_ci		entry->def.addrsel = addrmap;
24562306a36Sopenharmony_ci#endif /* IPv6 */
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ret_val = netlbl_domhsh_add(entry, audit_info);
24962306a36Sopenharmony_ci	if (ret_val != 0)
25062306a36Sopenharmony_ci		goto add_free_map;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciadd_free_map:
25562306a36Sopenharmony_ci	kfree(pmap);
25662306a36Sopenharmony_ciadd_free_addrmap:
25762306a36Sopenharmony_ci	kfree(addrmap);
25862306a36Sopenharmony_ciadd_doi_put_def:
25962306a36Sopenharmony_ci	cipso_v4_doi_putdef(cipsov4);
26062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
26162306a36Sopenharmony_ci	calipso_doi_putdef(calipso);
26262306a36Sopenharmony_ci#endif
26362306a36Sopenharmony_ciadd_free_domain:
26462306a36Sopenharmony_ci	kfree(entry->domain);
26562306a36Sopenharmony_ciadd_free_entry:
26662306a36Sopenharmony_ci	kfree(entry);
26762306a36Sopenharmony_ci	return ret_val;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
27262306a36Sopenharmony_ci * @skb: the NETLINK buffer
27362306a36Sopenharmony_ci * @entry: the map entry
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * Description:
27662306a36Sopenharmony_ci * This function is a helper function used by the LISTALL and LISTDEF command
27762306a36Sopenharmony_ci * handlers.  The caller is responsible for ensuring that the RCU read lock
27862306a36Sopenharmony_ci * is held.  Returns zero on success, negative values on failure.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic int netlbl_mgmt_listentry(struct sk_buff *skb,
28262306a36Sopenharmony_ci				 struct netlbl_dom_map *entry)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	int ret_val = 0;
28562306a36Sopenharmony_ci	struct nlattr *nla_a;
28662306a36Sopenharmony_ci	struct nlattr *nla_b;
28762306a36Sopenharmony_ci	struct netlbl_af4list *iter4;
28862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
28962306a36Sopenharmony_ci	struct netlbl_af6list *iter6;
29062306a36Sopenharmony_ci#endif
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (entry->domain != NULL) {
29362306a36Sopenharmony_ci		ret_val = nla_put_string(skb,
29462306a36Sopenharmony_ci					 NLBL_MGMT_A_DOMAIN, entry->domain);
29562306a36Sopenharmony_ci		if (ret_val != 0)
29662306a36Sopenharmony_ci			return ret_val;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret_val = nla_put_u16(skb, NLBL_MGMT_A_FAMILY, entry->family);
30062306a36Sopenharmony_ci	if (ret_val != 0)
30162306a36Sopenharmony_ci		return ret_val;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	switch (entry->def.type) {
30462306a36Sopenharmony_ci	case NETLBL_NLTYPE_ADDRSELECT:
30562306a36Sopenharmony_ci		nla_a = nla_nest_start_noflag(skb, NLBL_MGMT_A_SELECTORLIST);
30662306a36Sopenharmony_ci		if (nla_a == NULL)
30762306a36Sopenharmony_ci			return -ENOMEM;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) {
31062306a36Sopenharmony_ci			struct netlbl_domaddr4_map *map4;
31162306a36Sopenharmony_ci			struct in_addr addr_struct;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci			nla_b = nla_nest_start_noflag(skb,
31462306a36Sopenharmony_ci						      NLBL_MGMT_A_ADDRSELECTOR);
31562306a36Sopenharmony_ci			if (nla_b == NULL)
31662306a36Sopenharmony_ci				return -ENOMEM;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			addr_struct.s_addr = iter4->addr;
31962306a36Sopenharmony_ci			ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4ADDR,
32062306a36Sopenharmony_ci						  addr_struct.s_addr);
32162306a36Sopenharmony_ci			if (ret_val != 0)
32262306a36Sopenharmony_ci				return ret_val;
32362306a36Sopenharmony_ci			addr_struct.s_addr = iter4->mask;
32462306a36Sopenharmony_ci			ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4MASK,
32562306a36Sopenharmony_ci						  addr_struct.s_addr);
32662306a36Sopenharmony_ci			if (ret_val != 0)
32762306a36Sopenharmony_ci				return ret_val;
32862306a36Sopenharmony_ci			map4 = netlbl_domhsh_addr4_entry(iter4);
32962306a36Sopenharmony_ci			ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
33062306a36Sopenharmony_ci					      map4->def.type);
33162306a36Sopenharmony_ci			if (ret_val != 0)
33262306a36Sopenharmony_ci				return ret_val;
33362306a36Sopenharmony_ci			switch (map4->def.type) {
33462306a36Sopenharmony_ci			case NETLBL_NLTYPE_CIPSOV4:
33562306a36Sopenharmony_ci				ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
33662306a36Sopenharmony_ci						      map4->def.cipso->doi);
33762306a36Sopenharmony_ci				if (ret_val != 0)
33862306a36Sopenharmony_ci					return ret_val;
33962306a36Sopenharmony_ci				break;
34062306a36Sopenharmony_ci			}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci			nla_nest_end(skb, nla_b);
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
34562306a36Sopenharmony_ci		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) {
34662306a36Sopenharmony_ci			struct netlbl_domaddr6_map *map6;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci			nla_b = nla_nest_start_noflag(skb,
34962306a36Sopenharmony_ci						      NLBL_MGMT_A_ADDRSELECTOR);
35062306a36Sopenharmony_ci			if (nla_b == NULL)
35162306a36Sopenharmony_ci				return -ENOMEM;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci			ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6ADDR,
35462306a36Sopenharmony_ci						   &iter6->addr);
35562306a36Sopenharmony_ci			if (ret_val != 0)
35662306a36Sopenharmony_ci				return ret_val;
35762306a36Sopenharmony_ci			ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6MASK,
35862306a36Sopenharmony_ci						   &iter6->mask);
35962306a36Sopenharmony_ci			if (ret_val != 0)
36062306a36Sopenharmony_ci				return ret_val;
36162306a36Sopenharmony_ci			map6 = netlbl_domhsh_addr6_entry(iter6);
36262306a36Sopenharmony_ci			ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
36362306a36Sopenharmony_ci					      map6->def.type);
36462306a36Sopenharmony_ci			if (ret_val != 0)
36562306a36Sopenharmony_ci				return ret_val;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci			switch (map6->def.type) {
36862306a36Sopenharmony_ci			case NETLBL_NLTYPE_CALIPSO:
36962306a36Sopenharmony_ci				ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
37062306a36Sopenharmony_ci						      map6->def.calipso->doi);
37162306a36Sopenharmony_ci				if (ret_val != 0)
37262306a36Sopenharmony_ci					return ret_val;
37362306a36Sopenharmony_ci				break;
37462306a36Sopenharmony_ci			}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci			nla_nest_end(skb, nla_b);
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci#endif /* IPv6 */
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		nla_nest_end(skb, nla_a);
38162306a36Sopenharmony_ci		break;
38262306a36Sopenharmony_ci	case NETLBL_NLTYPE_UNLABELED:
38362306a36Sopenharmony_ci		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
38462306a36Sopenharmony_ci				      entry->def.type);
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci	case NETLBL_NLTYPE_CIPSOV4:
38762306a36Sopenharmony_ci		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
38862306a36Sopenharmony_ci				      entry->def.type);
38962306a36Sopenharmony_ci		if (ret_val != 0)
39062306a36Sopenharmony_ci			return ret_val;
39162306a36Sopenharmony_ci		ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
39262306a36Sopenharmony_ci				      entry->def.cipso->doi);
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	case NETLBL_NLTYPE_CALIPSO:
39562306a36Sopenharmony_ci		ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
39662306a36Sopenharmony_ci				      entry->def.type);
39762306a36Sopenharmony_ci		if (ret_val != 0)
39862306a36Sopenharmony_ci			return ret_val;
39962306a36Sopenharmony_ci		ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI,
40062306a36Sopenharmony_ci				      entry->def.calipso->doi);
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return ret_val;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/*
40862306a36Sopenharmony_ci * NetLabel Command Handlers
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/**
41262306a36Sopenharmony_ci * netlbl_mgmt_add - Handle an ADD message
41362306a36Sopenharmony_ci * @skb: the NETLINK buffer
41462306a36Sopenharmony_ci * @info: the Generic NETLINK info block
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * Description:
41762306a36Sopenharmony_ci * Process a user generated ADD message and add the domains from the message
41862306a36Sopenharmony_ci * to the hash table.  See netlabel.h for a description of the message format.
41962306a36Sopenharmony_ci * Returns zero on success, negative values on failure.
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci */
42262306a36Sopenharmony_cistatic int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct netlbl_audit audit_info;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
42762306a36Sopenharmony_ci	    (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
42862306a36Sopenharmony_ci	    (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
42962306a36Sopenharmony_ci	     info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
43062306a36Sopenharmony_ci	    (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
43162306a36Sopenharmony_ci	     info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
43262306a36Sopenharmony_ci	    ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
43362306a36Sopenharmony_ci	     (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
43462306a36Sopenharmony_ci	    ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
43562306a36Sopenharmony_ci	     (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
43662306a36Sopenharmony_ci		return -EINVAL;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	netlbl_netlink_auditinfo(&audit_info);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return netlbl_mgmt_add_common(info, &audit_info);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci/**
44462306a36Sopenharmony_ci * netlbl_mgmt_remove - Handle a REMOVE message
44562306a36Sopenharmony_ci * @skb: the NETLINK buffer
44662306a36Sopenharmony_ci * @info: the Generic NETLINK info block
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * Description:
44962306a36Sopenharmony_ci * Process a user generated REMOVE message and remove the specified domain
45062306a36Sopenharmony_ci * mappings.  Returns zero on success, negative values on failure.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_cistatic int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	char *domain;
45662306a36Sopenharmony_ci	struct netlbl_audit audit_info;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!info->attrs[NLBL_MGMT_A_DOMAIN])
45962306a36Sopenharmony_ci		return -EINVAL;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	netlbl_netlink_auditinfo(&audit_info);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
46462306a36Sopenharmony_ci	return netlbl_domhsh_remove(domain, AF_UNSPEC, &audit_info);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci/**
46862306a36Sopenharmony_ci * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL
46962306a36Sopenharmony_ci * @entry: the domain mapping hash table entry
47062306a36Sopenharmony_ci * @arg: the netlbl_domhsh_walk_arg structure
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * Description:
47362306a36Sopenharmony_ci * This function is designed to be used as a callback to the
47462306a36Sopenharmony_ci * netlbl_domhsh_walk() function for use in generating a response for a LISTALL
47562306a36Sopenharmony_ci * message.  Returns the size of the message on success, negative values on
47662306a36Sopenharmony_ci * failure.
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci */
47962306a36Sopenharmony_cistatic int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	int ret_val = -ENOMEM;
48262306a36Sopenharmony_ci	struct netlbl_domhsh_walk_arg *cb_arg = arg;
48362306a36Sopenharmony_ci	void *data;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
48662306a36Sopenharmony_ci			   cb_arg->seq, &netlbl_mgmt_gnl_family,
48762306a36Sopenharmony_ci			   NLM_F_MULTI, NLBL_MGMT_C_LISTALL);
48862306a36Sopenharmony_ci	if (data == NULL)
48962306a36Sopenharmony_ci		goto listall_cb_failure;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
49262306a36Sopenharmony_ci	if (ret_val != 0)
49362306a36Sopenharmony_ci		goto listall_cb_failure;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	cb_arg->seq++;
49662306a36Sopenharmony_ci	genlmsg_end(cb_arg->skb, data);
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cilistall_cb_failure:
50062306a36Sopenharmony_ci	genlmsg_cancel(cb_arg->skb, data);
50162306a36Sopenharmony_ci	return ret_val;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * netlbl_mgmt_listall - Handle a LISTALL message
50662306a36Sopenharmony_ci * @skb: the NETLINK buffer
50762306a36Sopenharmony_ci * @cb: the NETLINK callback
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Description:
51062306a36Sopenharmony_ci * Process a user generated LISTALL message and dumps the domain hash table in
51162306a36Sopenharmony_ci * a form suitable for use in a kernel generated LISTALL message.  Returns zero
51262306a36Sopenharmony_ci * on success, negative values on failure.
51362306a36Sopenharmony_ci *
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic int netlbl_mgmt_listall(struct sk_buff *skb,
51662306a36Sopenharmony_ci			       struct netlink_callback *cb)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct netlbl_domhsh_walk_arg cb_arg;
51962306a36Sopenharmony_ci	u32 skip_bkt = cb->args[0];
52062306a36Sopenharmony_ci	u32 skip_chain = cb->args[1];
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	cb_arg.nl_cb = cb;
52362306a36Sopenharmony_ci	cb_arg.skb = skb;
52462306a36Sopenharmony_ci	cb_arg.seq = cb->nlh->nlmsg_seq;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	netlbl_domhsh_walk(&skip_bkt,
52762306a36Sopenharmony_ci			   &skip_chain,
52862306a36Sopenharmony_ci			   netlbl_mgmt_listall_cb,
52962306a36Sopenharmony_ci			   &cb_arg);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	cb->args[0] = skip_bkt;
53262306a36Sopenharmony_ci	cb->args[1] = skip_chain;
53362306a36Sopenharmony_ci	return skb->len;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/**
53762306a36Sopenharmony_ci * netlbl_mgmt_adddef - Handle an ADDDEF message
53862306a36Sopenharmony_ci * @skb: the NETLINK buffer
53962306a36Sopenharmony_ci * @info: the Generic NETLINK info block
54062306a36Sopenharmony_ci *
54162306a36Sopenharmony_ci * Description:
54262306a36Sopenharmony_ci * Process a user generated ADDDEF message and respond accordingly.  Returns
54362306a36Sopenharmony_ci * zero on success, negative values on failure.
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_cistatic int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct netlbl_audit audit_info;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
55162306a36Sopenharmony_ci	    (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
55262306a36Sopenharmony_ci	     info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
55362306a36Sopenharmony_ci	    (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
55462306a36Sopenharmony_ci	     info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
55562306a36Sopenharmony_ci	    ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
55662306a36Sopenharmony_ci	     (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
55762306a36Sopenharmony_ci	    ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
55862306a36Sopenharmony_ci	     (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
55962306a36Sopenharmony_ci		return -EINVAL;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	netlbl_netlink_auditinfo(&audit_info);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return netlbl_mgmt_add_common(info, &audit_info);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci/**
56762306a36Sopenharmony_ci * netlbl_mgmt_removedef - Handle a REMOVEDEF message
56862306a36Sopenharmony_ci * @skb: the NETLINK buffer
56962306a36Sopenharmony_ci * @info: the Generic NETLINK info block
57062306a36Sopenharmony_ci *
57162306a36Sopenharmony_ci * Description:
57262306a36Sopenharmony_ci * Process a user generated REMOVEDEF message and remove the default domain
57362306a36Sopenharmony_ci * mapping.  Returns zero on success, negative values on failure.
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_cistatic int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct netlbl_audit audit_info;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	netlbl_netlink_auditinfo(&audit_info);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return netlbl_domhsh_remove_default(AF_UNSPEC, &audit_info);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * netlbl_mgmt_listdef - Handle a LISTDEF message
58762306a36Sopenharmony_ci * @skb: the NETLINK buffer
58862306a36Sopenharmony_ci * @info: the Generic NETLINK info block
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * Description:
59162306a36Sopenharmony_ci * Process a user generated LISTDEF message and dumps the default domain
59262306a36Sopenharmony_ci * mapping in a form suitable for use in a kernel generated LISTDEF message.
59362306a36Sopenharmony_ci * Returns zero on success, negative values on failure.
59462306a36Sopenharmony_ci *
59562306a36Sopenharmony_ci */
59662306a36Sopenharmony_cistatic int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	int ret_val = -ENOMEM;
59962306a36Sopenharmony_ci	struct sk_buff *ans_skb = NULL;
60062306a36Sopenharmony_ci	void *data;
60162306a36Sopenharmony_ci	struct netlbl_dom_map *entry;
60262306a36Sopenharmony_ci	u16 family;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (info->attrs[NLBL_MGMT_A_FAMILY])
60562306a36Sopenharmony_ci		family = nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]);
60662306a36Sopenharmony_ci	else
60762306a36Sopenharmony_ci		family = AF_INET;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
61062306a36Sopenharmony_ci	if (ans_skb == NULL)
61162306a36Sopenharmony_ci		return -ENOMEM;
61262306a36Sopenharmony_ci	data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
61362306a36Sopenharmony_ci				 0, NLBL_MGMT_C_LISTDEF);
61462306a36Sopenharmony_ci	if (data == NULL)
61562306a36Sopenharmony_ci		goto listdef_failure;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	rcu_read_lock();
61862306a36Sopenharmony_ci	entry = netlbl_domhsh_getentry(NULL, family);
61962306a36Sopenharmony_ci	if (entry == NULL) {
62062306a36Sopenharmony_ci		ret_val = -ENOENT;
62162306a36Sopenharmony_ci		goto listdef_failure_lock;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci	ret_val = netlbl_mgmt_listentry(ans_skb, entry);
62462306a36Sopenharmony_ci	rcu_read_unlock();
62562306a36Sopenharmony_ci	if (ret_val != 0)
62662306a36Sopenharmony_ci		goto listdef_failure;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	genlmsg_end(ans_skb, data);
62962306a36Sopenharmony_ci	return genlmsg_reply(ans_skb, info);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cilistdef_failure_lock:
63262306a36Sopenharmony_ci	rcu_read_unlock();
63362306a36Sopenharmony_cilistdef_failure:
63462306a36Sopenharmony_ci	kfree_skb(ans_skb);
63562306a36Sopenharmony_ci	return ret_val;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
64062306a36Sopenharmony_ci * @skb: the skb to write to
64162306a36Sopenharmony_ci * @cb: the NETLINK callback
64262306a36Sopenharmony_ci * @protocol: the NetLabel protocol to use in the message
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Description:
64562306a36Sopenharmony_ci * This function is to be used in conjunction with netlbl_mgmt_protocols() to
64662306a36Sopenharmony_ci * answer a application's PROTOCOLS message.  Returns the size of the message
64762306a36Sopenharmony_ci * on success, negative values on failure.
64862306a36Sopenharmony_ci *
64962306a36Sopenharmony_ci */
65062306a36Sopenharmony_cistatic int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
65162306a36Sopenharmony_ci				    struct netlink_callback *cb,
65262306a36Sopenharmony_ci				    u32 protocol)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	int ret_val = -ENOMEM;
65562306a36Sopenharmony_ci	void *data;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
65862306a36Sopenharmony_ci			   &netlbl_mgmt_gnl_family, NLM_F_MULTI,
65962306a36Sopenharmony_ci			   NLBL_MGMT_C_PROTOCOLS);
66062306a36Sopenharmony_ci	if (data == NULL)
66162306a36Sopenharmony_ci		goto protocols_cb_failure;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol);
66462306a36Sopenharmony_ci	if (ret_val != 0)
66562306a36Sopenharmony_ci		goto protocols_cb_failure;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	genlmsg_end(skb, data);
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ciprotocols_cb_failure:
67162306a36Sopenharmony_ci	genlmsg_cancel(skb, data);
67262306a36Sopenharmony_ci	return ret_val;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci/**
67662306a36Sopenharmony_ci * netlbl_mgmt_protocols - Handle a PROTOCOLS message
67762306a36Sopenharmony_ci * @skb: the NETLINK buffer
67862306a36Sopenharmony_ci * @cb: the NETLINK callback
67962306a36Sopenharmony_ci *
68062306a36Sopenharmony_ci * Description:
68162306a36Sopenharmony_ci * Process a user generated PROTOCOLS message and respond accordingly.
68262306a36Sopenharmony_ci *
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_cistatic int netlbl_mgmt_protocols(struct sk_buff *skb,
68562306a36Sopenharmony_ci				 struct netlink_callback *cb)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	u32 protos_sent = cb->args[0];
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (protos_sent == 0) {
69062306a36Sopenharmony_ci		if (netlbl_mgmt_protocols_cb(skb,
69162306a36Sopenharmony_ci					     cb,
69262306a36Sopenharmony_ci					     NETLBL_NLTYPE_UNLABELED) < 0)
69362306a36Sopenharmony_ci			goto protocols_return;
69462306a36Sopenharmony_ci		protos_sent++;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	if (protos_sent == 1) {
69762306a36Sopenharmony_ci		if (netlbl_mgmt_protocols_cb(skb,
69862306a36Sopenharmony_ci					     cb,
69962306a36Sopenharmony_ci					     NETLBL_NLTYPE_CIPSOV4) < 0)
70062306a36Sopenharmony_ci			goto protocols_return;
70162306a36Sopenharmony_ci		protos_sent++;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
70462306a36Sopenharmony_ci	if (protos_sent == 2) {
70562306a36Sopenharmony_ci		if (netlbl_mgmt_protocols_cb(skb,
70662306a36Sopenharmony_ci					     cb,
70762306a36Sopenharmony_ci					     NETLBL_NLTYPE_CALIPSO) < 0)
70862306a36Sopenharmony_ci			goto protocols_return;
70962306a36Sopenharmony_ci		protos_sent++;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci#endif
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ciprotocols_return:
71462306a36Sopenharmony_ci	cb->args[0] = protos_sent;
71562306a36Sopenharmony_ci	return skb->len;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci/**
71962306a36Sopenharmony_ci * netlbl_mgmt_version - Handle a VERSION message
72062306a36Sopenharmony_ci * @skb: the NETLINK buffer
72162306a36Sopenharmony_ci * @info: the Generic NETLINK info block
72262306a36Sopenharmony_ci *
72362306a36Sopenharmony_ci * Description:
72462306a36Sopenharmony_ci * Process a user generated VERSION message and respond accordingly.  Returns
72562306a36Sopenharmony_ci * zero on success, negative values on failure.
72662306a36Sopenharmony_ci *
72762306a36Sopenharmony_ci */
72862306a36Sopenharmony_cistatic int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	int ret_val = -ENOMEM;
73162306a36Sopenharmony_ci	struct sk_buff *ans_skb = NULL;
73262306a36Sopenharmony_ci	void *data;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
73562306a36Sopenharmony_ci	if (ans_skb == NULL)
73662306a36Sopenharmony_ci		return -ENOMEM;
73762306a36Sopenharmony_ci	data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
73862306a36Sopenharmony_ci				 0, NLBL_MGMT_C_VERSION);
73962306a36Sopenharmony_ci	if (data == NULL)
74062306a36Sopenharmony_ci		goto version_failure;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	ret_val = nla_put_u32(ans_skb,
74362306a36Sopenharmony_ci			      NLBL_MGMT_A_VERSION,
74462306a36Sopenharmony_ci			      NETLBL_PROTO_VERSION);
74562306a36Sopenharmony_ci	if (ret_val != 0)
74662306a36Sopenharmony_ci		goto version_failure;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	genlmsg_end(ans_skb, data);
74962306a36Sopenharmony_ci	return genlmsg_reply(ans_skb, info);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_civersion_failure:
75262306a36Sopenharmony_ci	kfree_skb(ans_skb);
75362306a36Sopenharmony_ci	return ret_val;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/*
75862306a36Sopenharmony_ci * NetLabel Generic NETLINK Command Definitions
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic const struct genl_small_ops netlbl_mgmt_genl_ops[] = {
76262306a36Sopenharmony_ci	{
76362306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_ADD,
76462306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
76562306a36Sopenharmony_ci	.flags = GENL_ADMIN_PERM,
76662306a36Sopenharmony_ci	.doit = netlbl_mgmt_add,
76762306a36Sopenharmony_ci	.dumpit = NULL,
76862306a36Sopenharmony_ci	},
76962306a36Sopenharmony_ci	{
77062306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_REMOVE,
77162306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
77262306a36Sopenharmony_ci	.flags = GENL_ADMIN_PERM,
77362306a36Sopenharmony_ci	.doit = netlbl_mgmt_remove,
77462306a36Sopenharmony_ci	.dumpit = NULL,
77562306a36Sopenharmony_ci	},
77662306a36Sopenharmony_ci	{
77762306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_LISTALL,
77862306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
77962306a36Sopenharmony_ci	.flags = 0,
78062306a36Sopenharmony_ci	.doit = NULL,
78162306a36Sopenharmony_ci	.dumpit = netlbl_mgmt_listall,
78262306a36Sopenharmony_ci	},
78362306a36Sopenharmony_ci	{
78462306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_ADDDEF,
78562306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
78662306a36Sopenharmony_ci	.flags = GENL_ADMIN_PERM,
78762306a36Sopenharmony_ci	.doit = netlbl_mgmt_adddef,
78862306a36Sopenharmony_ci	.dumpit = NULL,
78962306a36Sopenharmony_ci	},
79062306a36Sopenharmony_ci	{
79162306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_REMOVEDEF,
79262306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
79362306a36Sopenharmony_ci	.flags = GENL_ADMIN_PERM,
79462306a36Sopenharmony_ci	.doit = netlbl_mgmt_removedef,
79562306a36Sopenharmony_ci	.dumpit = NULL,
79662306a36Sopenharmony_ci	},
79762306a36Sopenharmony_ci	{
79862306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_LISTDEF,
79962306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
80062306a36Sopenharmony_ci	.flags = 0,
80162306a36Sopenharmony_ci	.doit = netlbl_mgmt_listdef,
80262306a36Sopenharmony_ci	.dumpit = NULL,
80362306a36Sopenharmony_ci	},
80462306a36Sopenharmony_ci	{
80562306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_PROTOCOLS,
80662306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
80762306a36Sopenharmony_ci	.flags = 0,
80862306a36Sopenharmony_ci	.doit = NULL,
80962306a36Sopenharmony_ci	.dumpit = netlbl_mgmt_protocols,
81062306a36Sopenharmony_ci	},
81162306a36Sopenharmony_ci	{
81262306a36Sopenharmony_ci	.cmd = NLBL_MGMT_C_VERSION,
81362306a36Sopenharmony_ci	.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
81462306a36Sopenharmony_ci	.flags = 0,
81562306a36Sopenharmony_ci	.doit = netlbl_mgmt_version,
81662306a36Sopenharmony_ci	.dumpit = NULL,
81762306a36Sopenharmony_ci	},
81862306a36Sopenharmony_ci};
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic struct genl_family netlbl_mgmt_gnl_family __ro_after_init = {
82162306a36Sopenharmony_ci	.hdrsize = 0,
82262306a36Sopenharmony_ci	.name = NETLBL_NLTYPE_MGMT_NAME,
82362306a36Sopenharmony_ci	.version = NETLBL_PROTO_VERSION,
82462306a36Sopenharmony_ci	.maxattr = NLBL_MGMT_A_MAX,
82562306a36Sopenharmony_ci	.policy = netlbl_mgmt_genl_policy,
82662306a36Sopenharmony_ci	.module = THIS_MODULE,
82762306a36Sopenharmony_ci	.small_ops = netlbl_mgmt_genl_ops,
82862306a36Sopenharmony_ci	.n_small_ops = ARRAY_SIZE(netlbl_mgmt_genl_ops),
82962306a36Sopenharmony_ci	.resv_start_op = NLBL_MGMT_C_VERSION + 1,
83062306a36Sopenharmony_ci};
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/*
83362306a36Sopenharmony_ci * NetLabel Generic NETLINK Protocol Functions
83462306a36Sopenharmony_ci */
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/**
83762306a36Sopenharmony_ci * netlbl_mgmt_genl_init - Register the NetLabel management component
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * Description:
84062306a36Sopenharmony_ci * Register the NetLabel management component with the Generic NETLINK
84162306a36Sopenharmony_ci * mechanism.  Returns zero on success, negative values on failure.
84262306a36Sopenharmony_ci *
84362306a36Sopenharmony_ci */
84462306a36Sopenharmony_ciint __init netlbl_mgmt_genl_init(void)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	return genl_register_family(&netlbl_mgmt_gnl_family);
84762306a36Sopenharmony_ci}
848