18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CIPSO - Commercial IP Security Option
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is an implementation of the CIPSO 2.2 protocol as specified in
68c2ecf20Sopenharmony_ci * draft-ietf-cipso-ipsecurity-01.txt with additional tag types as found in
78c2ecf20Sopenharmony_ci * FIPS-188.  While CIPSO never became a full IETF RFC standard many vendors
88c2ecf20Sopenharmony_ci * have chosen to adopt the protocol and over the years it has become a
98c2ecf20Sopenharmony_ci * de-facto standard for labeled networking.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The CIPSO draft specification can be found in the kernel's Documentation
128c2ecf20Sopenharmony_ci * directory as well as the following URL:
138c2ecf20Sopenharmony_ci *   https://tools.ietf.org/id/draft-ietf-cipso-ipsecurity-01.txt
148c2ecf20Sopenharmony_ci * The FIPS-188 specification can be found at the following URL:
158c2ecf20Sopenharmony_ci *   https://www.itl.nist.gov/fipspubs/fip188.htm
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Author: Paul Moore <paul.moore@hp.com>
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/init.h>
258c2ecf20Sopenharmony_ci#include <linux/types.h>
268c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
278c2ecf20Sopenharmony_ci#include <linux/list.h>
288c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
298c2ecf20Sopenharmony_ci#include <linux/string.h>
308c2ecf20Sopenharmony_ci#include <linux/jhash.h>
318c2ecf20Sopenharmony_ci#include <linux/audit.h>
328c2ecf20Sopenharmony_ci#include <linux/slab.h>
338c2ecf20Sopenharmony_ci#include <net/ip.h>
348c2ecf20Sopenharmony_ci#include <net/icmp.h>
358c2ecf20Sopenharmony_ci#include <net/tcp.h>
368c2ecf20Sopenharmony_ci#include <net/netlabel.h>
378c2ecf20Sopenharmony_ci#include <net/cipso_ipv4.h>
388c2ecf20Sopenharmony_ci#include <linux/atomic.h>
398c2ecf20Sopenharmony_ci#include <linux/bug.h>
408c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* List of available DOI definitions */
438c2ecf20Sopenharmony_ci/* XXX - This currently assumes a minimal number of different DOIs in use,
448c2ecf20Sopenharmony_ci * if in practice there are a lot of different DOIs this list should
458c2ecf20Sopenharmony_ci * probably be turned into a hash table or something similar so we
468c2ecf20Sopenharmony_ci * can do quick lookups. */
478c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cipso_v4_doi_list_lock);
488c2ecf20Sopenharmony_cistatic LIST_HEAD(cipso_v4_doi_list);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Label mapping cache */
518c2ecf20Sopenharmony_ciint cipso_v4_cache_enabled = 1;
528c2ecf20Sopenharmony_ciint cipso_v4_cache_bucketsize = 10;
538c2ecf20Sopenharmony_ci#define CIPSO_V4_CACHE_BUCKETBITS     7
548c2ecf20Sopenharmony_ci#define CIPSO_V4_CACHE_BUCKETS        (1 << CIPSO_V4_CACHE_BUCKETBITS)
558c2ecf20Sopenharmony_ci#define CIPSO_V4_CACHE_REORDERLIMIT   10
568c2ecf20Sopenharmony_cistruct cipso_v4_map_cache_bkt {
578c2ecf20Sopenharmony_ci	spinlock_t lock;
588c2ecf20Sopenharmony_ci	u32 size;
598c2ecf20Sopenharmony_ci	struct list_head list;
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct cipso_v4_map_cache_entry {
638c2ecf20Sopenharmony_ci	u32 hash;
648c2ecf20Sopenharmony_ci	unsigned char *key;
658c2ecf20Sopenharmony_ci	size_t key_len;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	struct netlbl_lsm_cache *lsm_data;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	u32 activity;
708c2ecf20Sopenharmony_ci	struct list_head list;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct cipso_v4_map_cache_bkt *cipso_v4_cache;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* Restricted bitmap (tag #1) flags */
768c2ecf20Sopenharmony_ciint cipso_v4_rbm_optfmt = 0;
778c2ecf20Sopenharmony_ciint cipso_v4_rbm_strictvalid = 1;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/*
808c2ecf20Sopenharmony_ci * Protocol Constants
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* Maximum size of the CIPSO IP option, derived from the fact that the maximum
848c2ecf20Sopenharmony_ci * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */
858c2ecf20Sopenharmony_ci#define CIPSO_V4_OPT_LEN_MAX          40
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Length of the base CIPSO option, this includes the option type (1 byte), the
888c2ecf20Sopenharmony_ci * option length (1 byte), and the DOI (4 bytes). */
898c2ecf20Sopenharmony_ci#define CIPSO_V4_HDR_LEN              6
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Base length of the restrictive category bitmap tag (tag #1). */
928c2ecf20Sopenharmony_ci#define CIPSO_V4_TAG_RBM_BLEN         4
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* Base length of the enumerated category tag (tag #2). */
958c2ecf20Sopenharmony_ci#define CIPSO_V4_TAG_ENUM_BLEN        4
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* Base length of the ranged categories bitmap tag (tag #5). */
988c2ecf20Sopenharmony_ci#define CIPSO_V4_TAG_RNG_BLEN         4
998c2ecf20Sopenharmony_ci/* The maximum number of category ranges permitted in the ranged category tag
1008c2ecf20Sopenharmony_ci * (tag #5).  You may note that the IETF draft states that the maximum number
1018c2ecf20Sopenharmony_ci * of category ranges is 7, but if the low end of the last category range is
1028c2ecf20Sopenharmony_ci * zero then it is possible to fit 8 category ranges because the zero should
1038c2ecf20Sopenharmony_ci * be omitted. */
1048c2ecf20Sopenharmony_ci#define CIPSO_V4_TAG_RNG_CAT_MAX      8
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Base length of the local tag (non-standard tag).
1078c2ecf20Sopenharmony_ci *  Tag definition (may change between kernel versions)
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci * 0          8          16         24         32
1108c2ecf20Sopenharmony_ci * +----------+----------+----------+----------+
1118c2ecf20Sopenharmony_ci * | 10000000 | 00000110 | 32-bit secid value  |
1128c2ecf20Sopenharmony_ci * +----------+----------+----------+----------+
1138c2ecf20Sopenharmony_ci * | in (host byte order)|
1148c2ecf20Sopenharmony_ci * +----------+----------+
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_ci#define CIPSO_V4_TAG_LOC_BLEN         6
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * Helper Functions
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * cipso_v4_cache_entry_free - Frees a cache entry
1258c2ecf20Sopenharmony_ci * @entry: the entry to free
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * Description:
1288c2ecf20Sopenharmony_ci * This function frees the memory associated with a cache entry including the
1298c2ecf20Sopenharmony_ci * LSM cache data if there are no longer any users, i.e. reference count == 0.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	if (entry->lsm_data)
1358c2ecf20Sopenharmony_ci		netlbl_secattr_cache_free(entry->lsm_data);
1368c2ecf20Sopenharmony_ci	kfree(entry->key);
1378c2ecf20Sopenharmony_ci	kfree(entry);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache
1428c2ecf20Sopenharmony_ci * @key: the hash key
1438c2ecf20Sopenharmony_ci * @key_len: the length of the key in bytes
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * Description:
1468c2ecf20Sopenharmony_ci * The CIPSO tag hashing function.  Returns a 32-bit hash value.
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	return jhash(key, key_len, 0);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/*
1558c2ecf20Sopenharmony_ci * Label Mapping Cache Functions
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/**
1598c2ecf20Sopenharmony_ci * cipso_v4_cache_init - Initialize the CIPSO cache
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * Description:
1628c2ecf20Sopenharmony_ci * Initializes the CIPSO label mapping cache, this function should be called
1638c2ecf20Sopenharmony_ci * before any of the other functions defined in this file.  Returns zero on
1648c2ecf20Sopenharmony_ci * success, negative values on error.
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic int __init cipso_v4_cache_init(void)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	u32 iter;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS,
1728c2ecf20Sopenharmony_ci				 sizeof(struct cipso_v4_map_cache_bkt),
1738c2ecf20Sopenharmony_ci				 GFP_KERNEL);
1748c2ecf20Sopenharmony_ci	if (!cipso_v4_cache)
1758c2ecf20Sopenharmony_ci		return -ENOMEM;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
1788c2ecf20Sopenharmony_ci		spin_lock_init(&cipso_v4_cache[iter].lock);
1798c2ecf20Sopenharmony_ci		cipso_v4_cache[iter].size = 0;
1808c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&cipso_v4_cache[iter].list);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/**
1878c2ecf20Sopenharmony_ci * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci * Description:
1908c2ecf20Sopenharmony_ci * Invalidates and frees any entries in the CIPSO cache.  Returns zero on
1918c2ecf20Sopenharmony_ci * success and negative values on failure.
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_civoid cipso_v4_cache_invalidate(void)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry, *tmp_entry;
1978c2ecf20Sopenharmony_ci	u32 iter;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
2008c2ecf20Sopenharmony_ci		spin_lock_bh(&cipso_v4_cache[iter].lock);
2018c2ecf20Sopenharmony_ci		list_for_each_entry_safe(entry,
2028c2ecf20Sopenharmony_ci					 tmp_entry,
2038c2ecf20Sopenharmony_ci					 &cipso_v4_cache[iter].list, list) {
2048c2ecf20Sopenharmony_ci			list_del(&entry->list);
2058c2ecf20Sopenharmony_ci			cipso_v4_cache_entry_free(entry);
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci		cipso_v4_cache[iter].size = 0;
2088c2ecf20Sopenharmony_ci		spin_unlock_bh(&cipso_v4_cache[iter].lock);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/**
2138c2ecf20Sopenharmony_ci * cipso_v4_cache_check - Check the CIPSO cache for a label mapping
2148c2ecf20Sopenharmony_ci * @key: the buffer to check
2158c2ecf20Sopenharmony_ci * @key_len: buffer length in bytes
2168c2ecf20Sopenharmony_ci * @secattr: the security attribute struct to use
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Description:
2198c2ecf20Sopenharmony_ci * This function checks the cache to see if a label mapping already exists for
2208c2ecf20Sopenharmony_ci * the given key.  If there is a match then the cache is adjusted and the
2218c2ecf20Sopenharmony_ci * @secattr struct is populated with the correct LSM security attributes.  The
2228c2ecf20Sopenharmony_ci * cache is adjusted in the following manner if the entry is not already the
2238c2ecf20Sopenharmony_ci * first in the cache bucket:
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci *  1. The cache entry's activity counter is incremented
2268c2ecf20Sopenharmony_ci *  2. The previous (higher ranking) entry's activity counter is decremented
2278c2ecf20Sopenharmony_ci *  3. If the difference between the two activity counters is geater than
2288c2ecf20Sopenharmony_ci *     CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * Returns zero on success, -ENOENT for a cache miss, and other negative values
2318c2ecf20Sopenharmony_ci * on error.
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic int cipso_v4_cache_check(const unsigned char *key,
2358c2ecf20Sopenharmony_ci				u32 key_len,
2368c2ecf20Sopenharmony_ci				struct netlbl_lsm_secattr *secattr)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	u32 bkt;
2398c2ecf20Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry;
2408c2ecf20Sopenharmony_ci	struct cipso_v4_map_cache_entry *prev_entry = NULL;
2418c2ecf20Sopenharmony_ci	u32 hash;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!READ_ONCE(cipso_v4_cache_enabled))
2448c2ecf20Sopenharmony_ci		return -ENOENT;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	hash = cipso_v4_map_cache_hash(key, key_len);
2478c2ecf20Sopenharmony_ci	bkt = hash & (CIPSO_V4_CACHE_BUCKETS - 1);
2488c2ecf20Sopenharmony_ci	spin_lock_bh(&cipso_v4_cache[bkt].lock);
2498c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) {
2508c2ecf20Sopenharmony_ci		if (entry->hash == hash &&
2518c2ecf20Sopenharmony_ci		    entry->key_len == key_len &&
2528c2ecf20Sopenharmony_ci		    memcmp(entry->key, key, key_len) == 0) {
2538c2ecf20Sopenharmony_ci			entry->activity += 1;
2548c2ecf20Sopenharmony_ci			refcount_inc(&entry->lsm_data->refcount);
2558c2ecf20Sopenharmony_ci			secattr->cache = entry->lsm_data;
2568c2ecf20Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_CACHE;
2578c2ecf20Sopenharmony_ci			secattr->type = NETLBL_NLTYPE_CIPSOV4;
2588c2ecf20Sopenharmony_ci			if (!prev_entry) {
2598c2ecf20Sopenharmony_ci				spin_unlock_bh(&cipso_v4_cache[bkt].lock);
2608c2ecf20Sopenharmony_ci				return 0;
2618c2ecf20Sopenharmony_ci			}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			if (prev_entry->activity > 0)
2648c2ecf20Sopenharmony_ci				prev_entry->activity -= 1;
2658c2ecf20Sopenharmony_ci			if (entry->activity > prev_entry->activity &&
2668c2ecf20Sopenharmony_ci			    entry->activity - prev_entry->activity >
2678c2ecf20Sopenharmony_ci			    CIPSO_V4_CACHE_REORDERLIMIT) {
2688c2ecf20Sopenharmony_ci				__list_del(entry->list.prev, entry->list.next);
2698c2ecf20Sopenharmony_ci				__list_add(&entry->list,
2708c2ecf20Sopenharmony_ci					   prev_entry->list.prev,
2718c2ecf20Sopenharmony_ci					   &prev_entry->list);
2728c2ecf20Sopenharmony_ci			}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci			spin_unlock_bh(&cipso_v4_cache[bkt].lock);
2758c2ecf20Sopenharmony_ci			return 0;
2768c2ecf20Sopenharmony_ci		}
2778c2ecf20Sopenharmony_ci		prev_entry = entry;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return -ENOENT;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/**
2858c2ecf20Sopenharmony_ci * cipso_v4_cache_add - Add an entry to the CIPSO cache
2868c2ecf20Sopenharmony_ci * @cipso_ptr: pointer to CIPSO IP option
2878c2ecf20Sopenharmony_ci * @secattr: the packet's security attributes
2888c2ecf20Sopenharmony_ci *
2898c2ecf20Sopenharmony_ci * Description:
2908c2ecf20Sopenharmony_ci * Add a new entry into the CIPSO label mapping cache.  Add the new entry to
2918c2ecf20Sopenharmony_ci * head of the cache bucket's list, if the cache bucket is out of room remove
2928c2ecf20Sopenharmony_ci * the last entry in the list first.  It is important to note that there is
2938c2ecf20Sopenharmony_ci * currently no checking for duplicate keys.  Returns zero on success,
2948c2ecf20Sopenharmony_ci * negative values on failure.
2958c2ecf20Sopenharmony_ci *
2968c2ecf20Sopenharmony_ci */
2978c2ecf20Sopenharmony_ciint cipso_v4_cache_add(const unsigned char *cipso_ptr,
2988c2ecf20Sopenharmony_ci		       const struct netlbl_lsm_secattr *secattr)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	int bkt_size = READ_ONCE(cipso_v4_cache_bucketsize);
3018c2ecf20Sopenharmony_ci	int ret_val = -EPERM;
3028c2ecf20Sopenharmony_ci	u32 bkt;
3038c2ecf20Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry = NULL;
3048c2ecf20Sopenharmony_ci	struct cipso_v4_map_cache_entry *old_entry = NULL;
3058c2ecf20Sopenharmony_ci	u32 cipso_ptr_len;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (!READ_ONCE(cipso_v4_cache_enabled) || bkt_size <= 0)
3088c2ecf20Sopenharmony_ci		return 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	cipso_ptr_len = cipso_ptr[1];
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
3138c2ecf20Sopenharmony_ci	if (!entry)
3148c2ecf20Sopenharmony_ci		return -ENOMEM;
3158c2ecf20Sopenharmony_ci	entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
3168c2ecf20Sopenharmony_ci	if (!entry->key) {
3178c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
3188c2ecf20Sopenharmony_ci		goto cache_add_failure;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	entry->key_len = cipso_ptr_len;
3218c2ecf20Sopenharmony_ci	entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
3228c2ecf20Sopenharmony_ci	refcount_inc(&secattr->cache->refcount);
3238c2ecf20Sopenharmony_ci	entry->lsm_data = secattr->cache;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETS - 1);
3268c2ecf20Sopenharmony_ci	spin_lock_bh(&cipso_v4_cache[bkt].lock);
3278c2ecf20Sopenharmony_ci	if (cipso_v4_cache[bkt].size < bkt_size) {
3288c2ecf20Sopenharmony_ci		list_add(&entry->list, &cipso_v4_cache[bkt].list);
3298c2ecf20Sopenharmony_ci		cipso_v4_cache[bkt].size += 1;
3308c2ecf20Sopenharmony_ci	} else {
3318c2ecf20Sopenharmony_ci		old_entry = list_entry(cipso_v4_cache[bkt].list.prev,
3328c2ecf20Sopenharmony_ci				       struct cipso_v4_map_cache_entry, list);
3338c2ecf20Sopenharmony_ci		list_del(&old_entry->list);
3348c2ecf20Sopenharmony_ci		list_add(&entry->list, &cipso_v4_cache[bkt].list);
3358c2ecf20Sopenharmony_ci		cipso_v4_cache_entry_free(old_entry);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cicache_add_failure:
3428c2ecf20Sopenharmony_ci	if (entry)
3438c2ecf20Sopenharmony_ci		cipso_v4_cache_entry_free(entry);
3448c2ecf20Sopenharmony_ci	return ret_val;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/*
3488c2ecf20Sopenharmony_ci * DOI List Functions
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/**
3528c2ecf20Sopenharmony_ci * cipso_v4_doi_search - Searches for a DOI definition
3538c2ecf20Sopenharmony_ci * @doi: the DOI to search for
3548c2ecf20Sopenharmony_ci *
3558c2ecf20Sopenharmony_ci * Description:
3568c2ecf20Sopenharmony_ci * Search the DOI definition list for a DOI definition with a DOI value that
3578c2ecf20Sopenharmony_ci * matches @doi.  The caller is responsible for calling rcu_read_[un]lock().
3588c2ecf20Sopenharmony_ci * Returns a pointer to the DOI definition on success and NULL on failure.
3598c2ecf20Sopenharmony_ci */
3608c2ecf20Sopenharmony_cistatic struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct cipso_v4_doi *iter;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
3658c2ecf20Sopenharmony_ci		if (iter->doi == doi && refcount_read(&iter->refcount))
3668c2ecf20Sopenharmony_ci			return iter;
3678c2ecf20Sopenharmony_ci	return NULL;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/**
3718c2ecf20Sopenharmony_ci * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
3728c2ecf20Sopenharmony_ci * @doi_def: the DOI structure
3738c2ecf20Sopenharmony_ci * @audit_info: NetLabel audit information
3748c2ecf20Sopenharmony_ci *
3758c2ecf20Sopenharmony_ci * Description:
3768c2ecf20Sopenharmony_ci * The caller defines a new DOI for use by the CIPSO engine and calls this
3778c2ecf20Sopenharmony_ci * function to add it to the list of acceptable domains.  The caller must
3788c2ecf20Sopenharmony_ci * ensure that the mapping table specified in @doi_def->map meets all of the
3798c2ecf20Sopenharmony_ci * requirements of the mapping type (see cipso_ipv4.h for details).  Returns
3808c2ecf20Sopenharmony_ci * zero on success and non-zero on failure.
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_ciint cipso_v4_doi_add(struct cipso_v4_doi *doi_def,
3848c2ecf20Sopenharmony_ci		     struct netlbl_audit *audit_info)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	int ret_val = -EINVAL;
3878c2ecf20Sopenharmony_ci	u32 iter;
3888c2ecf20Sopenharmony_ci	u32 doi;
3898c2ecf20Sopenharmony_ci	u32 doi_type;
3908c2ecf20Sopenharmony_ci	struct audit_buffer *audit_buf;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	doi = doi_def->doi;
3938c2ecf20Sopenharmony_ci	doi_type = doi_def->type;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (doi_def->doi == CIPSO_V4_DOI_UNKNOWN)
3968c2ecf20Sopenharmony_ci		goto doi_add_return;
3978c2ecf20Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
3988c2ecf20Sopenharmony_ci		switch (doi_def->tags[iter]) {
3998c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
4008c2ecf20Sopenharmony_ci			break;
4018c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
4028c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
4038c2ecf20Sopenharmony_ci			if (doi_def->type != CIPSO_V4_MAP_PASS)
4048c2ecf20Sopenharmony_ci				goto doi_add_return;
4058c2ecf20Sopenharmony_ci			break;
4068c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
4078c2ecf20Sopenharmony_ci			if (doi_def->type != CIPSO_V4_MAP_LOCAL)
4088c2ecf20Sopenharmony_ci				goto doi_add_return;
4098c2ecf20Sopenharmony_ci			break;
4108c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_INVALID:
4118c2ecf20Sopenharmony_ci			if (iter == 0)
4128c2ecf20Sopenharmony_ci				goto doi_add_return;
4138c2ecf20Sopenharmony_ci			break;
4148c2ecf20Sopenharmony_ci		default:
4158c2ecf20Sopenharmony_ci			goto doi_add_return;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	refcount_set(&doi_def->refcount, 1);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	spin_lock(&cipso_v4_doi_list_lock);
4228c2ecf20Sopenharmony_ci	if (cipso_v4_doi_search(doi_def->doi)) {
4238c2ecf20Sopenharmony_ci		spin_unlock(&cipso_v4_doi_list_lock);
4248c2ecf20Sopenharmony_ci		ret_val = -EEXIST;
4258c2ecf20Sopenharmony_ci		goto doi_add_return;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
4288c2ecf20Sopenharmony_ci	spin_unlock(&cipso_v4_doi_list_lock);
4298c2ecf20Sopenharmony_ci	ret_val = 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cidoi_add_return:
4328c2ecf20Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info);
4338c2ecf20Sopenharmony_ci	if (audit_buf) {
4348c2ecf20Sopenharmony_ci		const char *type_str;
4358c2ecf20Sopenharmony_ci		switch (doi_type) {
4368c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
4378c2ecf20Sopenharmony_ci			type_str = "trans";
4388c2ecf20Sopenharmony_ci			break;
4398c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
4408c2ecf20Sopenharmony_ci			type_str = "pass";
4418c2ecf20Sopenharmony_ci			break;
4428c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_LOCAL:
4438c2ecf20Sopenharmony_ci			type_str = "local";
4448c2ecf20Sopenharmony_ci			break;
4458c2ecf20Sopenharmony_ci		default:
4468c2ecf20Sopenharmony_ci			type_str = "(unknown)";
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci		audit_log_format(audit_buf,
4498c2ecf20Sopenharmony_ci				 " cipso_doi=%u cipso_type=%s res=%u",
4508c2ecf20Sopenharmony_ci				 doi, type_str, ret_val == 0 ? 1 : 0);
4518c2ecf20Sopenharmony_ci		audit_log_end(audit_buf);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return ret_val;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/**
4588c2ecf20Sopenharmony_ci * cipso_v4_doi_free - Frees a DOI definition
4598c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci * Description:
4628c2ecf20Sopenharmony_ci * This function frees all of the memory associated with a DOI definition.
4638c2ecf20Sopenharmony_ci *
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_civoid cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	if (!doi_def)
4688c2ecf20Sopenharmony_ci		return;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	switch (doi_def->type) {
4718c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
4728c2ecf20Sopenharmony_ci		kfree(doi_def->map.std->lvl.cipso);
4738c2ecf20Sopenharmony_ci		kfree(doi_def->map.std->lvl.local);
4748c2ecf20Sopenharmony_ci		kfree(doi_def->map.std->cat.cipso);
4758c2ecf20Sopenharmony_ci		kfree(doi_def->map.std->cat.local);
4768c2ecf20Sopenharmony_ci		kfree(doi_def->map.std);
4778c2ecf20Sopenharmony_ci		break;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci	kfree(doi_def);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci/**
4838c2ecf20Sopenharmony_ci * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
4848c2ecf20Sopenharmony_ci * @entry: the entry's RCU field
4858c2ecf20Sopenharmony_ci *
4868c2ecf20Sopenharmony_ci * Description:
4878c2ecf20Sopenharmony_ci * This function is designed to be used as a callback to the call_rcu()
4888c2ecf20Sopenharmony_ci * function so that the memory allocated to the DOI definition can be released
4898c2ecf20Sopenharmony_ci * safely.
4908c2ecf20Sopenharmony_ci *
4918c2ecf20Sopenharmony_ci */
4928c2ecf20Sopenharmony_cistatic void cipso_v4_doi_free_rcu(struct rcu_head *entry)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct cipso_v4_doi *doi_def;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	doi_def = container_of(entry, struct cipso_v4_doi, rcu);
4978c2ecf20Sopenharmony_ci	cipso_v4_doi_free(doi_def);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/**
5018c2ecf20Sopenharmony_ci * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
5028c2ecf20Sopenharmony_ci * @doi: the DOI value
5038c2ecf20Sopenharmony_ci * @audit_info: NetLabel audit information
5048c2ecf20Sopenharmony_ci *
5058c2ecf20Sopenharmony_ci * Description:
5068c2ecf20Sopenharmony_ci * Removes a DOI definition from the CIPSO engine.  The NetLabel routines will
5078c2ecf20Sopenharmony_ci * be called to release their own LSM domain mappings as well as our own
5088c2ecf20Sopenharmony_ci * domain list.  Returns zero on success and negative values on failure.
5098c2ecf20Sopenharmony_ci *
5108c2ecf20Sopenharmony_ci */
5118c2ecf20Sopenharmony_ciint cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	int ret_val;
5148c2ecf20Sopenharmony_ci	struct cipso_v4_doi *doi_def;
5158c2ecf20Sopenharmony_ci	struct audit_buffer *audit_buf;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	spin_lock(&cipso_v4_doi_list_lock);
5188c2ecf20Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
5198c2ecf20Sopenharmony_ci	if (!doi_def) {
5208c2ecf20Sopenharmony_ci		spin_unlock(&cipso_v4_doi_list_lock);
5218c2ecf20Sopenharmony_ci		ret_val = -ENOENT;
5228c2ecf20Sopenharmony_ci		goto doi_remove_return;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	list_del_rcu(&doi_def->list);
5258c2ecf20Sopenharmony_ci	spin_unlock(&cipso_v4_doi_list_lock);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	cipso_v4_doi_putdef(doi_def);
5288c2ecf20Sopenharmony_ci	ret_val = 0;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cidoi_remove_return:
5318c2ecf20Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info);
5328c2ecf20Sopenharmony_ci	if (audit_buf) {
5338c2ecf20Sopenharmony_ci		audit_log_format(audit_buf,
5348c2ecf20Sopenharmony_ci				 " cipso_doi=%u res=%u",
5358c2ecf20Sopenharmony_ci				 doi, ret_val == 0 ? 1 : 0);
5368c2ecf20Sopenharmony_ci		audit_log_end(audit_buf);
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	return ret_val;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/**
5438c2ecf20Sopenharmony_ci * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
5448c2ecf20Sopenharmony_ci * @doi: the DOI value
5458c2ecf20Sopenharmony_ci *
5468c2ecf20Sopenharmony_ci * Description:
5478c2ecf20Sopenharmony_ci * Searches for a valid DOI definition and if one is found it is returned to
5488c2ecf20Sopenharmony_ci * the caller.  Otherwise NULL is returned.  The caller must ensure that
5498c2ecf20Sopenharmony_ci * rcu_read_lock() is held while accessing the returned definition and the DOI
5508c2ecf20Sopenharmony_ci * definition reference count is decremented when the caller is done.
5518c2ecf20Sopenharmony_ci *
5528c2ecf20Sopenharmony_ci */
5538c2ecf20Sopenharmony_cistruct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct cipso_v4_doi *doi_def;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	rcu_read_lock();
5588c2ecf20Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
5598c2ecf20Sopenharmony_ci	if (!doi_def)
5608c2ecf20Sopenharmony_ci		goto doi_getdef_return;
5618c2ecf20Sopenharmony_ci	if (!refcount_inc_not_zero(&doi_def->refcount))
5628c2ecf20Sopenharmony_ci		doi_def = NULL;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cidoi_getdef_return:
5658c2ecf20Sopenharmony_ci	rcu_read_unlock();
5668c2ecf20Sopenharmony_ci	return doi_def;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * cipso_v4_doi_putdef - Releases a reference for the given DOI definition
5718c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
5728c2ecf20Sopenharmony_ci *
5738c2ecf20Sopenharmony_ci * Description:
5748c2ecf20Sopenharmony_ci * Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
5758c2ecf20Sopenharmony_ci *
5768c2ecf20Sopenharmony_ci */
5778c2ecf20Sopenharmony_civoid cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	if (!doi_def)
5808c2ecf20Sopenharmony_ci		return;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&doi_def->refcount))
5838c2ecf20Sopenharmony_ci		return;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	cipso_v4_cache_invalidate();
5868c2ecf20Sopenharmony_ci	call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci/**
5908c2ecf20Sopenharmony_ci * cipso_v4_doi_walk - Iterate through the DOI definitions
5918c2ecf20Sopenharmony_ci * @skip_cnt: skip past this number of DOI definitions, updated
5928c2ecf20Sopenharmony_ci * @callback: callback for each DOI definition
5938c2ecf20Sopenharmony_ci * @cb_arg: argument for the callback function
5948c2ecf20Sopenharmony_ci *
5958c2ecf20Sopenharmony_ci * Description:
5968c2ecf20Sopenharmony_ci * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
5978c2ecf20Sopenharmony_ci * For each entry call @callback, if @callback returns a negative value stop
5988c2ecf20Sopenharmony_ci * 'walking' through the list and return.  Updates the value in @skip_cnt upon
5998c2ecf20Sopenharmony_ci * return.  Returns zero on success, negative values on failure.
6008c2ecf20Sopenharmony_ci *
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_ciint cipso_v4_doi_walk(u32 *skip_cnt,
6038c2ecf20Sopenharmony_ci		     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
6048c2ecf20Sopenharmony_ci		     void *cb_arg)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	int ret_val = -ENOENT;
6078c2ecf20Sopenharmony_ci	u32 doi_cnt = 0;
6088c2ecf20Sopenharmony_ci	struct cipso_v4_doi *iter_doi;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	rcu_read_lock();
6118c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
6128c2ecf20Sopenharmony_ci		if (refcount_read(&iter_doi->refcount) > 0) {
6138c2ecf20Sopenharmony_ci			if (doi_cnt++ < *skip_cnt)
6148c2ecf20Sopenharmony_ci				continue;
6158c2ecf20Sopenharmony_ci			ret_val = callback(iter_doi, cb_arg);
6168c2ecf20Sopenharmony_ci			if (ret_val < 0) {
6178c2ecf20Sopenharmony_ci				doi_cnt--;
6188c2ecf20Sopenharmony_ci				goto doi_walk_return;
6198c2ecf20Sopenharmony_ci			}
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cidoi_walk_return:
6238c2ecf20Sopenharmony_ci	rcu_read_unlock();
6248c2ecf20Sopenharmony_ci	*skip_cnt = doi_cnt;
6258c2ecf20Sopenharmony_ci	return ret_val;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/*
6298c2ecf20Sopenharmony_ci * Label Mapping Functions
6308c2ecf20Sopenharmony_ci */
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/**
6338c2ecf20Sopenharmony_ci * cipso_v4_map_lvl_valid - Checks to see if the given level is understood
6348c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
6358c2ecf20Sopenharmony_ci * @level: the level to check
6368c2ecf20Sopenharmony_ci *
6378c2ecf20Sopenharmony_ci * Description:
6388c2ecf20Sopenharmony_ci * Checks the given level against the given DOI definition and returns a
6398c2ecf20Sopenharmony_ci * negative value if the level does not have a valid mapping and a zero value
6408c2ecf20Sopenharmony_ci * if the level is defined by the DOI.
6418c2ecf20Sopenharmony_ci *
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_cistatic int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	switch (doi_def->type) {
6468c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
6478c2ecf20Sopenharmony_ci		return 0;
6488c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
6498c2ecf20Sopenharmony_ci		if ((level < doi_def->map.std->lvl.cipso_size) &&
6508c2ecf20Sopenharmony_ci		    (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL))
6518c2ecf20Sopenharmony_ci			return 0;
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	return -EFAULT;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci/**
6598c2ecf20Sopenharmony_ci * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network
6608c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
6618c2ecf20Sopenharmony_ci * @host_lvl: the host MLS level
6628c2ecf20Sopenharmony_ci * @net_lvl: the network/CIPSO MLS level
6638c2ecf20Sopenharmony_ci *
6648c2ecf20Sopenharmony_ci * Description:
6658c2ecf20Sopenharmony_ci * Perform a label mapping to translate a local MLS level to the correct
6668c2ecf20Sopenharmony_ci * CIPSO level using the given DOI definition.  Returns zero on success,
6678c2ecf20Sopenharmony_ci * negative values otherwise.
6688c2ecf20Sopenharmony_ci *
6698c2ecf20Sopenharmony_ci */
6708c2ecf20Sopenharmony_cistatic int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
6718c2ecf20Sopenharmony_ci				 u32 host_lvl,
6728c2ecf20Sopenharmony_ci				 u32 *net_lvl)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	switch (doi_def->type) {
6758c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
6768c2ecf20Sopenharmony_ci		*net_lvl = host_lvl;
6778c2ecf20Sopenharmony_ci		return 0;
6788c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
6798c2ecf20Sopenharmony_ci		if (host_lvl < doi_def->map.std->lvl.local_size &&
6808c2ecf20Sopenharmony_ci		    doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
6818c2ecf20Sopenharmony_ci			*net_lvl = doi_def->map.std->lvl.local[host_lvl];
6828c2ecf20Sopenharmony_ci			return 0;
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci		return -EPERM;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	return -EINVAL;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci/**
6918c2ecf20Sopenharmony_ci * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host
6928c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
6938c2ecf20Sopenharmony_ci * @net_lvl: the network/CIPSO MLS level
6948c2ecf20Sopenharmony_ci * @host_lvl: the host MLS level
6958c2ecf20Sopenharmony_ci *
6968c2ecf20Sopenharmony_ci * Description:
6978c2ecf20Sopenharmony_ci * Perform a label mapping to translate a CIPSO level to the correct local MLS
6988c2ecf20Sopenharmony_ci * level using the given DOI definition.  Returns zero on success, negative
6998c2ecf20Sopenharmony_ci * values otherwise.
7008c2ecf20Sopenharmony_ci *
7018c2ecf20Sopenharmony_ci */
7028c2ecf20Sopenharmony_cistatic int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
7038c2ecf20Sopenharmony_ci				 u32 net_lvl,
7048c2ecf20Sopenharmony_ci				 u32 *host_lvl)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	struct cipso_v4_std_map_tbl *map_tbl;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	switch (doi_def->type) {
7098c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
7108c2ecf20Sopenharmony_ci		*host_lvl = net_lvl;
7118c2ecf20Sopenharmony_ci		return 0;
7128c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
7138c2ecf20Sopenharmony_ci		map_tbl = doi_def->map.std;
7148c2ecf20Sopenharmony_ci		if (net_lvl < map_tbl->lvl.cipso_size &&
7158c2ecf20Sopenharmony_ci		    map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
7168c2ecf20Sopenharmony_ci			*host_lvl = doi_def->map.std->lvl.cipso[net_lvl];
7178c2ecf20Sopenharmony_ci			return 0;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci		return -EPERM;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	return -EINVAL;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/**
7268c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid
7278c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
7288c2ecf20Sopenharmony_ci * @bitmap: category bitmap
7298c2ecf20Sopenharmony_ci * @bitmap_len: bitmap length in bytes
7308c2ecf20Sopenharmony_ci *
7318c2ecf20Sopenharmony_ci * Description:
7328c2ecf20Sopenharmony_ci * Checks the given category bitmap against the given DOI definition and
7338c2ecf20Sopenharmony_ci * returns a negative value if any of the categories in the bitmap do not have
7348c2ecf20Sopenharmony_ci * a valid mapping and a zero value if all of the categories are valid.
7358c2ecf20Sopenharmony_ci *
7368c2ecf20Sopenharmony_ci */
7378c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
7388c2ecf20Sopenharmony_ci				      const unsigned char *bitmap,
7398c2ecf20Sopenharmony_ci				      u32 bitmap_len)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	int cat = -1;
7428c2ecf20Sopenharmony_ci	u32 bitmap_len_bits = bitmap_len * 8;
7438c2ecf20Sopenharmony_ci	u32 cipso_cat_size;
7448c2ecf20Sopenharmony_ci	u32 *cipso_array;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	switch (doi_def->type) {
7478c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
7488c2ecf20Sopenharmony_ci		return 0;
7498c2ecf20Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
7508c2ecf20Sopenharmony_ci		cipso_cat_size = doi_def->map.std->cat.cipso_size;
7518c2ecf20Sopenharmony_ci		cipso_array = doi_def->map.std->cat.cipso;
7528c2ecf20Sopenharmony_ci		for (;;) {
7538c2ecf20Sopenharmony_ci			cat = netlbl_bitmap_walk(bitmap,
7548c2ecf20Sopenharmony_ci						 bitmap_len_bits,
7558c2ecf20Sopenharmony_ci						 cat + 1,
7568c2ecf20Sopenharmony_ci						 1);
7578c2ecf20Sopenharmony_ci			if (cat < 0)
7588c2ecf20Sopenharmony_ci				break;
7598c2ecf20Sopenharmony_ci			if (cat >= cipso_cat_size ||
7608c2ecf20Sopenharmony_ci			    cipso_array[cat] >= CIPSO_V4_INV_CAT)
7618c2ecf20Sopenharmony_ci				return -EFAULT;
7628c2ecf20Sopenharmony_ci		}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		if (cat == -1)
7658c2ecf20Sopenharmony_ci			return 0;
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	return -EFAULT;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci/**
7738c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network
7748c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
7758c2ecf20Sopenharmony_ci * @secattr: the security attributes
7768c2ecf20Sopenharmony_ci * @net_cat: the zero'd out category bitmap in network/CIPSO format
7778c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
7788c2ecf20Sopenharmony_ci *
7798c2ecf20Sopenharmony_ci * Description:
7808c2ecf20Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
7818c2ecf20Sopenharmony_ci * correct CIPSO bitmap using the given DOI definition.  Returns the minimum
7828c2ecf20Sopenharmony_ci * size in bytes of the network bitmap on success, negative values otherwise.
7838c2ecf20Sopenharmony_ci *
7848c2ecf20Sopenharmony_ci */
7858c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
7868c2ecf20Sopenharmony_ci				     const struct netlbl_lsm_secattr *secattr,
7878c2ecf20Sopenharmony_ci				     unsigned char *net_cat,
7888c2ecf20Sopenharmony_ci				     u32 net_cat_len)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	int host_spot = -1;
7918c2ecf20Sopenharmony_ci	u32 net_spot = CIPSO_V4_INV_CAT;
7928c2ecf20Sopenharmony_ci	u32 net_spot_max = 0;
7938c2ecf20Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
7948c2ecf20Sopenharmony_ci	u32 host_cat_size = 0;
7958c2ecf20Sopenharmony_ci	u32 *host_cat_array = NULL;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	if (doi_def->type == CIPSO_V4_MAP_TRANS) {
7988c2ecf20Sopenharmony_ci		host_cat_size = doi_def->map.std->cat.local_size;
7998c2ecf20Sopenharmony_ci		host_cat_array = doi_def->map.std->cat.local;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	for (;;) {
8038c2ecf20Sopenharmony_ci		host_spot = netlbl_catmap_walk(secattr->attr.mls.cat,
8048c2ecf20Sopenharmony_ci					       host_spot + 1);
8058c2ecf20Sopenharmony_ci		if (host_spot < 0)
8068c2ecf20Sopenharmony_ci			break;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		switch (doi_def->type) {
8098c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
8108c2ecf20Sopenharmony_ci			net_spot = host_spot;
8118c2ecf20Sopenharmony_ci			break;
8128c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
8138c2ecf20Sopenharmony_ci			if (host_spot >= host_cat_size)
8148c2ecf20Sopenharmony_ci				return -EPERM;
8158c2ecf20Sopenharmony_ci			net_spot = host_cat_array[host_spot];
8168c2ecf20Sopenharmony_ci			if (net_spot >= CIPSO_V4_INV_CAT)
8178c2ecf20Sopenharmony_ci				return -EPERM;
8188c2ecf20Sopenharmony_ci			break;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci		if (net_spot >= net_clen_bits)
8218c2ecf20Sopenharmony_ci			return -ENOSPC;
8228c2ecf20Sopenharmony_ci		netlbl_bitmap_setbit(net_cat, net_spot, 1);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		if (net_spot > net_spot_max)
8258c2ecf20Sopenharmony_ci			net_spot_max = net_spot;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (++net_spot_max % 8)
8298c2ecf20Sopenharmony_ci		return net_spot_max / 8 + 1;
8308c2ecf20Sopenharmony_ci	return net_spot_max / 8;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci/**
8348c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host
8358c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
8368c2ecf20Sopenharmony_ci * @net_cat: the category bitmap in network/CIPSO format
8378c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
8388c2ecf20Sopenharmony_ci * @secattr: the security attributes
8398c2ecf20Sopenharmony_ci *
8408c2ecf20Sopenharmony_ci * Description:
8418c2ecf20Sopenharmony_ci * Perform a label mapping to translate a CIPSO bitmap to the correct local
8428c2ecf20Sopenharmony_ci * MLS category bitmap using the given DOI definition.  Returns zero on
8438c2ecf20Sopenharmony_ci * success, negative values on failure.
8448c2ecf20Sopenharmony_ci *
8458c2ecf20Sopenharmony_ci */
8468c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
8478c2ecf20Sopenharmony_ci				     const unsigned char *net_cat,
8488c2ecf20Sopenharmony_ci				     u32 net_cat_len,
8498c2ecf20Sopenharmony_ci				     struct netlbl_lsm_secattr *secattr)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	int ret_val;
8528c2ecf20Sopenharmony_ci	int net_spot = -1;
8538c2ecf20Sopenharmony_ci	u32 host_spot = CIPSO_V4_INV_CAT;
8548c2ecf20Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
8558c2ecf20Sopenharmony_ci	u32 net_cat_size = 0;
8568c2ecf20Sopenharmony_ci	u32 *net_cat_array = NULL;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (doi_def->type == CIPSO_V4_MAP_TRANS) {
8598c2ecf20Sopenharmony_ci		net_cat_size = doi_def->map.std->cat.cipso_size;
8608c2ecf20Sopenharmony_ci		net_cat_array = doi_def->map.std->cat.cipso;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	for (;;) {
8648c2ecf20Sopenharmony_ci		net_spot = netlbl_bitmap_walk(net_cat,
8658c2ecf20Sopenharmony_ci					      net_clen_bits,
8668c2ecf20Sopenharmony_ci					      net_spot + 1,
8678c2ecf20Sopenharmony_ci					      1);
8688c2ecf20Sopenharmony_ci		if (net_spot < 0) {
8698c2ecf20Sopenharmony_ci			if (net_spot == -2)
8708c2ecf20Sopenharmony_ci				return -EFAULT;
8718c2ecf20Sopenharmony_ci			return 0;
8728c2ecf20Sopenharmony_ci		}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		switch (doi_def->type) {
8758c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
8768c2ecf20Sopenharmony_ci			host_spot = net_spot;
8778c2ecf20Sopenharmony_ci			break;
8788c2ecf20Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
8798c2ecf20Sopenharmony_ci			if (net_spot >= net_cat_size)
8808c2ecf20Sopenharmony_ci				return -EPERM;
8818c2ecf20Sopenharmony_ci			host_spot = net_cat_array[net_spot];
8828c2ecf20Sopenharmony_ci			if (host_spot >= CIPSO_V4_INV_CAT)
8838c2ecf20Sopenharmony_ci				return -EPERM;
8848c2ecf20Sopenharmony_ci			break;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
8878c2ecf20Sopenharmony_ci						       host_spot,
8888c2ecf20Sopenharmony_ci						       GFP_ATOMIC);
8898c2ecf20Sopenharmony_ci		if (ret_val != 0)
8908c2ecf20Sopenharmony_ci			return ret_val;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	return -EINVAL;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/**
8978c2ecf20Sopenharmony_ci * cipso_v4_map_cat_enum_valid - Checks to see if the categories are valid
8988c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
8998c2ecf20Sopenharmony_ci * @enumcat: category list
9008c2ecf20Sopenharmony_ci * @enumcat_len: length of the category list in bytes
9018c2ecf20Sopenharmony_ci *
9028c2ecf20Sopenharmony_ci * Description:
9038c2ecf20Sopenharmony_ci * Checks the given categories against the given DOI definition and returns a
9048c2ecf20Sopenharmony_ci * negative value if any of the categories do not have a valid mapping and a
9058c2ecf20Sopenharmony_ci * zero value if all of the categories are valid.
9068c2ecf20Sopenharmony_ci *
9078c2ecf20Sopenharmony_ci */
9088c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_enum_valid(const struct cipso_v4_doi *doi_def,
9098c2ecf20Sopenharmony_ci				       const unsigned char *enumcat,
9108c2ecf20Sopenharmony_ci				       u32 enumcat_len)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	u16 cat;
9138c2ecf20Sopenharmony_ci	int cat_prev = -1;
9148c2ecf20Sopenharmony_ci	u32 iter;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (doi_def->type != CIPSO_V4_MAP_PASS || enumcat_len & 0x01)
9178c2ecf20Sopenharmony_ci		return -EFAULT;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	for (iter = 0; iter < enumcat_len; iter += 2) {
9208c2ecf20Sopenharmony_ci		cat = get_unaligned_be16(&enumcat[iter]);
9218c2ecf20Sopenharmony_ci		if (cat <= cat_prev)
9228c2ecf20Sopenharmony_ci			return -EFAULT;
9238c2ecf20Sopenharmony_ci		cat_prev = cat;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return 0;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci/**
9308c2ecf20Sopenharmony_ci * cipso_v4_map_cat_enum_hton - Perform a category mapping from host to network
9318c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
9328c2ecf20Sopenharmony_ci * @secattr: the security attributes
9338c2ecf20Sopenharmony_ci * @net_cat: the zero'd out category list in network/CIPSO format
9348c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO category list in bytes
9358c2ecf20Sopenharmony_ci *
9368c2ecf20Sopenharmony_ci * Description:
9378c2ecf20Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
9388c2ecf20Sopenharmony_ci * correct CIPSO category list using the given DOI definition.   Returns the
9398c2ecf20Sopenharmony_ci * size in bytes of the network category bitmap on success, negative values
9408c2ecf20Sopenharmony_ci * otherwise.
9418c2ecf20Sopenharmony_ci *
9428c2ecf20Sopenharmony_ci */
9438c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def,
9448c2ecf20Sopenharmony_ci				      const struct netlbl_lsm_secattr *secattr,
9458c2ecf20Sopenharmony_ci				      unsigned char *net_cat,
9468c2ecf20Sopenharmony_ci				      u32 net_cat_len)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	int cat = -1;
9498c2ecf20Sopenharmony_ci	u32 cat_iter = 0;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	for (;;) {
9528c2ecf20Sopenharmony_ci		cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1);
9538c2ecf20Sopenharmony_ci		if (cat < 0)
9548c2ecf20Sopenharmony_ci			break;
9558c2ecf20Sopenharmony_ci		if ((cat_iter + 2) > net_cat_len)
9568c2ecf20Sopenharmony_ci			return -ENOSPC;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		*((__be16 *)&net_cat[cat_iter]) = htons(cat);
9598c2ecf20Sopenharmony_ci		cat_iter += 2;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	return cat_iter;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci/**
9668c2ecf20Sopenharmony_ci * cipso_v4_map_cat_enum_ntoh - Perform a category mapping from network to host
9678c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
9688c2ecf20Sopenharmony_ci * @net_cat: the category list in network/CIPSO format
9698c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
9708c2ecf20Sopenharmony_ci * @secattr: the security attributes
9718c2ecf20Sopenharmony_ci *
9728c2ecf20Sopenharmony_ci * Description:
9738c2ecf20Sopenharmony_ci * Perform a label mapping to translate a CIPSO category list to the correct
9748c2ecf20Sopenharmony_ci * local MLS category bitmap using the given DOI definition.  Returns zero on
9758c2ecf20Sopenharmony_ci * success, negative values on failure.
9768c2ecf20Sopenharmony_ci *
9778c2ecf20Sopenharmony_ci */
9788c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def,
9798c2ecf20Sopenharmony_ci				      const unsigned char *net_cat,
9808c2ecf20Sopenharmony_ci				      u32 net_cat_len,
9818c2ecf20Sopenharmony_ci				      struct netlbl_lsm_secattr *secattr)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	int ret_val;
9848c2ecf20Sopenharmony_ci	u32 iter;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	for (iter = 0; iter < net_cat_len; iter += 2) {
9878c2ecf20Sopenharmony_ci		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
9888c2ecf20Sopenharmony_ci					     get_unaligned_be16(&net_cat[iter]),
9898c2ecf20Sopenharmony_ci					     GFP_ATOMIC);
9908c2ecf20Sopenharmony_ci		if (ret_val != 0)
9918c2ecf20Sopenharmony_ci			return ret_val;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	return 0;
9958c2ecf20Sopenharmony_ci}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci/**
9988c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rng_valid - Checks to see if the categories are valid
9998c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
10008c2ecf20Sopenharmony_ci * @rngcat: category list
10018c2ecf20Sopenharmony_ci * @rngcat_len: length of the category list in bytes
10028c2ecf20Sopenharmony_ci *
10038c2ecf20Sopenharmony_ci * Description:
10048c2ecf20Sopenharmony_ci * Checks the given categories against the given DOI definition and returns a
10058c2ecf20Sopenharmony_ci * negative value if any of the categories do not have a valid mapping and a
10068c2ecf20Sopenharmony_ci * zero value if all of the categories are valid.
10078c2ecf20Sopenharmony_ci *
10088c2ecf20Sopenharmony_ci */
10098c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rng_valid(const struct cipso_v4_doi *doi_def,
10108c2ecf20Sopenharmony_ci				      const unsigned char *rngcat,
10118c2ecf20Sopenharmony_ci				      u32 rngcat_len)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	u16 cat_high;
10148c2ecf20Sopenharmony_ci	u16 cat_low;
10158c2ecf20Sopenharmony_ci	u32 cat_prev = CIPSO_V4_MAX_REM_CATS + 1;
10168c2ecf20Sopenharmony_ci	u32 iter;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (doi_def->type != CIPSO_V4_MAP_PASS || rngcat_len & 0x01)
10198c2ecf20Sopenharmony_ci		return -EFAULT;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	for (iter = 0; iter < rngcat_len; iter += 4) {
10228c2ecf20Sopenharmony_ci		cat_high = get_unaligned_be16(&rngcat[iter]);
10238c2ecf20Sopenharmony_ci		if ((iter + 4) <= rngcat_len)
10248c2ecf20Sopenharmony_ci			cat_low = get_unaligned_be16(&rngcat[iter + 2]);
10258c2ecf20Sopenharmony_ci		else
10268c2ecf20Sopenharmony_ci			cat_low = 0;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		if (cat_high > cat_prev)
10298c2ecf20Sopenharmony_ci			return -EFAULT;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci		cat_prev = cat_low;
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	return 0;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/**
10388c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rng_hton - Perform a category mapping from host to network
10398c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
10408c2ecf20Sopenharmony_ci * @secattr: the security attributes
10418c2ecf20Sopenharmony_ci * @net_cat: the zero'd out category list in network/CIPSO format
10428c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO category list in bytes
10438c2ecf20Sopenharmony_ci *
10448c2ecf20Sopenharmony_ci * Description:
10458c2ecf20Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
10468c2ecf20Sopenharmony_ci * correct CIPSO category list using the given DOI definition.   Returns the
10478c2ecf20Sopenharmony_ci * size in bytes of the network category bitmap on success, negative values
10488c2ecf20Sopenharmony_ci * otherwise.
10498c2ecf20Sopenharmony_ci *
10508c2ecf20Sopenharmony_ci */
10518c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
10528c2ecf20Sopenharmony_ci				     const struct netlbl_lsm_secattr *secattr,
10538c2ecf20Sopenharmony_ci				     unsigned char *net_cat,
10548c2ecf20Sopenharmony_ci				     u32 net_cat_len)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	int iter = -1;
10578c2ecf20Sopenharmony_ci	u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2];
10588c2ecf20Sopenharmony_ci	u32 array_cnt = 0;
10598c2ecf20Sopenharmony_ci	u32 cat_size = 0;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	/* make sure we don't overflow the 'array[]' variable */
10628c2ecf20Sopenharmony_ci	if (net_cat_len >
10638c2ecf20Sopenharmony_ci	    (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN))
10648c2ecf20Sopenharmony_ci		return -ENOSPC;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	for (;;) {
10678c2ecf20Sopenharmony_ci		iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1);
10688c2ecf20Sopenharmony_ci		if (iter < 0)
10698c2ecf20Sopenharmony_ci			break;
10708c2ecf20Sopenharmony_ci		cat_size += (iter == 0 ? 0 : sizeof(u16));
10718c2ecf20Sopenharmony_ci		if (cat_size > net_cat_len)
10728c2ecf20Sopenharmony_ci			return -ENOSPC;
10738c2ecf20Sopenharmony_ci		array[array_cnt++] = iter;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter);
10768c2ecf20Sopenharmony_ci		if (iter < 0)
10778c2ecf20Sopenharmony_ci			return -EFAULT;
10788c2ecf20Sopenharmony_ci		cat_size += sizeof(u16);
10798c2ecf20Sopenharmony_ci		if (cat_size > net_cat_len)
10808c2ecf20Sopenharmony_ci			return -ENOSPC;
10818c2ecf20Sopenharmony_ci		array[array_cnt++] = iter;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	for (iter = 0; array_cnt > 0;) {
10858c2ecf20Sopenharmony_ci		*((__be16 *)&net_cat[iter]) = htons(array[--array_cnt]);
10868c2ecf20Sopenharmony_ci		iter += 2;
10878c2ecf20Sopenharmony_ci		array_cnt--;
10888c2ecf20Sopenharmony_ci		if (array[array_cnt] != 0) {
10898c2ecf20Sopenharmony_ci			*((__be16 *)&net_cat[iter]) = htons(array[array_cnt]);
10908c2ecf20Sopenharmony_ci			iter += 2;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	return cat_size;
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci/**
10988c2ecf20Sopenharmony_ci * cipso_v4_map_cat_rng_ntoh - Perform a category mapping from network to host
10998c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
11008c2ecf20Sopenharmony_ci * @net_cat: the category list in network/CIPSO format
11018c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
11028c2ecf20Sopenharmony_ci * @secattr: the security attributes
11038c2ecf20Sopenharmony_ci *
11048c2ecf20Sopenharmony_ci * Description:
11058c2ecf20Sopenharmony_ci * Perform a label mapping to translate a CIPSO category list to the correct
11068c2ecf20Sopenharmony_ci * local MLS category bitmap using the given DOI definition.  Returns zero on
11078c2ecf20Sopenharmony_ci * success, negative values on failure.
11088c2ecf20Sopenharmony_ci *
11098c2ecf20Sopenharmony_ci */
11108c2ecf20Sopenharmony_cistatic int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def,
11118c2ecf20Sopenharmony_ci				     const unsigned char *net_cat,
11128c2ecf20Sopenharmony_ci				     u32 net_cat_len,
11138c2ecf20Sopenharmony_ci				     struct netlbl_lsm_secattr *secattr)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	int ret_val;
11168c2ecf20Sopenharmony_ci	u32 net_iter;
11178c2ecf20Sopenharmony_ci	u16 cat_low;
11188c2ecf20Sopenharmony_ci	u16 cat_high;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) {
11218c2ecf20Sopenharmony_ci		cat_high = get_unaligned_be16(&net_cat[net_iter]);
11228c2ecf20Sopenharmony_ci		if ((net_iter + 4) <= net_cat_len)
11238c2ecf20Sopenharmony_ci			cat_low = get_unaligned_be16(&net_cat[net_iter + 2]);
11248c2ecf20Sopenharmony_ci		else
11258c2ecf20Sopenharmony_ci			cat_low = 0;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat,
11288c2ecf20Sopenharmony_ci					       cat_low,
11298c2ecf20Sopenharmony_ci					       cat_high,
11308c2ecf20Sopenharmony_ci					       GFP_ATOMIC);
11318c2ecf20Sopenharmony_ci		if (ret_val != 0)
11328c2ecf20Sopenharmony_ci			return ret_val;
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	return 0;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci/*
11398c2ecf20Sopenharmony_ci * Protocol Handling Functions
11408c2ecf20Sopenharmony_ci */
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci/**
11438c2ecf20Sopenharmony_ci * cipso_v4_gentag_hdr - Generate a CIPSO option header
11448c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
11458c2ecf20Sopenharmony_ci * @len: the total tag length in bytes, not including this header
11468c2ecf20Sopenharmony_ci * @buf: the CIPSO option buffer
11478c2ecf20Sopenharmony_ci *
11488c2ecf20Sopenharmony_ci * Description:
11498c2ecf20Sopenharmony_ci * Write a CIPSO header into the beginning of @buffer.
11508c2ecf20Sopenharmony_ci *
11518c2ecf20Sopenharmony_ci */
11528c2ecf20Sopenharmony_cistatic void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def,
11538c2ecf20Sopenharmony_ci				unsigned char *buf,
11548c2ecf20Sopenharmony_ci				u32 len)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	buf[0] = IPOPT_CIPSO;
11578c2ecf20Sopenharmony_ci	buf[1] = CIPSO_V4_HDR_LEN + len;
11588c2ecf20Sopenharmony_ci	*(__be32 *)&buf[2] = htonl(doi_def->doi);
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci/**
11628c2ecf20Sopenharmony_ci * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1)
11638c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
11648c2ecf20Sopenharmony_ci * @secattr: the security attributes
11658c2ecf20Sopenharmony_ci * @buffer: the option buffer
11668c2ecf20Sopenharmony_ci * @buffer_len: length of buffer in bytes
11678c2ecf20Sopenharmony_ci *
11688c2ecf20Sopenharmony_ci * Description:
11698c2ecf20Sopenharmony_ci * Generate a CIPSO option using the restricted bitmap tag, tag type #1.  The
11708c2ecf20Sopenharmony_ci * actual buffer length may be larger than the indicated size due to
11718c2ecf20Sopenharmony_ci * translation between host and network category bitmaps.  Returns the size of
11728c2ecf20Sopenharmony_ci * the tag on success, negative values on failure.
11738c2ecf20Sopenharmony_ci *
11748c2ecf20Sopenharmony_ci */
11758c2ecf20Sopenharmony_cistatic int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
11768c2ecf20Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
11778c2ecf20Sopenharmony_ci			       unsigned char *buffer,
11788c2ecf20Sopenharmony_ci			       u32 buffer_len)
11798c2ecf20Sopenharmony_ci{
11808c2ecf20Sopenharmony_ci	int ret_val;
11818c2ecf20Sopenharmony_ci	u32 tag_len;
11828c2ecf20Sopenharmony_ci	u32 level;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
11858c2ecf20Sopenharmony_ci		return -EPERM;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
11888c2ecf20Sopenharmony_ci					secattr->attr.mls.lvl,
11898c2ecf20Sopenharmony_ci					&level);
11908c2ecf20Sopenharmony_ci	if (ret_val != 0)
11918c2ecf20Sopenharmony_ci		return ret_val;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
11948c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_rbm_hton(doi_def,
11958c2ecf20Sopenharmony_ci						    secattr,
11968c2ecf20Sopenharmony_ci						    &buffer[4],
11978c2ecf20Sopenharmony_ci						    buffer_len - 4);
11988c2ecf20Sopenharmony_ci		if (ret_val < 0)
11998c2ecf20Sopenharmony_ci			return ret_val;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci		/* This will send packets using the "optimized" format when
12028c2ecf20Sopenharmony_ci		 * possible as specified in  section 3.4.2.6 of the
12038c2ecf20Sopenharmony_ci		 * CIPSO draft. */
12048c2ecf20Sopenharmony_ci		if (READ_ONCE(cipso_v4_rbm_optfmt) && ret_val > 0 &&
12058c2ecf20Sopenharmony_ci		    ret_val <= 10)
12068c2ecf20Sopenharmony_ci			tag_len = 14;
12078c2ecf20Sopenharmony_ci		else
12088c2ecf20Sopenharmony_ci			tag_len = 4 + ret_val;
12098c2ecf20Sopenharmony_ci	} else
12108c2ecf20Sopenharmony_ci		tag_len = 4;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_RBITMAP;
12138c2ecf20Sopenharmony_ci	buffer[1] = tag_len;
12148c2ecf20Sopenharmony_ci	buffer[3] = level;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	return tag_len;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci/**
12208c2ecf20Sopenharmony_ci * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag
12218c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
12228c2ecf20Sopenharmony_ci * @tag: the CIPSO tag
12238c2ecf20Sopenharmony_ci * @secattr: the security attributes
12248c2ecf20Sopenharmony_ci *
12258c2ecf20Sopenharmony_ci * Description:
12268c2ecf20Sopenharmony_ci * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security
12278c2ecf20Sopenharmony_ci * attributes in @secattr.  Return zero on success, negatives values on
12288c2ecf20Sopenharmony_ci * failure.
12298c2ecf20Sopenharmony_ci *
12308c2ecf20Sopenharmony_ci */
12318c2ecf20Sopenharmony_cistatic int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
12328c2ecf20Sopenharmony_ci				 const unsigned char *tag,
12338c2ecf20Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	int ret_val;
12368c2ecf20Sopenharmony_ci	u8 tag_len = tag[1];
12378c2ecf20Sopenharmony_ci	u32 level;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
12408c2ecf20Sopenharmony_ci	if (ret_val != 0)
12418c2ecf20Sopenharmony_ci		return ret_val;
12428c2ecf20Sopenharmony_ci	secattr->attr.mls.lvl = level;
12438c2ecf20Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	if (tag_len > 4) {
12468c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
12478c2ecf20Sopenharmony_ci						    &tag[4],
12488c2ecf20Sopenharmony_ci						    tag_len - 4,
12498c2ecf20Sopenharmony_ci						    secattr);
12508c2ecf20Sopenharmony_ci		if (ret_val != 0) {
12518c2ecf20Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
12528c2ecf20Sopenharmony_ci			return ret_val;
12538c2ecf20Sopenharmony_ci		}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		if (secattr->attr.mls.cat)
12568c2ecf20Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	return 0;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci/**
12638c2ecf20Sopenharmony_ci * cipso_v4_gentag_enum - Generate a CIPSO enumerated tag (type #2)
12648c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
12658c2ecf20Sopenharmony_ci * @secattr: the security attributes
12668c2ecf20Sopenharmony_ci * @buffer: the option buffer
12678c2ecf20Sopenharmony_ci * @buffer_len: length of buffer in bytes
12688c2ecf20Sopenharmony_ci *
12698c2ecf20Sopenharmony_ci * Description:
12708c2ecf20Sopenharmony_ci * Generate a CIPSO option using the enumerated tag, tag type #2.  Returns the
12718c2ecf20Sopenharmony_ci * size of the tag on success, negative values on failure.
12728c2ecf20Sopenharmony_ci *
12738c2ecf20Sopenharmony_ci */
12748c2ecf20Sopenharmony_cistatic int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
12758c2ecf20Sopenharmony_ci				const struct netlbl_lsm_secattr *secattr,
12768c2ecf20Sopenharmony_ci				unsigned char *buffer,
12778c2ecf20Sopenharmony_ci				u32 buffer_len)
12788c2ecf20Sopenharmony_ci{
12798c2ecf20Sopenharmony_ci	int ret_val;
12808c2ecf20Sopenharmony_ci	u32 tag_len;
12818c2ecf20Sopenharmony_ci	u32 level;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
12848c2ecf20Sopenharmony_ci		return -EPERM;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
12878c2ecf20Sopenharmony_ci					secattr->attr.mls.lvl,
12888c2ecf20Sopenharmony_ci					&level);
12898c2ecf20Sopenharmony_ci	if (ret_val != 0)
12908c2ecf20Sopenharmony_ci		return ret_val;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
12938c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_enum_hton(doi_def,
12948c2ecf20Sopenharmony_ci						     secattr,
12958c2ecf20Sopenharmony_ci						     &buffer[4],
12968c2ecf20Sopenharmony_ci						     buffer_len - 4);
12978c2ecf20Sopenharmony_ci		if (ret_val < 0)
12988c2ecf20Sopenharmony_ci			return ret_val;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		tag_len = 4 + ret_val;
13018c2ecf20Sopenharmony_ci	} else
13028c2ecf20Sopenharmony_ci		tag_len = 4;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_ENUM;
13058c2ecf20Sopenharmony_ci	buffer[1] = tag_len;
13068c2ecf20Sopenharmony_ci	buffer[3] = level;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	return tag_len;
13098c2ecf20Sopenharmony_ci}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci/**
13128c2ecf20Sopenharmony_ci * cipso_v4_parsetag_enum - Parse a CIPSO enumerated tag
13138c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
13148c2ecf20Sopenharmony_ci * @tag: the CIPSO tag
13158c2ecf20Sopenharmony_ci * @secattr: the security attributes
13168c2ecf20Sopenharmony_ci *
13178c2ecf20Sopenharmony_ci * Description:
13188c2ecf20Sopenharmony_ci * Parse a CIPSO enumerated tag (tag type #2) and return the security
13198c2ecf20Sopenharmony_ci * attributes in @secattr.  Return zero on success, negatives values on
13208c2ecf20Sopenharmony_ci * failure.
13218c2ecf20Sopenharmony_ci *
13228c2ecf20Sopenharmony_ci */
13238c2ecf20Sopenharmony_cistatic int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
13248c2ecf20Sopenharmony_ci				  const unsigned char *tag,
13258c2ecf20Sopenharmony_ci				  struct netlbl_lsm_secattr *secattr)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	int ret_val;
13288c2ecf20Sopenharmony_ci	u8 tag_len = tag[1];
13298c2ecf20Sopenharmony_ci	u32 level;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
13328c2ecf20Sopenharmony_ci	if (ret_val != 0)
13338c2ecf20Sopenharmony_ci		return ret_val;
13348c2ecf20Sopenharmony_ci	secattr->attr.mls.lvl = level;
13358c2ecf20Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	if (tag_len > 4) {
13388c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_enum_ntoh(doi_def,
13398c2ecf20Sopenharmony_ci						     &tag[4],
13408c2ecf20Sopenharmony_ci						     tag_len - 4,
13418c2ecf20Sopenharmony_ci						     secattr);
13428c2ecf20Sopenharmony_ci		if (ret_val != 0) {
13438c2ecf20Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
13448c2ecf20Sopenharmony_ci			return ret_val;
13458c2ecf20Sopenharmony_ci		}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	return 0;
13518c2ecf20Sopenharmony_ci}
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci/**
13548c2ecf20Sopenharmony_ci * cipso_v4_gentag_rng - Generate a CIPSO ranged tag (type #5)
13558c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
13568c2ecf20Sopenharmony_ci * @secattr: the security attributes
13578c2ecf20Sopenharmony_ci * @buffer: the option buffer
13588c2ecf20Sopenharmony_ci * @buffer_len: length of buffer in bytes
13598c2ecf20Sopenharmony_ci *
13608c2ecf20Sopenharmony_ci * Description:
13618c2ecf20Sopenharmony_ci * Generate a CIPSO option using the ranged tag, tag type #5.  Returns the
13628c2ecf20Sopenharmony_ci * size of the tag on success, negative values on failure.
13638c2ecf20Sopenharmony_ci *
13648c2ecf20Sopenharmony_ci */
13658c2ecf20Sopenharmony_cistatic int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
13668c2ecf20Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
13678c2ecf20Sopenharmony_ci			       unsigned char *buffer,
13688c2ecf20Sopenharmony_ci			       u32 buffer_len)
13698c2ecf20Sopenharmony_ci{
13708c2ecf20Sopenharmony_ci	int ret_val;
13718c2ecf20Sopenharmony_ci	u32 tag_len;
13728c2ecf20Sopenharmony_ci	u32 level;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
13758c2ecf20Sopenharmony_ci		return -EPERM;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
13788c2ecf20Sopenharmony_ci					secattr->attr.mls.lvl,
13798c2ecf20Sopenharmony_ci					&level);
13808c2ecf20Sopenharmony_ci	if (ret_val != 0)
13818c2ecf20Sopenharmony_ci		return ret_val;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
13848c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_rng_hton(doi_def,
13858c2ecf20Sopenharmony_ci						    secattr,
13868c2ecf20Sopenharmony_ci						    &buffer[4],
13878c2ecf20Sopenharmony_ci						    buffer_len - 4);
13888c2ecf20Sopenharmony_ci		if (ret_val < 0)
13898c2ecf20Sopenharmony_ci			return ret_val;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci		tag_len = 4 + ret_val;
13928c2ecf20Sopenharmony_ci	} else
13938c2ecf20Sopenharmony_ci		tag_len = 4;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_RANGE;
13968c2ecf20Sopenharmony_ci	buffer[1] = tag_len;
13978c2ecf20Sopenharmony_ci	buffer[3] = level;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	return tag_len;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci/**
14038c2ecf20Sopenharmony_ci * cipso_v4_parsetag_rng - Parse a CIPSO ranged tag
14048c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
14058c2ecf20Sopenharmony_ci * @tag: the CIPSO tag
14068c2ecf20Sopenharmony_ci * @secattr: the security attributes
14078c2ecf20Sopenharmony_ci *
14088c2ecf20Sopenharmony_ci * Description:
14098c2ecf20Sopenharmony_ci * Parse a CIPSO ranged tag (tag type #5) and return the security attributes
14108c2ecf20Sopenharmony_ci * in @secattr.  Return zero on success, negatives values on failure.
14118c2ecf20Sopenharmony_ci *
14128c2ecf20Sopenharmony_ci */
14138c2ecf20Sopenharmony_cistatic int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
14148c2ecf20Sopenharmony_ci				 const unsigned char *tag,
14158c2ecf20Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	int ret_val;
14188c2ecf20Sopenharmony_ci	u8 tag_len = tag[1];
14198c2ecf20Sopenharmony_ci	u32 level;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
14228c2ecf20Sopenharmony_ci	if (ret_val != 0)
14238c2ecf20Sopenharmony_ci		return ret_val;
14248c2ecf20Sopenharmony_ci	secattr->attr.mls.lvl = level;
14258c2ecf20Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	if (tag_len > 4) {
14288c2ecf20Sopenharmony_ci		ret_val = cipso_v4_map_cat_rng_ntoh(doi_def,
14298c2ecf20Sopenharmony_ci						    &tag[4],
14308c2ecf20Sopenharmony_ci						    tag_len - 4,
14318c2ecf20Sopenharmony_ci						    secattr);
14328c2ecf20Sopenharmony_ci		if (ret_val != 0) {
14338c2ecf20Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
14348c2ecf20Sopenharmony_ci			return ret_val;
14358c2ecf20Sopenharmony_ci		}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		if (secattr->attr.mls.cat)
14388c2ecf20Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
14398c2ecf20Sopenharmony_ci	}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	return 0;
14428c2ecf20Sopenharmony_ci}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci/**
14458c2ecf20Sopenharmony_ci * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard)
14468c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
14478c2ecf20Sopenharmony_ci * @secattr: the security attributes
14488c2ecf20Sopenharmony_ci * @buffer: the option buffer
14498c2ecf20Sopenharmony_ci * @buffer_len: length of buffer in bytes
14508c2ecf20Sopenharmony_ci *
14518c2ecf20Sopenharmony_ci * Description:
14528c2ecf20Sopenharmony_ci * Generate a CIPSO option using the local tag.  Returns the size of the tag
14538c2ecf20Sopenharmony_ci * on success, negative values on failure.
14548c2ecf20Sopenharmony_ci *
14558c2ecf20Sopenharmony_ci */
14568c2ecf20Sopenharmony_cistatic int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def,
14578c2ecf20Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
14588c2ecf20Sopenharmony_ci			       unsigned char *buffer,
14598c2ecf20Sopenharmony_ci			       u32 buffer_len)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_SECID))
14628c2ecf20Sopenharmony_ci		return -EPERM;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_LOCAL;
14658c2ecf20Sopenharmony_ci	buffer[1] = CIPSO_V4_TAG_LOC_BLEN;
14668c2ecf20Sopenharmony_ci	*(u32 *)&buffer[2] = secattr->attr.secid;
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	return CIPSO_V4_TAG_LOC_BLEN;
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci/**
14728c2ecf20Sopenharmony_ci * cipso_v4_parsetag_loc - Parse a CIPSO local tag
14738c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
14748c2ecf20Sopenharmony_ci * @tag: the CIPSO tag
14758c2ecf20Sopenharmony_ci * @secattr: the security attributes
14768c2ecf20Sopenharmony_ci *
14778c2ecf20Sopenharmony_ci * Description:
14788c2ecf20Sopenharmony_ci * Parse a CIPSO local tag and return the security attributes in @secattr.
14798c2ecf20Sopenharmony_ci * Return zero on success, negatives values on failure.
14808c2ecf20Sopenharmony_ci *
14818c2ecf20Sopenharmony_ci */
14828c2ecf20Sopenharmony_cistatic int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def,
14838c2ecf20Sopenharmony_ci				 const unsigned char *tag,
14848c2ecf20Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
14858c2ecf20Sopenharmony_ci{
14868c2ecf20Sopenharmony_ci	secattr->attr.secid = *(u32 *)&tag[2];
14878c2ecf20Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_SECID;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	return 0;
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci/**
14938c2ecf20Sopenharmony_ci * cipso_v4_optptr - Find the CIPSO option in the packet
14948c2ecf20Sopenharmony_ci * @skb: the packet
14958c2ecf20Sopenharmony_ci *
14968c2ecf20Sopenharmony_ci * Description:
14978c2ecf20Sopenharmony_ci * Parse the packet's IP header looking for a CIPSO option.  Returns a pointer
14988c2ecf20Sopenharmony_ci * to the start of the CIPSO option on success, NULL if one is not found.
14998c2ecf20Sopenharmony_ci *
15008c2ecf20Sopenharmony_ci */
15018c2ecf20Sopenharmony_ciunsigned char *cipso_v4_optptr(const struct sk_buff *skb)
15028c2ecf20Sopenharmony_ci{
15038c2ecf20Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
15048c2ecf20Sopenharmony_ci	unsigned char *optptr = (unsigned char *)&(ip_hdr(skb)[1]);
15058c2ecf20Sopenharmony_ci	int optlen;
15068c2ecf20Sopenharmony_ci	int taglen;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 1; ) {
15098c2ecf20Sopenharmony_ci		switch (optptr[0]) {
15108c2ecf20Sopenharmony_ci		case IPOPT_END:
15118c2ecf20Sopenharmony_ci			return NULL;
15128c2ecf20Sopenharmony_ci		case IPOPT_NOOP:
15138c2ecf20Sopenharmony_ci			taglen = 1;
15148c2ecf20Sopenharmony_ci			break;
15158c2ecf20Sopenharmony_ci		default:
15168c2ecf20Sopenharmony_ci			taglen = optptr[1];
15178c2ecf20Sopenharmony_ci		}
15188c2ecf20Sopenharmony_ci		if (!taglen || taglen > optlen)
15198c2ecf20Sopenharmony_ci			return NULL;
15208c2ecf20Sopenharmony_ci		if (optptr[0] == IPOPT_CIPSO)
15218c2ecf20Sopenharmony_ci			return optptr;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci		optlen -= taglen;
15248c2ecf20Sopenharmony_ci		optptr += taglen;
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	return NULL;
15288c2ecf20Sopenharmony_ci}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci/**
15318c2ecf20Sopenharmony_ci * cipso_v4_validate - Validate a CIPSO option
15328c2ecf20Sopenharmony_ci * @skb: the packet
15338c2ecf20Sopenharmony_ci * @option: the start of the option, on error it is set to point to the error
15348c2ecf20Sopenharmony_ci *
15358c2ecf20Sopenharmony_ci * Description:
15368c2ecf20Sopenharmony_ci * This routine is called to validate a CIPSO option, it checks all of the
15378c2ecf20Sopenharmony_ci * fields to ensure that they are at least valid, see the draft snippet below
15388c2ecf20Sopenharmony_ci * for details.  If the option is valid then a zero value is returned and
15398c2ecf20Sopenharmony_ci * the value of @option is unchanged.  If the option is invalid then a
15408c2ecf20Sopenharmony_ci * non-zero value is returned and @option is adjusted to point to the
15418c2ecf20Sopenharmony_ci * offending portion of the option.  From the IETF draft ...
15428c2ecf20Sopenharmony_ci *
15438c2ecf20Sopenharmony_ci *  "If any field within the CIPSO options, such as the DOI identifier, is not
15448c2ecf20Sopenharmony_ci *   recognized the IP datagram is discarded and an ICMP 'parameter problem'
15458c2ecf20Sopenharmony_ci *   (type 12) is generated and returned.  The ICMP code field is set to 'bad
15468c2ecf20Sopenharmony_ci *   parameter' (code 0) and the pointer is set to the start of the CIPSO field
15478c2ecf20Sopenharmony_ci *   that is unrecognized."
15488c2ecf20Sopenharmony_ci *
15498c2ecf20Sopenharmony_ci */
15508c2ecf20Sopenharmony_ciint cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	unsigned char *opt = *option;
15538c2ecf20Sopenharmony_ci	unsigned char *tag;
15548c2ecf20Sopenharmony_ci	unsigned char opt_iter;
15558c2ecf20Sopenharmony_ci	unsigned char err_offset = 0;
15568c2ecf20Sopenharmony_ci	u8 opt_len;
15578c2ecf20Sopenharmony_ci	u8 tag_len;
15588c2ecf20Sopenharmony_ci	struct cipso_v4_doi *doi_def = NULL;
15598c2ecf20Sopenharmony_ci	u32 tag_iter;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	/* caller already checks for length values that are too large */
15628c2ecf20Sopenharmony_ci	opt_len = opt[1];
15638c2ecf20Sopenharmony_ci	if (opt_len < 8) {
15648c2ecf20Sopenharmony_ci		err_offset = 1;
15658c2ecf20Sopenharmony_ci		goto validate_return;
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	rcu_read_lock();
15698c2ecf20Sopenharmony_ci	doi_def = cipso_v4_doi_search(get_unaligned_be32(&opt[2]));
15708c2ecf20Sopenharmony_ci	if (!doi_def) {
15718c2ecf20Sopenharmony_ci		err_offset = 2;
15728c2ecf20Sopenharmony_ci		goto validate_return_locked;
15738c2ecf20Sopenharmony_ci	}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	opt_iter = CIPSO_V4_HDR_LEN;
15768c2ecf20Sopenharmony_ci	tag = opt + opt_iter;
15778c2ecf20Sopenharmony_ci	while (opt_iter < opt_len) {
15788c2ecf20Sopenharmony_ci		for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
15798c2ecf20Sopenharmony_ci			if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID ||
15808c2ecf20Sopenharmony_ci			    ++tag_iter == CIPSO_V4_TAG_MAXCNT) {
15818c2ecf20Sopenharmony_ci				err_offset = opt_iter;
15828c2ecf20Sopenharmony_ci				goto validate_return_locked;
15838c2ecf20Sopenharmony_ci			}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci		if (opt_iter + 1 == opt_len) {
15868c2ecf20Sopenharmony_ci			err_offset = opt_iter;
15878c2ecf20Sopenharmony_ci			goto validate_return_locked;
15888c2ecf20Sopenharmony_ci		}
15898c2ecf20Sopenharmony_ci		tag_len = tag[1];
15908c2ecf20Sopenharmony_ci		if (tag_len > (opt_len - opt_iter)) {
15918c2ecf20Sopenharmony_ci			err_offset = opt_iter + 1;
15928c2ecf20Sopenharmony_ci			goto validate_return_locked;
15938c2ecf20Sopenharmony_ci		}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		switch (tag[0]) {
15968c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
15978c2ecf20Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_RBM_BLEN) {
15988c2ecf20Sopenharmony_ci				err_offset = opt_iter + 1;
15998c2ecf20Sopenharmony_ci				goto validate_return_locked;
16008c2ecf20Sopenharmony_ci			}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci			/* We are already going to do all the verification
16038c2ecf20Sopenharmony_ci			 * necessary at the socket layer so from our point of
16048c2ecf20Sopenharmony_ci			 * view it is safe to turn these checks off (and less
16058c2ecf20Sopenharmony_ci			 * work), however, the CIPSO draft says we should do
16068c2ecf20Sopenharmony_ci			 * all the CIPSO validations here but it doesn't
16078c2ecf20Sopenharmony_ci			 * really specify _exactly_ what we need to validate
16088c2ecf20Sopenharmony_ci			 * ... so, just make it a sysctl tunable. */
16098c2ecf20Sopenharmony_ci			if (READ_ONCE(cipso_v4_rbm_strictvalid)) {
16108c2ecf20Sopenharmony_ci				if (cipso_v4_map_lvl_valid(doi_def,
16118c2ecf20Sopenharmony_ci							   tag[3]) < 0) {
16128c2ecf20Sopenharmony_ci					err_offset = opt_iter + 3;
16138c2ecf20Sopenharmony_ci					goto validate_return_locked;
16148c2ecf20Sopenharmony_ci				}
16158c2ecf20Sopenharmony_ci				if (tag_len > CIPSO_V4_TAG_RBM_BLEN &&
16168c2ecf20Sopenharmony_ci				    cipso_v4_map_cat_rbm_valid(doi_def,
16178c2ecf20Sopenharmony_ci							    &tag[4],
16188c2ecf20Sopenharmony_ci							    tag_len - 4) < 0) {
16198c2ecf20Sopenharmony_ci					err_offset = opt_iter + 4;
16208c2ecf20Sopenharmony_ci					goto validate_return_locked;
16218c2ecf20Sopenharmony_ci				}
16228c2ecf20Sopenharmony_ci			}
16238c2ecf20Sopenharmony_ci			break;
16248c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
16258c2ecf20Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) {
16268c2ecf20Sopenharmony_ci				err_offset = opt_iter + 1;
16278c2ecf20Sopenharmony_ci				goto validate_return_locked;
16288c2ecf20Sopenharmony_ci			}
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci			if (cipso_v4_map_lvl_valid(doi_def,
16318c2ecf20Sopenharmony_ci						   tag[3]) < 0) {
16328c2ecf20Sopenharmony_ci				err_offset = opt_iter + 3;
16338c2ecf20Sopenharmony_ci				goto validate_return_locked;
16348c2ecf20Sopenharmony_ci			}
16358c2ecf20Sopenharmony_ci			if (tag_len > CIPSO_V4_TAG_ENUM_BLEN &&
16368c2ecf20Sopenharmony_ci			    cipso_v4_map_cat_enum_valid(doi_def,
16378c2ecf20Sopenharmony_ci							&tag[4],
16388c2ecf20Sopenharmony_ci							tag_len - 4) < 0) {
16398c2ecf20Sopenharmony_ci				err_offset = opt_iter + 4;
16408c2ecf20Sopenharmony_ci				goto validate_return_locked;
16418c2ecf20Sopenharmony_ci			}
16428c2ecf20Sopenharmony_ci			break;
16438c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
16448c2ecf20Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_RNG_BLEN) {
16458c2ecf20Sopenharmony_ci				err_offset = opt_iter + 1;
16468c2ecf20Sopenharmony_ci				goto validate_return_locked;
16478c2ecf20Sopenharmony_ci			}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci			if (cipso_v4_map_lvl_valid(doi_def,
16508c2ecf20Sopenharmony_ci						   tag[3]) < 0) {
16518c2ecf20Sopenharmony_ci				err_offset = opt_iter + 3;
16528c2ecf20Sopenharmony_ci				goto validate_return_locked;
16538c2ecf20Sopenharmony_ci			}
16548c2ecf20Sopenharmony_ci			if (tag_len > CIPSO_V4_TAG_RNG_BLEN &&
16558c2ecf20Sopenharmony_ci			    cipso_v4_map_cat_rng_valid(doi_def,
16568c2ecf20Sopenharmony_ci						       &tag[4],
16578c2ecf20Sopenharmony_ci						       tag_len - 4) < 0) {
16588c2ecf20Sopenharmony_ci				err_offset = opt_iter + 4;
16598c2ecf20Sopenharmony_ci				goto validate_return_locked;
16608c2ecf20Sopenharmony_ci			}
16618c2ecf20Sopenharmony_ci			break;
16628c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
16638c2ecf20Sopenharmony_ci			/* This is a non-standard tag that we only allow for
16648c2ecf20Sopenharmony_ci			 * local connections, so if the incoming interface is
16658c2ecf20Sopenharmony_ci			 * not the loopback device drop the packet. Further,
16668c2ecf20Sopenharmony_ci			 * there is no legitimate reason for setting this from
16678c2ecf20Sopenharmony_ci			 * userspace so reject it if skb is NULL. */
16688c2ecf20Sopenharmony_ci			if (!skb || !(skb->dev->flags & IFF_LOOPBACK)) {
16698c2ecf20Sopenharmony_ci				err_offset = opt_iter;
16708c2ecf20Sopenharmony_ci				goto validate_return_locked;
16718c2ecf20Sopenharmony_ci			}
16728c2ecf20Sopenharmony_ci			if (tag_len != CIPSO_V4_TAG_LOC_BLEN) {
16738c2ecf20Sopenharmony_ci				err_offset = opt_iter + 1;
16748c2ecf20Sopenharmony_ci				goto validate_return_locked;
16758c2ecf20Sopenharmony_ci			}
16768c2ecf20Sopenharmony_ci			break;
16778c2ecf20Sopenharmony_ci		default:
16788c2ecf20Sopenharmony_ci			err_offset = opt_iter;
16798c2ecf20Sopenharmony_ci			goto validate_return_locked;
16808c2ecf20Sopenharmony_ci		}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci		tag += tag_len;
16838c2ecf20Sopenharmony_ci		opt_iter += tag_len;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_civalidate_return_locked:
16878c2ecf20Sopenharmony_ci	rcu_read_unlock();
16888c2ecf20Sopenharmony_civalidate_return:
16898c2ecf20Sopenharmony_ci	*option = opt + err_offset;
16908c2ecf20Sopenharmony_ci	return err_offset;
16918c2ecf20Sopenharmony_ci}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci/**
16948c2ecf20Sopenharmony_ci * cipso_v4_error - Send the correct response for a bad packet
16958c2ecf20Sopenharmony_ci * @skb: the packet
16968c2ecf20Sopenharmony_ci * @error: the error code
16978c2ecf20Sopenharmony_ci * @gateway: CIPSO gateway flag
16988c2ecf20Sopenharmony_ci *
16998c2ecf20Sopenharmony_ci * Description:
17008c2ecf20Sopenharmony_ci * Based on the error code given in @error, send an ICMP error message back to
17018c2ecf20Sopenharmony_ci * the originating host.  From the IETF draft ...
17028c2ecf20Sopenharmony_ci *
17038c2ecf20Sopenharmony_ci *  "If the contents of the CIPSO [option] are valid but the security label is
17048c2ecf20Sopenharmony_ci *   outside of the configured host or port label range, the datagram is
17058c2ecf20Sopenharmony_ci *   discarded and an ICMP 'destination unreachable' (type 3) is generated and
17068c2ecf20Sopenharmony_ci *   returned.  The code field of the ICMP is set to 'communication with
17078c2ecf20Sopenharmony_ci *   destination network administratively prohibited' (code 9) or to
17088c2ecf20Sopenharmony_ci *   'communication with destination host administratively prohibited'
17098c2ecf20Sopenharmony_ci *   (code 10).  The value of the code is dependent on whether the originator
17108c2ecf20Sopenharmony_ci *   of the ICMP message is acting as a CIPSO host or a CIPSO gateway.  The
17118c2ecf20Sopenharmony_ci *   recipient of the ICMP message MUST be able to handle either value.  The
17128c2ecf20Sopenharmony_ci *   same procedure is performed if a CIPSO [option] can not be added to an
17138c2ecf20Sopenharmony_ci *   IP packet because it is too large to fit in the IP options area."
17148c2ecf20Sopenharmony_ci *
17158c2ecf20Sopenharmony_ci *  "If the error is triggered by receipt of an ICMP message, the message is
17168c2ecf20Sopenharmony_ci *   discarded and no response is permitted (consistent with general ICMP
17178c2ecf20Sopenharmony_ci *   processing rules)."
17188c2ecf20Sopenharmony_ci *
17198c2ecf20Sopenharmony_ci */
17208c2ecf20Sopenharmony_civoid cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	unsigned char optbuf[sizeof(struct ip_options) + 40];
17238c2ecf20Sopenharmony_ci	struct ip_options *opt = (struct ip_options *)optbuf;
17248c2ecf20Sopenharmony_ci	int res;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
17278c2ecf20Sopenharmony_ci		return;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	/*
17308c2ecf20Sopenharmony_ci	 * We might be called above the IP layer,
17318c2ecf20Sopenharmony_ci	 * so we can not use icmp_send and IPCB here.
17328c2ecf20Sopenharmony_ci	 */
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	memset(opt, 0, sizeof(struct ip_options));
17358c2ecf20Sopenharmony_ci	opt->optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
17368c2ecf20Sopenharmony_ci	rcu_read_lock();
17378c2ecf20Sopenharmony_ci	res = __ip_options_compile(dev_net(skb->dev), opt, skb, NULL);
17388c2ecf20Sopenharmony_ci	rcu_read_unlock();
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	if (res)
17418c2ecf20Sopenharmony_ci		return;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (gateway)
17448c2ecf20Sopenharmony_ci		__icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0, opt);
17458c2ecf20Sopenharmony_ci	else
17468c2ecf20Sopenharmony_ci		__icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0, opt);
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci/**
17508c2ecf20Sopenharmony_ci * cipso_v4_genopt - Generate a CIPSO option
17518c2ecf20Sopenharmony_ci * @buf: the option buffer
17528c2ecf20Sopenharmony_ci * @buf_len: the size of opt_buf
17538c2ecf20Sopenharmony_ci * @doi_def: the CIPSO DOI to use
17548c2ecf20Sopenharmony_ci * @secattr: the security attributes
17558c2ecf20Sopenharmony_ci *
17568c2ecf20Sopenharmony_ci * Description:
17578c2ecf20Sopenharmony_ci * Generate a CIPSO option using the DOI definition and security attributes
17588c2ecf20Sopenharmony_ci * passed to the function.  Returns the length of the option on success and
17598c2ecf20Sopenharmony_ci * negative values on failure.
17608c2ecf20Sopenharmony_ci *
17618c2ecf20Sopenharmony_ci */
17628c2ecf20Sopenharmony_cistatic int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
17638c2ecf20Sopenharmony_ci			   const struct cipso_v4_doi *doi_def,
17648c2ecf20Sopenharmony_ci			   const struct netlbl_lsm_secattr *secattr)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci	int ret_val;
17678c2ecf20Sopenharmony_ci	u32 iter;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	if (buf_len <= CIPSO_V4_HDR_LEN)
17708c2ecf20Sopenharmony_ci		return -ENOSPC;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	/* XXX - This code assumes only one tag per CIPSO option which isn't
17738c2ecf20Sopenharmony_ci	 * really a good assumption to make but since we only support the MAC
17748c2ecf20Sopenharmony_ci	 * tags right now it is a safe assumption. */
17758c2ecf20Sopenharmony_ci	iter = 0;
17768c2ecf20Sopenharmony_ci	do {
17778c2ecf20Sopenharmony_ci		memset(buf, 0, buf_len);
17788c2ecf20Sopenharmony_ci		switch (doi_def->tags[iter]) {
17798c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
17808c2ecf20Sopenharmony_ci			ret_val = cipso_v4_gentag_rbm(doi_def,
17818c2ecf20Sopenharmony_ci						   secattr,
17828c2ecf20Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
17838c2ecf20Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
17848c2ecf20Sopenharmony_ci			break;
17858c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
17868c2ecf20Sopenharmony_ci			ret_val = cipso_v4_gentag_enum(doi_def,
17878c2ecf20Sopenharmony_ci						   secattr,
17888c2ecf20Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
17898c2ecf20Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
17908c2ecf20Sopenharmony_ci			break;
17918c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
17928c2ecf20Sopenharmony_ci			ret_val = cipso_v4_gentag_rng(doi_def,
17938c2ecf20Sopenharmony_ci						   secattr,
17948c2ecf20Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
17958c2ecf20Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
17968c2ecf20Sopenharmony_ci			break;
17978c2ecf20Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
17988c2ecf20Sopenharmony_ci			ret_val = cipso_v4_gentag_loc(doi_def,
17998c2ecf20Sopenharmony_ci						   secattr,
18008c2ecf20Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
18018c2ecf20Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
18028c2ecf20Sopenharmony_ci			break;
18038c2ecf20Sopenharmony_ci		default:
18048c2ecf20Sopenharmony_ci			return -EPERM;
18058c2ecf20Sopenharmony_ci		}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci		iter++;
18088c2ecf20Sopenharmony_ci	} while (ret_val < 0 &&
18098c2ecf20Sopenharmony_ci		 iter < CIPSO_V4_TAG_MAXCNT &&
18108c2ecf20Sopenharmony_ci		 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
18118c2ecf20Sopenharmony_ci	if (ret_val < 0)
18128c2ecf20Sopenharmony_ci		return ret_val;
18138c2ecf20Sopenharmony_ci	cipso_v4_gentag_hdr(doi_def, buf, ret_val);
18148c2ecf20Sopenharmony_ci	return CIPSO_V4_HDR_LEN + ret_val;
18158c2ecf20Sopenharmony_ci}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci/**
18188c2ecf20Sopenharmony_ci * cipso_v4_sock_setattr - Add a CIPSO option to a socket
18198c2ecf20Sopenharmony_ci * @sk: the socket
18208c2ecf20Sopenharmony_ci * @doi_def: the CIPSO DOI to use
18218c2ecf20Sopenharmony_ci * @secattr: the specific security attributes of the socket
18228c2ecf20Sopenharmony_ci *
18238c2ecf20Sopenharmony_ci * Description:
18248c2ecf20Sopenharmony_ci * Set the CIPSO option on the given socket using the DOI definition and
18258c2ecf20Sopenharmony_ci * security attributes passed to the function.  This function requires
18268c2ecf20Sopenharmony_ci * exclusive access to @sk, which means it either needs to be in the
18278c2ecf20Sopenharmony_ci * process of being created or locked.  Returns zero on success and negative
18288c2ecf20Sopenharmony_ci * values on failure.
18298c2ecf20Sopenharmony_ci *
18308c2ecf20Sopenharmony_ci */
18318c2ecf20Sopenharmony_ciint cipso_v4_sock_setattr(struct sock *sk,
18328c2ecf20Sopenharmony_ci			  const struct cipso_v4_doi *doi_def,
18338c2ecf20Sopenharmony_ci			  const struct netlbl_lsm_secattr *secattr)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	int ret_val = -EPERM;
18368c2ecf20Sopenharmony_ci	unsigned char *buf = NULL;
18378c2ecf20Sopenharmony_ci	u32 buf_len;
18388c2ecf20Sopenharmony_ci	u32 opt_len;
18398c2ecf20Sopenharmony_ci	struct ip_options_rcu *old, *opt = NULL;
18408c2ecf20Sopenharmony_ci	struct inet_sock *sk_inet;
18418c2ecf20Sopenharmony_ci	struct inet_connection_sock *sk_conn;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	/* In the case of sock_create_lite(), the sock->sk field is not
18448c2ecf20Sopenharmony_ci	 * defined yet but it is not a problem as the only users of these
18458c2ecf20Sopenharmony_ci	 * "lite" PF_INET sockets are functions which do an accept() call
18468c2ecf20Sopenharmony_ci	 * afterwards so we will label the socket as part of the accept(). */
18478c2ecf20Sopenharmony_ci	if (!sk)
18488c2ecf20Sopenharmony_ci		return 0;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	/* We allocate the maximum CIPSO option size here so we are probably
18518c2ecf20Sopenharmony_ci	 * being a little wasteful, but it makes our life _much_ easier later
18528c2ecf20Sopenharmony_ci	 * on and after all we are only talking about 40 bytes. */
18538c2ecf20Sopenharmony_ci	buf_len = CIPSO_V4_OPT_LEN_MAX;
18548c2ecf20Sopenharmony_ci	buf = kmalloc(buf_len, GFP_ATOMIC);
18558c2ecf20Sopenharmony_ci	if (!buf) {
18568c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
18578c2ecf20Sopenharmony_ci		goto socket_setattr_failure;
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
18618c2ecf20Sopenharmony_ci	if (ret_val < 0)
18628c2ecf20Sopenharmony_ci		goto socket_setattr_failure;
18638c2ecf20Sopenharmony_ci	buf_len = ret_val;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	/* We can't use ip_options_get() directly because it makes a call to
18668c2ecf20Sopenharmony_ci	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
18678c2ecf20Sopenharmony_ci	 * we won't always have CAP_NET_RAW even though we _always_ want to
18688c2ecf20Sopenharmony_ci	 * set the IPOPT_CIPSO option. */
18698c2ecf20Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
18708c2ecf20Sopenharmony_ci	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
18718c2ecf20Sopenharmony_ci	if (!opt) {
18728c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
18738c2ecf20Sopenharmony_ci		goto socket_setattr_failure;
18748c2ecf20Sopenharmony_ci	}
18758c2ecf20Sopenharmony_ci	memcpy(opt->opt.__data, buf, buf_len);
18768c2ecf20Sopenharmony_ci	opt->opt.optlen = opt_len;
18778c2ecf20Sopenharmony_ci	opt->opt.cipso = sizeof(struct iphdr);
18788c2ecf20Sopenharmony_ci	kfree(buf);
18798c2ecf20Sopenharmony_ci	buf = NULL;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	sk_inet = inet_sk(sk);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	old = rcu_dereference_protected(sk_inet->inet_opt,
18848c2ecf20Sopenharmony_ci					lockdep_sock_is_held(sk));
18858c2ecf20Sopenharmony_ci	if (sk_inet->is_icsk) {
18868c2ecf20Sopenharmony_ci		sk_conn = inet_csk(sk);
18878c2ecf20Sopenharmony_ci		if (old)
18888c2ecf20Sopenharmony_ci			sk_conn->icsk_ext_hdr_len -= old->opt.optlen;
18898c2ecf20Sopenharmony_ci		sk_conn->icsk_ext_hdr_len += opt->opt.optlen;
18908c2ecf20Sopenharmony_ci		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
18918c2ecf20Sopenharmony_ci	}
18928c2ecf20Sopenharmony_ci	rcu_assign_pointer(sk_inet->inet_opt, opt);
18938c2ecf20Sopenharmony_ci	if (old)
18948c2ecf20Sopenharmony_ci		kfree_rcu(old, rcu);
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci	return 0;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_cisocket_setattr_failure:
18998c2ecf20Sopenharmony_ci	kfree(buf);
19008c2ecf20Sopenharmony_ci	kfree(opt);
19018c2ecf20Sopenharmony_ci	return ret_val;
19028c2ecf20Sopenharmony_ci}
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci/**
19058c2ecf20Sopenharmony_ci * cipso_v4_req_setattr - Add a CIPSO option to a connection request socket
19068c2ecf20Sopenharmony_ci * @req: the connection request socket
19078c2ecf20Sopenharmony_ci * @doi_def: the CIPSO DOI to use
19088c2ecf20Sopenharmony_ci * @secattr: the specific security attributes of the socket
19098c2ecf20Sopenharmony_ci *
19108c2ecf20Sopenharmony_ci * Description:
19118c2ecf20Sopenharmony_ci * Set the CIPSO option on the given socket using the DOI definition and
19128c2ecf20Sopenharmony_ci * security attributes passed to the function.  Returns zero on success and
19138c2ecf20Sopenharmony_ci * negative values on failure.
19148c2ecf20Sopenharmony_ci *
19158c2ecf20Sopenharmony_ci */
19168c2ecf20Sopenharmony_ciint cipso_v4_req_setattr(struct request_sock *req,
19178c2ecf20Sopenharmony_ci			 const struct cipso_v4_doi *doi_def,
19188c2ecf20Sopenharmony_ci			 const struct netlbl_lsm_secattr *secattr)
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	int ret_val = -EPERM;
19218c2ecf20Sopenharmony_ci	unsigned char *buf = NULL;
19228c2ecf20Sopenharmony_ci	u32 buf_len;
19238c2ecf20Sopenharmony_ci	u32 opt_len;
19248c2ecf20Sopenharmony_ci	struct ip_options_rcu *opt = NULL;
19258c2ecf20Sopenharmony_ci	struct inet_request_sock *req_inet;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	/* We allocate the maximum CIPSO option size here so we are probably
19288c2ecf20Sopenharmony_ci	 * being a little wasteful, but it makes our life _much_ easier later
19298c2ecf20Sopenharmony_ci	 * on and after all we are only talking about 40 bytes. */
19308c2ecf20Sopenharmony_ci	buf_len = CIPSO_V4_OPT_LEN_MAX;
19318c2ecf20Sopenharmony_ci	buf = kmalloc(buf_len, GFP_ATOMIC);
19328c2ecf20Sopenharmony_ci	if (!buf) {
19338c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
19348c2ecf20Sopenharmony_ci		goto req_setattr_failure;
19358c2ecf20Sopenharmony_ci	}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
19388c2ecf20Sopenharmony_ci	if (ret_val < 0)
19398c2ecf20Sopenharmony_ci		goto req_setattr_failure;
19408c2ecf20Sopenharmony_ci	buf_len = ret_val;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	/* We can't use ip_options_get() directly because it makes a call to
19438c2ecf20Sopenharmony_ci	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
19448c2ecf20Sopenharmony_ci	 * we won't always have CAP_NET_RAW even though we _always_ want to
19458c2ecf20Sopenharmony_ci	 * set the IPOPT_CIPSO option. */
19468c2ecf20Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
19478c2ecf20Sopenharmony_ci	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
19488c2ecf20Sopenharmony_ci	if (!opt) {
19498c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
19508c2ecf20Sopenharmony_ci		goto req_setattr_failure;
19518c2ecf20Sopenharmony_ci	}
19528c2ecf20Sopenharmony_ci	memcpy(opt->opt.__data, buf, buf_len);
19538c2ecf20Sopenharmony_ci	opt->opt.optlen = opt_len;
19548c2ecf20Sopenharmony_ci	opt->opt.cipso = sizeof(struct iphdr);
19558c2ecf20Sopenharmony_ci	kfree(buf);
19568c2ecf20Sopenharmony_ci	buf = NULL;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	req_inet = inet_rsk(req);
19598c2ecf20Sopenharmony_ci	opt = xchg((__force struct ip_options_rcu **)&req_inet->ireq_opt, opt);
19608c2ecf20Sopenharmony_ci	if (opt)
19618c2ecf20Sopenharmony_ci		kfree_rcu(opt, rcu);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	return 0;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_cireq_setattr_failure:
19668c2ecf20Sopenharmony_ci	kfree(buf);
19678c2ecf20Sopenharmony_ci	kfree(opt);
19688c2ecf20Sopenharmony_ci	return ret_val;
19698c2ecf20Sopenharmony_ci}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci/**
19728c2ecf20Sopenharmony_ci * cipso_v4_delopt - Delete the CIPSO option from a set of IP options
19738c2ecf20Sopenharmony_ci * @opt_ptr: IP option pointer
19748c2ecf20Sopenharmony_ci *
19758c2ecf20Sopenharmony_ci * Description:
19768c2ecf20Sopenharmony_ci * Deletes the CIPSO IP option from a set of IP options and makes the necessary
19778c2ecf20Sopenharmony_ci * adjustments to the IP option structure.  Returns zero on success, negative
19788c2ecf20Sopenharmony_ci * values on failure.
19798c2ecf20Sopenharmony_ci *
19808c2ecf20Sopenharmony_ci */
19818c2ecf20Sopenharmony_cistatic int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct ip_options_rcu *opt = rcu_dereference_protected(*opt_ptr, 1);
19848c2ecf20Sopenharmony_ci	int hdr_delta = 0;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	if (!opt || opt->opt.cipso == 0)
19878c2ecf20Sopenharmony_ci		return 0;
19888c2ecf20Sopenharmony_ci	if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) {
19898c2ecf20Sopenharmony_ci		u8 cipso_len;
19908c2ecf20Sopenharmony_ci		u8 cipso_off;
19918c2ecf20Sopenharmony_ci		unsigned char *cipso_ptr;
19928c2ecf20Sopenharmony_ci		int iter;
19938c2ecf20Sopenharmony_ci		int optlen_new;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci		cipso_off = opt->opt.cipso - sizeof(struct iphdr);
19968c2ecf20Sopenharmony_ci		cipso_ptr = &opt->opt.__data[cipso_off];
19978c2ecf20Sopenharmony_ci		cipso_len = cipso_ptr[1];
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci		if (opt->opt.srr > opt->opt.cipso)
20008c2ecf20Sopenharmony_ci			opt->opt.srr -= cipso_len;
20018c2ecf20Sopenharmony_ci		if (opt->opt.rr > opt->opt.cipso)
20028c2ecf20Sopenharmony_ci			opt->opt.rr -= cipso_len;
20038c2ecf20Sopenharmony_ci		if (opt->opt.ts > opt->opt.cipso)
20048c2ecf20Sopenharmony_ci			opt->opt.ts -= cipso_len;
20058c2ecf20Sopenharmony_ci		if (opt->opt.router_alert > opt->opt.cipso)
20068c2ecf20Sopenharmony_ci			opt->opt.router_alert -= cipso_len;
20078c2ecf20Sopenharmony_ci		opt->opt.cipso = 0;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci		memmove(cipso_ptr, cipso_ptr + cipso_len,
20108c2ecf20Sopenharmony_ci			opt->opt.optlen - cipso_off - cipso_len);
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci		/* determining the new total option length is tricky because of
20138c2ecf20Sopenharmony_ci		 * the padding necessary, the only thing i can think to do at
20148c2ecf20Sopenharmony_ci		 * this point is walk the options one-by-one, skipping the
20158c2ecf20Sopenharmony_ci		 * padding at the end to determine the actual option size and
20168c2ecf20Sopenharmony_ci		 * from there we can determine the new total option length */
20178c2ecf20Sopenharmony_ci		iter = 0;
20188c2ecf20Sopenharmony_ci		optlen_new = 0;
20198c2ecf20Sopenharmony_ci		while (iter < opt->opt.optlen)
20208c2ecf20Sopenharmony_ci			if (opt->opt.__data[iter] != IPOPT_NOP) {
20218c2ecf20Sopenharmony_ci				iter += opt->opt.__data[iter + 1];
20228c2ecf20Sopenharmony_ci				optlen_new = iter;
20238c2ecf20Sopenharmony_ci			} else
20248c2ecf20Sopenharmony_ci				iter++;
20258c2ecf20Sopenharmony_ci		hdr_delta = opt->opt.optlen;
20268c2ecf20Sopenharmony_ci		opt->opt.optlen = (optlen_new + 3) & ~3;
20278c2ecf20Sopenharmony_ci		hdr_delta -= opt->opt.optlen;
20288c2ecf20Sopenharmony_ci	} else {
20298c2ecf20Sopenharmony_ci		/* only the cipso option was present on the socket so we can
20308c2ecf20Sopenharmony_ci		 * remove the entire option struct */
20318c2ecf20Sopenharmony_ci		*opt_ptr = NULL;
20328c2ecf20Sopenharmony_ci		hdr_delta = opt->opt.optlen;
20338c2ecf20Sopenharmony_ci		kfree_rcu(opt, rcu);
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	return hdr_delta;
20378c2ecf20Sopenharmony_ci}
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci/**
20408c2ecf20Sopenharmony_ci * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
20418c2ecf20Sopenharmony_ci * @sk: the socket
20428c2ecf20Sopenharmony_ci *
20438c2ecf20Sopenharmony_ci * Description:
20448c2ecf20Sopenharmony_ci * Removes the CIPSO option from a socket, if present.
20458c2ecf20Sopenharmony_ci *
20468c2ecf20Sopenharmony_ci */
20478c2ecf20Sopenharmony_civoid cipso_v4_sock_delattr(struct sock *sk)
20488c2ecf20Sopenharmony_ci{
20498c2ecf20Sopenharmony_ci	struct inet_sock *sk_inet;
20508c2ecf20Sopenharmony_ci	int hdr_delta;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	sk_inet = inet_sk(sk);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt);
20558c2ecf20Sopenharmony_ci	if (sk_inet->is_icsk && hdr_delta > 0) {
20568c2ecf20Sopenharmony_ci		struct inet_connection_sock *sk_conn = inet_csk(sk);
20578c2ecf20Sopenharmony_ci		sk_conn->icsk_ext_hdr_len -= hdr_delta;
20588c2ecf20Sopenharmony_ci		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci/**
20638c2ecf20Sopenharmony_ci * cipso_v4_req_delattr - Delete the CIPSO option from a request socket
20648c2ecf20Sopenharmony_ci * @req: the request socket
20658c2ecf20Sopenharmony_ci *
20668c2ecf20Sopenharmony_ci * Description:
20678c2ecf20Sopenharmony_ci * Removes the CIPSO option from a request socket, if present.
20688c2ecf20Sopenharmony_ci *
20698c2ecf20Sopenharmony_ci */
20708c2ecf20Sopenharmony_civoid cipso_v4_req_delattr(struct request_sock *req)
20718c2ecf20Sopenharmony_ci{
20728c2ecf20Sopenharmony_ci	cipso_v4_delopt(&inet_rsk(req)->ireq_opt);
20738c2ecf20Sopenharmony_ci}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci/**
20768c2ecf20Sopenharmony_ci * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
20778c2ecf20Sopenharmony_ci * @cipso: the CIPSO v4 option
20788c2ecf20Sopenharmony_ci * @secattr: the security attributes
20798c2ecf20Sopenharmony_ci *
20808c2ecf20Sopenharmony_ci * Description:
20818c2ecf20Sopenharmony_ci * Inspect @cipso and return the security attributes in @secattr.  Returns zero
20828c2ecf20Sopenharmony_ci * on success and negative values on failure.
20838c2ecf20Sopenharmony_ci *
20848c2ecf20Sopenharmony_ci */
20858c2ecf20Sopenharmony_ciint cipso_v4_getattr(const unsigned char *cipso,
20868c2ecf20Sopenharmony_ci		     struct netlbl_lsm_secattr *secattr)
20878c2ecf20Sopenharmony_ci{
20888c2ecf20Sopenharmony_ci	int ret_val = -ENOMSG;
20898c2ecf20Sopenharmony_ci	u32 doi;
20908c2ecf20Sopenharmony_ci	struct cipso_v4_doi *doi_def;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	if (cipso_v4_cache_check(cipso, cipso[1], secattr) == 0)
20938c2ecf20Sopenharmony_ci		return 0;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	doi = get_unaligned_be32(&cipso[2]);
20968c2ecf20Sopenharmony_ci	rcu_read_lock();
20978c2ecf20Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
20988c2ecf20Sopenharmony_ci	if (!doi_def)
20998c2ecf20Sopenharmony_ci		goto getattr_return;
21008c2ecf20Sopenharmony_ci	/* XXX - This code assumes only one tag per CIPSO option which isn't
21018c2ecf20Sopenharmony_ci	 * really a good assumption to make but since we only support the MAC
21028c2ecf20Sopenharmony_ci	 * tags right now it is a safe assumption. */
21038c2ecf20Sopenharmony_ci	switch (cipso[6]) {
21048c2ecf20Sopenharmony_ci	case CIPSO_V4_TAG_RBITMAP:
21058c2ecf20Sopenharmony_ci		ret_val = cipso_v4_parsetag_rbm(doi_def, &cipso[6], secattr);
21068c2ecf20Sopenharmony_ci		break;
21078c2ecf20Sopenharmony_ci	case CIPSO_V4_TAG_ENUM:
21088c2ecf20Sopenharmony_ci		ret_val = cipso_v4_parsetag_enum(doi_def, &cipso[6], secattr);
21098c2ecf20Sopenharmony_ci		break;
21108c2ecf20Sopenharmony_ci	case CIPSO_V4_TAG_RANGE:
21118c2ecf20Sopenharmony_ci		ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
21128c2ecf20Sopenharmony_ci		break;
21138c2ecf20Sopenharmony_ci	case CIPSO_V4_TAG_LOCAL:
21148c2ecf20Sopenharmony_ci		ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr);
21158c2ecf20Sopenharmony_ci		break;
21168c2ecf20Sopenharmony_ci	}
21178c2ecf20Sopenharmony_ci	if (ret_val == 0)
21188c2ecf20Sopenharmony_ci		secattr->type = NETLBL_NLTYPE_CIPSOV4;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_cigetattr_return:
21218c2ecf20Sopenharmony_ci	rcu_read_unlock();
21228c2ecf20Sopenharmony_ci	return ret_val;
21238c2ecf20Sopenharmony_ci}
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci/**
21268c2ecf20Sopenharmony_ci * cipso_v4_sock_getattr - Get the security attributes from a sock
21278c2ecf20Sopenharmony_ci * @sk: the sock
21288c2ecf20Sopenharmony_ci * @secattr: the security attributes
21298c2ecf20Sopenharmony_ci *
21308c2ecf20Sopenharmony_ci * Description:
21318c2ecf20Sopenharmony_ci * Query @sk to see if there is a CIPSO option attached to the sock and if
21328c2ecf20Sopenharmony_ci * there is return the CIPSO security attributes in @secattr.  This function
21338c2ecf20Sopenharmony_ci * requires that @sk be locked, or privately held, but it does not do any
21348c2ecf20Sopenharmony_ci * locking itself.  Returns zero on success and negative values on failure.
21358c2ecf20Sopenharmony_ci *
21368c2ecf20Sopenharmony_ci */
21378c2ecf20Sopenharmony_ciint cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
21388c2ecf20Sopenharmony_ci{
21398c2ecf20Sopenharmony_ci	struct ip_options_rcu *opt;
21408c2ecf20Sopenharmony_ci	int res = -ENOMSG;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	rcu_read_lock();
21438c2ecf20Sopenharmony_ci	opt = rcu_dereference(inet_sk(sk)->inet_opt);
21448c2ecf20Sopenharmony_ci	if (opt && opt->opt.cipso)
21458c2ecf20Sopenharmony_ci		res = cipso_v4_getattr(opt->opt.__data +
21468c2ecf20Sopenharmony_ci						opt->opt.cipso -
21478c2ecf20Sopenharmony_ci						sizeof(struct iphdr),
21488c2ecf20Sopenharmony_ci				       secattr);
21498c2ecf20Sopenharmony_ci	rcu_read_unlock();
21508c2ecf20Sopenharmony_ci	return res;
21518c2ecf20Sopenharmony_ci}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci/**
21548c2ecf20Sopenharmony_ci * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet
21558c2ecf20Sopenharmony_ci * @skb: the packet
21568c2ecf20Sopenharmony_ci * @doi_def: the DOI structure
21578c2ecf20Sopenharmony_ci * @secattr: the security attributes
21588c2ecf20Sopenharmony_ci *
21598c2ecf20Sopenharmony_ci * Description:
21608c2ecf20Sopenharmony_ci * Set the CIPSO option on the given packet based on the security attributes.
21618c2ecf20Sopenharmony_ci * Returns a pointer to the IP header on success and NULL on failure.
21628c2ecf20Sopenharmony_ci *
21638c2ecf20Sopenharmony_ci */
21648c2ecf20Sopenharmony_ciint cipso_v4_skbuff_setattr(struct sk_buff *skb,
21658c2ecf20Sopenharmony_ci			    const struct cipso_v4_doi *doi_def,
21668c2ecf20Sopenharmony_ci			    const struct netlbl_lsm_secattr *secattr)
21678c2ecf20Sopenharmony_ci{
21688c2ecf20Sopenharmony_ci	int ret_val;
21698c2ecf20Sopenharmony_ci	struct iphdr *iph;
21708c2ecf20Sopenharmony_ci	struct ip_options *opt = &IPCB(skb)->opt;
21718c2ecf20Sopenharmony_ci	unsigned char buf[CIPSO_V4_OPT_LEN_MAX];
21728c2ecf20Sopenharmony_ci	u32 buf_len = CIPSO_V4_OPT_LEN_MAX;
21738c2ecf20Sopenharmony_ci	u32 opt_len;
21748c2ecf20Sopenharmony_ci	int len_delta;
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
21778c2ecf20Sopenharmony_ci	if (ret_val < 0)
21788c2ecf20Sopenharmony_ci		return ret_val;
21798c2ecf20Sopenharmony_ci	buf_len = ret_val;
21808c2ecf20Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	/* we overwrite any existing options to ensure that we have enough
21838c2ecf20Sopenharmony_ci	 * room for the CIPSO option, the reason is that we _need_ to guarantee
21848c2ecf20Sopenharmony_ci	 * that the security label is applied to the packet - we do the same
21858c2ecf20Sopenharmony_ci	 * thing when using the socket options and it hasn't caused a problem,
21868c2ecf20Sopenharmony_ci	 * if we need to we can always revisit this choice later */
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	len_delta = opt_len - opt->optlen;
21898c2ecf20Sopenharmony_ci	/* if we don't ensure enough headroom we could panic on the skb_push()
21908c2ecf20Sopenharmony_ci	 * call below so make sure we have enough, we are also "mangling" the
21918c2ecf20Sopenharmony_ci	 * packet so we should probably do a copy-on-write call anyway */
21928c2ecf20Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
21938c2ecf20Sopenharmony_ci	if (ret_val < 0)
21948c2ecf20Sopenharmony_ci		return ret_val;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	if (len_delta > 0) {
21978c2ecf20Sopenharmony_ci		/* we assume that the header + opt->optlen have already been
21988c2ecf20Sopenharmony_ci		 * "pushed" in ip_options_build() or similar */
21998c2ecf20Sopenharmony_ci		iph = ip_hdr(skb);
22008c2ecf20Sopenharmony_ci		skb_push(skb, len_delta);
22018c2ecf20Sopenharmony_ci		memmove((char *)iph - len_delta, iph, iph->ihl << 2);
22028c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
22038c2ecf20Sopenharmony_ci		iph = ip_hdr(skb);
22048c2ecf20Sopenharmony_ci	} else if (len_delta < 0) {
22058c2ecf20Sopenharmony_ci		iph = ip_hdr(skb);
22068c2ecf20Sopenharmony_ci		memset(iph + 1, IPOPT_NOP, opt->optlen);
22078c2ecf20Sopenharmony_ci	} else
22088c2ecf20Sopenharmony_ci		iph = ip_hdr(skb);
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	if (opt->optlen > 0)
22118c2ecf20Sopenharmony_ci		memset(opt, 0, sizeof(*opt));
22128c2ecf20Sopenharmony_ci	opt->optlen = opt_len;
22138c2ecf20Sopenharmony_ci	opt->cipso = sizeof(struct iphdr);
22148c2ecf20Sopenharmony_ci	opt->is_changed = 1;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	/* we have to do the following because we are being called from a
22178c2ecf20Sopenharmony_ci	 * netfilter hook which means the packet already has had the header
22188c2ecf20Sopenharmony_ci	 * fields populated and the checksum calculated - yes this means we
22198c2ecf20Sopenharmony_ci	 * are doing more work than needed but we do it to keep the core
22208c2ecf20Sopenharmony_ci	 * stack clean and tidy */
22218c2ecf20Sopenharmony_ci	memcpy(iph + 1, buf, buf_len);
22228c2ecf20Sopenharmony_ci	if (opt_len > buf_len)
22238c2ecf20Sopenharmony_ci		memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len);
22248c2ecf20Sopenharmony_ci	if (len_delta != 0) {
22258c2ecf20Sopenharmony_ci		iph->ihl = 5 + (opt_len >> 2);
22268c2ecf20Sopenharmony_ci		iph->tot_len = htons(skb->len);
22278c2ecf20Sopenharmony_ci	}
22288c2ecf20Sopenharmony_ci	ip_send_check(iph);
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	return 0;
22318c2ecf20Sopenharmony_ci}
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci/**
22348c2ecf20Sopenharmony_ci * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet
22358c2ecf20Sopenharmony_ci * @skb: the packet
22368c2ecf20Sopenharmony_ci *
22378c2ecf20Sopenharmony_ci * Description:
22388c2ecf20Sopenharmony_ci * Removes any and all CIPSO options from the given packet.  Returns zero on
22398c2ecf20Sopenharmony_ci * success, negative values on failure.
22408c2ecf20Sopenharmony_ci *
22418c2ecf20Sopenharmony_ci */
22428c2ecf20Sopenharmony_ciint cipso_v4_skbuff_delattr(struct sk_buff *skb)
22438c2ecf20Sopenharmony_ci{
22448c2ecf20Sopenharmony_ci	int ret_val;
22458c2ecf20Sopenharmony_ci	struct iphdr *iph;
22468c2ecf20Sopenharmony_ci	struct ip_options *opt = &IPCB(skb)->opt;
22478c2ecf20Sopenharmony_ci	unsigned char *cipso_ptr;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (opt->cipso == 0)
22508c2ecf20Sopenharmony_ci		return 0;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	/* since we are changing the packet we should make a copy */
22538c2ecf20Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb));
22548c2ecf20Sopenharmony_ci	if (ret_val < 0)
22558c2ecf20Sopenharmony_ci		return ret_val;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	/* the easiest thing to do is just replace the cipso option with noop
22588c2ecf20Sopenharmony_ci	 * options since we don't change the size of the packet, although we
22598c2ecf20Sopenharmony_ci	 * still need to recalculate the checksum */
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	iph = ip_hdr(skb);
22628c2ecf20Sopenharmony_ci	cipso_ptr = (unsigned char *)iph + opt->cipso;
22638c2ecf20Sopenharmony_ci	memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
22648c2ecf20Sopenharmony_ci	opt->cipso = 0;
22658c2ecf20Sopenharmony_ci	opt->is_changed = 1;
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	ip_send_check(iph);
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	return 0;
22708c2ecf20Sopenharmony_ci}
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci/*
22738c2ecf20Sopenharmony_ci * Setup Functions
22748c2ecf20Sopenharmony_ci */
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci/**
22778c2ecf20Sopenharmony_ci * cipso_v4_init - Initialize the CIPSO module
22788c2ecf20Sopenharmony_ci *
22798c2ecf20Sopenharmony_ci * Description:
22808c2ecf20Sopenharmony_ci * Initialize the CIPSO module and prepare it for use.  Returns zero on success
22818c2ecf20Sopenharmony_ci * and negative values on failure.
22828c2ecf20Sopenharmony_ci *
22838c2ecf20Sopenharmony_ci */
22848c2ecf20Sopenharmony_cistatic int __init cipso_v4_init(void)
22858c2ecf20Sopenharmony_ci{
22868c2ecf20Sopenharmony_ci	int ret_val;
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	ret_val = cipso_v4_cache_init();
22898c2ecf20Sopenharmony_ci	if (ret_val != 0)
22908c2ecf20Sopenharmony_ci		panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n",
22918c2ecf20Sopenharmony_ci		      ret_val);
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	return 0;
22948c2ecf20Sopenharmony_ci}
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_cisubsys_initcall(cipso_v4_init);
2297