xref: /kernel/linux/linux-5.10/net/ipv6/calipso.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CALIPSO - Common Architecture Label IPv6 Security Option
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is an implementation of the CALIPSO protocol as specified in
68c2ecf20Sopenharmony_ci * RFC 5570.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Authors: Paul Moore <paul.moore@hp.com>
98c2ecf20Sopenharmony_ci *          Huw Davies <huw@codeweavers.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
138c2ecf20Sopenharmony_ci * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
198c2ecf20Sopenharmony_ci#include <linux/list.h>
208c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
218c2ecf20Sopenharmony_ci#include <linux/string.h>
228c2ecf20Sopenharmony_ci#include <linux/jhash.h>
238c2ecf20Sopenharmony_ci#include <linux/audit.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <net/ip.h>
268c2ecf20Sopenharmony_ci#include <net/icmp.h>
278c2ecf20Sopenharmony_ci#include <net/tcp.h>
288c2ecf20Sopenharmony_ci#include <net/netlabel.h>
298c2ecf20Sopenharmony_ci#include <net/calipso.h>
308c2ecf20Sopenharmony_ci#include <linux/atomic.h>
318c2ecf20Sopenharmony_ci#include <linux/bug.h>
328c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
338c2ecf20Sopenharmony_ci#include <linux/crc-ccitt.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Maximium size of the calipso option including
368c2ecf20Sopenharmony_ci * the two-byte TLV header.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define CALIPSO_OPT_LEN_MAX (2 + 252)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Size of the minimum calipso option including
418c2ecf20Sopenharmony_ci * the two-byte TLV header.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci#define CALIPSO_HDR_LEN (2 + 8)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Maximium size of the calipso option including
468c2ecf20Sopenharmony_ci * the two-byte TLV header and upto 3 bytes of
478c2ecf20Sopenharmony_ci * leading pad and 7 bytes of trailing pad.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci /* Maximium size of u32 aligned buffer required to hold calipso
528c2ecf20Sopenharmony_ci  * option.  Max of 3 initial pad bytes starting from buffer + 3.
538c2ecf20Sopenharmony_ci  * i.e. the worst case is when the previous tlv finishes on 4n + 3.
548c2ecf20Sopenharmony_ci  */
558c2ecf20Sopenharmony_ci#define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* List of available DOI definitions */
588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(calipso_doi_list_lock);
598c2ecf20Sopenharmony_cistatic LIST_HEAD(calipso_doi_list);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Label mapping cache */
628c2ecf20Sopenharmony_ciint calipso_cache_enabled = 1;
638c2ecf20Sopenharmony_ciint calipso_cache_bucketsize = 10;
648c2ecf20Sopenharmony_ci#define CALIPSO_CACHE_BUCKETBITS     7
658c2ecf20Sopenharmony_ci#define CALIPSO_CACHE_BUCKETS        BIT(CALIPSO_CACHE_BUCKETBITS)
668c2ecf20Sopenharmony_ci#define CALIPSO_CACHE_REORDERLIMIT   10
678c2ecf20Sopenharmony_cistruct calipso_map_cache_bkt {
688c2ecf20Sopenharmony_ci	spinlock_t lock;
698c2ecf20Sopenharmony_ci	u32 size;
708c2ecf20Sopenharmony_ci	struct list_head list;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct calipso_map_cache_entry {
748c2ecf20Sopenharmony_ci	u32 hash;
758c2ecf20Sopenharmony_ci	unsigned char *key;
768c2ecf20Sopenharmony_ci	size_t key_len;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	struct netlbl_lsm_cache *lsm_data;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	u32 activity;
818c2ecf20Sopenharmony_ci	struct list_head list;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic struct calipso_map_cache_bkt *calipso_cache;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void calipso_cache_invalidate(void);
878c2ecf20Sopenharmony_cistatic void calipso_doi_putdef(struct calipso_doi *doi_def);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* Label Mapping Cache Functions
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/**
938c2ecf20Sopenharmony_ci * calipso_cache_entry_free - Frees a cache entry
948c2ecf20Sopenharmony_ci * @entry: the entry to free
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * Description:
978c2ecf20Sopenharmony_ci * This function frees the memory associated with a cache entry including the
988c2ecf20Sopenharmony_ci * LSM cache data if there are no longer any users, i.e. reference count == 0.
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_cistatic void calipso_cache_entry_free(struct calipso_map_cache_entry *entry)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	if (entry->lsm_data)
1048c2ecf20Sopenharmony_ci		netlbl_secattr_cache_free(entry->lsm_data);
1058c2ecf20Sopenharmony_ci	kfree(entry->key);
1068c2ecf20Sopenharmony_ci	kfree(entry);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/**
1108c2ecf20Sopenharmony_ci * calipso_map_cache_hash - Hashing function for the CALIPSO cache
1118c2ecf20Sopenharmony_ci * @key: the hash key
1128c2ecf20Sopenharmony_ci * @key_len: the length of the key in bytes
1138c2ecf20Sopenharmony_ci *
1148c2ecf20Sopenharmony_ci * Description:
1158c2ecf20Sopenharmony_ci * The CALIPSO tag hashing function.  Returns a 32-bit hash value.
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_cistatic u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return jhash(key, key_len, 0);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * calipso_cache_init - Initialize the CALIPSO cache
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Description:
1278c2ecf20Sopenharmony_ci * Initializes the CALIPSO label mapping cache, this function should be called
1288c2ecf20Sopenharmony_ci * before any of the other functions defined in this file.  Returns zero on
1298c2ecf20Sopenharmony_ci * success, negative values on error.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic int __init calipso_cache_init(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	u32 iter;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS,
1378c2ecf20Sopenharmony_ci				sizeof(struct calipso_map_cache_bkt),
1388c2ecf20Sopenharmony_ci				GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	if (!calipso_cache)
1408c2ecf20Sopenharmony_ci		return -ENOMEM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
1438c2ecf20Sopenharmony_ci		spin_lock_init(&calipso_cache[iter].lock);
1448c2ecf20Sopenharmony_ci		calipso_cache[iter].size = 0;
1458c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&calipso_cache[iter].list);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * calipso_cache_invalidate - Invalidates the current CALIPSO cache
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * Description:
1558c2ecf20Sopenharmony_ci * Invalidates and frees any entries in the CALIPSO cache.  Returns zero on
1568c2ecf20Sopenharmony_ci * success and negative values on failure.
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistatic void calipso_cache_invalidate(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct calipso_map_cache_entry *entry, *tmp_entry;
1628c2ecf20Sopenharmony_ci	u32 iter;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
1658c2ecf20Sopenharmony_ci		spin_lock_bh(&calipso_cache[iter].lock);
1668c2ecf20Sopenharmony_ci		list_for_each_entry_safe(entry,
1678c2ecf20Sopenharmony_ci					 tmp_entry,
1688c2ecf20Sopenharmony_ci					 &calipso_cache[iter].list, list) {
1698c2ecf20Sopenharmony_ci			list_del(&entry->list);
1708c2ecf20Sopenharmony_ci			calipso_cache_entry_free(entry);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		calipso_cache[iter].size = 0;
1738c2ecf20Sopenharmony_ci		spin_unlock_bh(&calipso_cache[iter].lock);
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * calipso_cache_check - Check the CALIPSO cache for a label mapping
1798c2ecf20Sopenharmony_ci * @key: the buffer to check
1808c2ecf20Sopenharmony_ci * @key_len: buffer length in bytes
1818c2ecf20Sopenharmony_ci * @secattr: the security attribute struct to use
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * Description:
1848c2ecf20Sopenharmony_ci * This function checks the cache to see if a label mapping already exists for
1858c2ecf20Sopenharmony_ci * the given key.  If there is a match then the cache is adjusted and the
1868c2ecf20Sopenharmony_ci * @secattr struct is populated with the correct LSM security attributes.  The
1878c2ecf20Sopenharmony_ci * cache is adjusted in the following manner if the entry is not already the
1888c2ecf20Sopenharmony_ci * first in the cache bucket:
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci *  1. The cache entry's activity counter is incremented
1918c2ecf20Sopenharmony_ci *  2. The previous (higher ranking) entry's activity counter is decremented
1928c2ecf20Sopenharmony_ci *  3. If the difference between the two activity counters is geater than
1938c2ecf20Sopenharmony_ci *     CALIPSO_CACHE_REORDERLIMIT the two entries are swapped
1948c2ecf20Sopenharmony_ci *
1958c2ecf20Sopenharmony_ci * Returns zero on success, -ENOENT for a cache miss, and other negative values
1968c2ecf20Sopenharmony_ci * on error.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_cistatic int calipso_cache_check(const unsigned char *key,
2008c2ecf20Sopenharmony_ci			       u32 key_len,
2018c2ecf20Sopenharmony_ci			       struct netlbl_lsm_secattr *secattr)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	u32 bkt;
2048c2ecf20Sopenharmony_ci	struct calipso_map_cache_entry *entry;
2058c2ecf20Sopenharmony_ci	struct calipso_map_cache_entry *prev_entry = NULL;
2068c2ecf20Sopenharmony_ci	u32 hash;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (!calipso_cache_enabled)
2098c2ecf20Sopenharmony_ci		return -ENOENT;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	hash = calipso_map_cache_hash(key, key_len);
2128c2ecf20Sopenharmony_ci	bkt = hash & (CALIPSO_CACHE_BUCKETS - 1);
2138c2ecf20Sopenharmony_ci	spin_lock_bh(&calipso_cache[bkt].lock);
2148c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &calipso_cache[bkt].list, list) {
2158c2ecf20Sopenharmony_ci		if (entry->hash == hash &&
2168c2ecf20Sopenharmony_ci		    entry->key_len == key_len &&
2178c2ecf20Sopenharmony_ci		    memcmp(entry->key, key, key_len) == 0) {
2188c2ecf20Sopenharmony_ci			entry->activity += 1;
2198c2ecf20Sopenharmony_ci			refcount_inc(&entry->lsm_data->refcount);
2208c2ecf20Sopenharmony_ci			secattr->cache = entry->lsm_data;
2218c2ecf20Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_CACHE;
2228c2ecf20Sopenharmony_ci			secattr->type = NETLBL_NLTYPE_CALIPSO;
2238c2ecf20Sopenharmony_ci			if (!prev_entry) {
2248c2ecf20Sopenharmony_ci				spin_unlock_bh(&calipso_cache[bkt].lock);
2258c2ecf20Sopenharmony_ci				return 0;
2268c2ecf20Sopenharmony_ci			}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci			if (prev_entry->activity > 0)
2298c2ecf20Sopenharmony_ci				prev_entry->activity -= 1;
2308c2ecf20Sopenharmony_ci			if (entry->activity > prev_entry->activity &&
2318c2ecf20Sopenharmony_ci			    entry->activity - prev_entry->activity >
2328c2ecf20Sopenharmony_ci			    CALIPSO_CACHE_REORDERLIMIT) {
2338c2ecf20Sopenharmony_ci				__list_del(entry->list.prev, entry->list.next);
2348c2ecf20Sopenharmony_ci				__list_add(&entry->list,
2358c2ecf20Sopenharmony_ci					   prev_entry->list.prev,
2368c2ecf20Sopenharmony_ci					   &prev_entry->list);
2378c2ecf20Sopenharmony_ci			}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci			spin_unlock_bh(&calipso_cache[bkt].lock);
2408c2ecf20Sopenharmony_ci			return 0;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci		prev_entry = entry;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	spin_unlock_bh(&calipso_cache[bkt].lock);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return -ENOENT;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/**
2508c2ecf20Sopenharmony_ci * calipso_cache_add - Add an entry to the CALIPSO cache
2518c2ecf20Sopenharmony_ci * @calipso_ptr: the CALIPSO option
2528c2ecf20Sopenharmony_ci * @secattr: the packet's security attributes
2538c2ecf20Sopenharmony_ci *
2548c2ecf20Sopenharmony_ci * Description:
2558c2ecf20Sopenharmony_ci * Add a new entry into the CALIPSO label mapping cache.  Add the new entry to
2568c2ecf20Sopenharmony_ci * head of the cache bucket's list, if the cache bucket is out of room remove
2578c2ecf20Sopenharmony_ci * the last entry in the list first.  It is important to note that there is
2588c2ecf20Sopenharmony_ci * currently no checking for duplicate keys.  Returns zero on success,
2598c2ecf20Sopenharmony_ci * negative values on failure.  The key stored starts at calipso_ptr + 2,
2608c2ecf20Sopenharmony_ci * i.e. the type and length bytes are not stored, this corresponds to
2618c2ecf20Sopenharmony_ci * calipso_ptr[1] bytes of data.
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic int calipso_cache_add(const unsigned char *calipso_ptr,
2658c2ecf20Sopenharmony_ci			     const struct netlbl_lsm_secattr *secattr)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int ret_val = -EPERM;
2688c2ecf20Sopenharmony_ci	u32 bkt;
2698c2ecf20Sopenharmony_ci	struct calipso_map_cache_entry *entry = NULL;
2708c2ecf20Sopenharmony_ci	struct calipso_map_cache_entry *old_entry = NULL;
2718c2ecf20Sopenharmony_ci	u32 calipso_ptr_len;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0)
2748c2ecf20Sopenharmony_ci		return 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	calipso_ptr_len = calipso_ptr[1];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
2798c2ecf20Sopenharmony_ci	if (!entry)
2808c2ecf20Sopenharmony_ci		return -ENOMEM;
2818c2ecf20Sopenharmony_ci	entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC);
2828c2ecf20Sopenharmony_ci	if (!entry->key) {
2838c2ecf20Sopenharmony_ci		ret_val = -ENOMEM;
2848c2ecf20Sopenharmony_ci		goto cache_add_failure;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	entry->key_len = calipso_ptr_len;
2878c2ecf20Sopenharmony_ci	entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len);
2888c2ecf20Sopenharmony_ci	refcount_inc(&secattr->cache->refcount);
2898c2ecf20Sopenharmony_ci	entry->lsm_data = secattr->cache;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1);
2928c2ecf20Sopenharmony_ci	spin_lock_bh(&calipso_cache[bkt].lock);
2938c2ecf20Sopenharmony_ci	if (calipso_cache[bkt].size < calipso_cache_bucketsize) {
2948c2ecf20Sopenharmony_ci		list_add(&entry->list, &calipso_cache[bkt].list);
2958c2ecf20Sopenharmony_ci		calipso_cache[bkt].size += 1;
2968c2ecf20Sopenharmony_ci	} else {
2978c2ecf20Sopenharmony_ci		old_entry = list_entry(calipso_cache[bkt].list.prev,
2988c2ecf20Sopenharmony_ci				       struct calipso_map_cache_entry, list);
2998c2ecf20Sopenharmony_ci		list_del(&old_entry->list);
3008c2ecf20Sopenharmony_ci		list_add(&entry->list, &calipso_cache[bkt].list);
3018c2ecf20Sopenharmony_ci		calipso_cache_entry_free(old_entry);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	spin_unlock_bh(&calipso_cache[bkt].lock);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cicache_add_failure:
3088c2ecf20Sopenharmony_ci	if (entry)
3098c2ecf20Sopenharmony_ci		calipso_cache_entry_free(entry);
3108c2ecf20Sopenharmony_ci	return ret_val;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/* DOI List Functions
3148c2ecf20Sopenharmony_ci */
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/**
3178c2ecf20Sopenharmony_ci * calipso_doi_search - Searches for a DOI definition
3188c2ecf20Sopenharmony_ci * @doi: the DOI to search for
3198c2ecf20Sopenharmony_ci *
3208c2ecf20Sopenharmony_ci * Description:
3218c2ecf20Sopenharmony_ci * Search the DOI definition list for a DOI definition with a DOI value that
3228c2ecf20Sopenharmony_ci * matches @doi.  The caller is responsible for calling rcu_read_[un]lock().
3238c2ecf20Sopenharmony_ci * Returns a pointer to the DOI definition on success and NULL on failure.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic struct calipso_doi *calipso_doi_search(u32 doi)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct calipso_doi *iter;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(iter, &calipso_doi_list, list)
3308c2ecf20Sopenharmony_ci		if (iter->doi == doi && refcount_read(&iter->refcount))
3318c2ecf20Sopenharmony_ci			return iter;
3328c2ecf20Sopenharmony_ci	return NULL;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
3378c2ecf20Sopenharmony_ci * @doi_def: the DOI structure
3388c2ecf20Sopenharmony_ci * @audit_info: NetLabel audit information
3398c2ecf20Sopenharmony_ci *
3408c2ecf20Sopenharmony_ci * Description:
3418c2ecf20Sopenharmony_ci * The caller defines a new DOI for use by the CALIPSO engine and calls this
3428c2ecf20Sopenharmony_ci * function to add it to the list of acceptable domains.  The caller must
3438c2ecf20Sopenharmony_ci * ensure that the mapping table specified in @doi_def->map meets all of the
3448c2ecf20Sopenharmony_ci * requirements of the mapping type (see calipso.h for details).  Returns
3458c2ecf20Sopenharmony_ci * zero on success and non-zero on failure.
3468c2ecf20Sopenharmony_ci *
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic int calipso_doi_add(struct calipso_doi *doi_def,
3498c2ecf20Sopenharmony_ci			   struct netlbl_audit *audit_info)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	int ret_val = -EINVAL;
3528c2ecf20Sopenharmony_ci	u32 doi;
3538c2ecf20Sopenharmony_ci	u32 doi_type;
3548c2ecf20Sopenharmony_ci	struct audit_buffer *audit_buf;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	doi = doi_def->doi;
3578c2ecf20Sopenharmony_ci	doi_type = doi_def->type;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (doi_def->doi == CALIPSO_DOI_UNKNOWN)
3608c2ecf20Sopenharmony_ci		goto doi_add_return;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	refcount_set(&doi_def->refcount, 1);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	spin_lock(&calipso_doi_list_lock);
3658c2ecf20Sopenharmony_ci	if (calipso_doi_search(doi_def->doi)) {
3668c2ecf20Sopenharmony_ci		spin_unlock(&calipso_doi_list_lock);
3678c2ecf20Sopenharmony_ci		ret_val = -EEXIST;
3688c2ecf20Sopenharmony_ci		goto doi_add_return;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	list_add_tail_rcu(&doi_def->list, &calipso_doi_list);
3718c2ecf20Sopenharmony_ci	spin_unlock(&calipso_doi_list_lock);
3728c2ecf20Sopenharmony_ci	ret_val = 0;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cidoi_add_return:
3758c2ecf20Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info);
3768c2ecf20Sopenharmony_ci	if (audit_buf) {
3778c2ecf20Sopenharmony_ci		const char *type_str;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		switch (doi_type) {
3808c2ecf20Sopenharmony_ci		case CALIPSO_MAP_PASS:
3818c2ecf20Sopenharmony_ci			type_str = "pass";
3828c2ecf20Sopenharmony_ci			break;
3838c2ecf20Sopenharmony_ci		default:
3848c2ecf20Sopenharmony_ci			type_str = "(unknown)";
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci		audit_log_format(audit_buf,
3878c2ecf20Sopenharmony_ci				 " calipso_doi=%u calipso_type=%s res=%u",
3888c2ecf20Sopenharmony_ci				 doi, type_str, ret_val == 0 ? 1 : 0);
3898c2ecf20Sopenharmony_ci		audit_log_end(audit_buf);
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return ret_val;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci/**
3968c2ecf20Sopenharmony_ci * calipso_doi_free - Frees a DOI definition
3978c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
3988c2ecf20Sopenharmony_ci *
3998c2ecf20Sopenharmony_ci * Description:
4008c2ecf20Sopenharmony_ci * This function frees all of the memory associated with a DOI definition.
4018c2ecf20Sopenharmony_ci *
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic void calipso_doi_free(struct calipso_doi *doi_def)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	kfree(doi_def);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/**
4098c2ecf20Sopenharmony_ci * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer
4108c2ecf20Sopenharmony_ci * @entry: the entry's RCU field
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci * Description:
4138c2ecf20Sopenharmony_ci * This function is designed to be used as a callback to the call_rcu()
4148c2ecf20Sopenharmony_ci * function so that the memory allocated to the DOI definition can be released
4158c2ecf20Sopenharmony_ci * safely.
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_cistatic void calipso_doi_free_rcu(struct rcu_head *entry)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct calipso_doi *doi_def;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	doi_def = container_of(entry, struct calipso_doi, rcu);
4238c2ecf20Sopenharmony_ci	calipso_doi_free(doi_def);
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/**
4278c2ecf20Sopenharmony_ci * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
4288c2ecf20Sopenharmony_ci * @doi: the DOI value
4298c2ecf20Sopenharmony_ci * @audit_secid: the LSM secid to use in the audit message
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci * Description:
4328c2ecf20Sopenharmony_ci * Removes a DOI definition from the CALIPSO engine.  The NetLabel routines will
4338c2ecf20Sopenharmony_ci * be called to release their own LSM domain mappings as well as our own
4348c2ecf20Sopenharmony_ci * domain list.  Returns zero on success and negative values on failure.
4358c2ecf20Sopenharmony_ci *
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_cistatic int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	int ret_val;
4408c2ecf20Sopenharmony_ci	struct calipso_doi *doi_def;
4418c2ecf20Sopenharmony_ci	struct audit_buffer *audit_buf;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	spin_lock(&calipso_doi_list_lock);
4448c2ecf20Sopenharmony_ci	doi_def = calipso_doi_search(doi);
4458c2ecf20Sopenharmony_ci	if (!doi_def) {
4468c2ecf20Sopenharmony_ci		spin_unlock(&calipso_doi_list_lock);
4478c2ecf20Sopenharmony_ci		ret_val = -ENOENT;
4488c2ecf20Sopenharmony_ci		goto doi_remove_return;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	list_del_rcu(&doi_def->list);
4518c2ecf20Sopenharmony_ci	spin_unlock(&calipso_doi_list_lock);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	calipso_doi_putdef(doi_def);
4548c2ecf20Sopenharmony_ci	ret_val = 0;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cidoi_remove_return:
4578c2ecf20Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info);
4588c2ecf20Sopenharmony_ci	if (audit_buf) {
4598c2ecf20Sopenharmony_ci		audit_log_format(audit_buf,
4608c2ecf20Sopenharmony_ci				 " calipso_doi=%u res=%u",
4618c2ecf20Sopenharmony_ci				 doi, ret_val == 0 ? 1 : 0);
4628c2ecf20Sopenharmony_ci		audit_log_end(audit_buf);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return ret_val;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/**
4698c2ecf20Sopenharmony_ci * calipso_doi_getdef - Returns a reference to a valid DOI definition
4708c2ecf20Sopenharmony_ci * @doi: the DOI value
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci * Description:
4738c2ecf20Sopenharmony_ci * Searches for a valid DOI definition and if one is found it is returned to
4748c2ecf20Sopenharmony_ci * the caller.  Otherwise NULL is returned.  The caller must ensure that
4758c2ecf20Sopenharmony_ci * calipso_doi_putdef() is called when the caller is done.
4768c2ecf20Sopenharmony_ci *
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_cistatic struct calipso_doi *calipso_doi_getdef(u32 doi)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct calipso_doi *doi_def;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	rcu_read_lock();
4838c2ecf20Sopenharmony_ci	doi_def = calipso_doi_search(doi);
4848c2ecf20Sopenharmony_ci	if (!doi_def)
4858c2ecf20Sopenharmony_ci		goto doi_getdef_return;
4868c2ecf20Sopenharmony_ci	if (!refcount_inc_not_zero(&doi_def->refcount))
4878c2ecf20Sopenharmony_ci		doi_def = NULL;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cidoi_getdef_return:
4908c2ecf20Sopenharmony_ci	rcu_read_unlock();
4918c2ecf20Sopenharmony_ci	return doi_def;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci/**
4958c2ecf20Sopenharmony_ci * calipso_doi_putdef - Releases a reference for the given DOI definition
4968c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
4978c2ecf20Sopenharmony_ci *
4988c2ecf20Sopenharmony_ci * Description:
4998c2ecf20Sopenharmony_ci * Releases a DOI definition reference obtained from calipso_doi_getdef().
5008c2ecf20Sopenharmony_ci *
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_cistatic void calipso_doi_putdef(struct calipso_doi *doi_def)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	if (!doi_def)
5058c2ecf20Sopenharmony_ci		return;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&doi_def->refcount))
5088c2ecf20Sopenharmony_ci		return;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	calipso_cache_invalidate();
5118c2ecf20Sopenharmony_ci	call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci/**
5158c2ecf20Sopenharmony_ci * calipso_doi_walk - Iterate through the DOI definitions
5168c2ecf20Sopenharmony_ci * @skip_cnt: skip past this number of DOI definitions, updated
5178c2ecf20Sopenharmony_ci * @callback: callback for each DOI definition
5188c2ecf20Sopenharmony_ci * @cb_arg: argument for the callback function
5198c2ecf20Sopenharmony_ci *
5208c2ecf20Sopenharmony_ci * Description:
5218c2ecf20Sopenharmony_ci * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
5228c2ecf20Sopenharmony_ci * For each entry call @callback, if @callback returns a negative value stop
5238c2ecf20Sopenharmony_ci * 'walking' through the list and return.  Updates the value in @skip_cnt upon
5248c2ecf20Sopenharmony_ci * return.  Returns zero on success, negative values on failure.
5258c2ecf20Sopenharmony_ci *
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_cistatic int calipso_doi_walk(u32 *skip_cnt,
5288c2ecf20Sopenharmony_ci			    int (*callback)(struct calipso_doi *doi_def,
5298c2ecf20Sopenharmony_ci					    void *arg),
5308c2ecf20Sopenharmony_ci			    void *cb_arg)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	int ret_val = -ENOENT;
5338c2ecf20Sopenharmony_ci	u32 doi_cnt = 0;
5348c2ecf20Sopenharmony_ci	struct calipso_doi *iter_doi;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	rcu_read_lock();
5378c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list)
5388c2ecf20Sopenharmony_ci		if (refcount_read(&iter_doi->refcount) > 0) {
5398c2ecf20Sopenharmony_ci			if (doi_cnt++ < *skip_cnt)
5408c2ecf20Sopenharmony_ci				continue;
5418c2ecf20Sopenharmony_ci			ret_val = callback(iter_doi, cb_arg);
5428c2ecf20Sopenharmony_ci			if (ret_val < 0) {
5438c2ecf20Sopenharmony_ci				doi_cnt--;
5448c2ecf20Sopenharmony_ci				goto doi_walk_return;
5458c2ecf20Sopenharmony_ci			}
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cidoi_walk_return:
5498c2ecf20Sopenharmony_ci	rcu_read_unlock();
5508c2ecf20Sopenharmony_ci	*skip_cnt = doi_cnt;
5518c2ecf20Sopenharmony_ci	return ret_val;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/**
5558c2ecf20Sopenharmony_ci * calipso_validate - Validate a CALIPSO option
5568c2ecf20Sopenharmony_ci * @skb: the packet
5578c2ecf20Sopenharmony_ci * @option: the start of the option
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Description:
5608c2ecf20Sopenharmony_ci * This routine is called to validate a CALIPSO option.
5618c2ecf20Sopenharmony_ci * If the option is valid then %true is returned, otherwise
5628c2ecf20Sopenharmony_ci * %false is returned.
5638c2ecf20Sopenharmony_ci *
5648c2ecf20Sopenharmony_ci * The caller should have already checked that the length of the
5658c2ecf20Sopenharmony_ci * option (including the TLV header) is >= 10 and that the catmap
5668c2ecf20Sopenharmony_ci * length is consistent with the option length.
5678c2ecf20Sopenharmony_ci *
5688c2ecf20Sopenharmony_ci * We leave checks on the level and categories to the socket layer.
5698c2ecf20Sopenharmony_ci */
5708c2ecf20Sopenharmony_cibool calipso_validate(const struct sk_buff *skb, const unsigned char *option)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct calipso_doi *doi_def;
5738c2ecf20Sopenharmony_ci	bool ret_val;
5748c2ecf20Sopenharmony_ci	u16 crc, len = option[1] + 2;
5758c2ecf20Sopenharmony_ci	static const u8 zero[2];
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* The original CRC runs over the option including the TLV header
5788c2ecf20Sopenharmony_ci	 * with the CRC-16 field (at offset 8) zeroed out. */
5798c2ecf20Sopenharmony_ci	crc = crc_ccitt(0xffff, option, 8);
5808c2ecf20Sopenharmony_ci	crc = crc_ccitt(crc, zero, sizeof(zero));
5818c2ecf20Sopenharmony_ci	if (len > 10)
5828c2ecf20Sopenharmony_ci		crc = crc_ccitt(crc, option + 10, len - 10);
5838c2ecf20Sopenharmony_ci	crc = ~crc;
5848c2ecf20Sopenharmony_ci	if (option[8] != (crc & 0xff) || option[9] != ((crc >> 8) & 0xff))
5858c2ecf20Sopenharmony_ci		return false;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	rcu_read_lock();
5888c2ecf20Sopenharmony_ci	doi_def = calipso_doi_search(get_unaligned_be32(option + 2));
5898c2ecf20Sopenharmony_ci	ret_val = !!doi_def;
5908c2ecf20Sopenharmony_ci	rcu_read_unlock();
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return ret_val;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/**
5968c2ecf20Sopenharmony_ci * calipso_map_cat_hton - Perform a category mapping from host to network
5978c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
5988c2ecf20Sopenharmony_ci * @secattr: the security attributes
5998c2ecf20Sopenharmony_ci * @net_cat: the zero'd out category bitmap in network/CALIPSO format
6008c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CALIPSO bitmap in bytes
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci * Description:
6038c2ecf20Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
6048c2ecf20Sopenharmony_ci * correct CALIPSO bitmap using the given DOI definition.  Returns the minimum
6058c2ecf20Sopenharmony_ci * size in bytes of the network bitmap on success, negative values otherwise.
6068c2ecf20Sopenharmony_ci *
6078c2ecf20Sopenharmony_ci */
6088c2ecf20Sopenharmony_cistatic int calipso_map_cat_hton(const struct calipso_doi *doi_def,
6098c2ecf20Sopenharmony_ci				const struct netlbl_lsm_secattr *secattr,
6108c2ecf20Sopenharmony_ci				unsigned char *net_cat,
6118c2ecf20Sopenharmony_ci				u32 net_cat_len)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	int spot = -1;
6148c2ecf20Sopenharmony_ci	u32 net_spot_max = 0;
6158c2ecf20Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	for (;;) {
6188c2ecf20Sopenharmony_ci		spot = netlbl_catmap_walk(secattr->attr.mls.cat,
6198c2ecf20Sopenharmony_ci					  spot + 1);
6208c2ecf20Sopenharmony_ci		if (spot < 0)
6218c2ecf20Sopenharmony_ci			break;
6228c2ecf20Sopenharmony_ci		if (spot >= net_clen_bits)
6238c2ecf20Sopenharmony_ci			return -ENOSPC;
6248c2ecf20Sopenharmony_ci		netlbl_bitmap_setbit(net_cat, spot, 1);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		if (spot > net_spot_max)
6278c2ecf20Sopenharmony_ci			net_spot_max = spot;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	return (net_spot_max / 32 + 1) * 4;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/**
6348c2ecf20Sopenharmony_ci * calipso_map_cat_ntoh - Perform a category mapping from network to host
6358c2ecf20Sopenharmony_ci * @doi_def: the DOI definition
6368c2ecf20Sopenharmony_ci * @net_cat: the category bitmap in network/CALIPSO format
6378c2ecf20Sopenharmony_ci * @net_cat_len: the length of the CALIPSO bitmap in bytes
6388c2ecf20Sopenharmony_ci * @secattr: the security attributes
6398c2ecf20Sopenharmony_ci *
6408c2ecf20Sopenharmony_ci * Description:
6418c2ecf20Sopenharmony_ci * Perform a label mapping to translate a CALIPSO bitmap to the correct local
6428c2ecf20Sopenharmony_ci * MLS category bitmap using the given DOI definition.  Returns zero on
6438c2ecf20Sopenharmony_ci * success, negative values on failure.
6448c2ecf20Sopenharmony_ci *
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_cistatic int calipso_map_cat_ntoh(const struct calipso_doi *doi_def,
6478c2ecf20Sopenharmony_ci				const unsigned char *net_cat,
6488c2ecf20Sopenharmony_ci				u32 net_cat_len,
6498c2ecf20Sopenharmony_ci				struct netlbl_lsm_secattr *secattr)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	int ret_val;
6528c2ecf20Sopenharmony_ci	int spot = -1;
6538c2ecf20Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	for (;;) {
6568c2ecf20Sopenharmony_ci		spot = netlbl_bitmap_walk(net_cat,
6578c2ecf20Sopenharmony_ci					  net_clen_bits,
6588c2ecf20Sopenharmony_ci					  spot + 1,
6598c2ecf20Sopenharmony_ci					  1);
6608c2ecf20Sopenharmony_ci		if (spot < 0) {
6618c2ecf20Sopenharmony_ci			if (spot == -2)
6628c2ecf20Sopenharmony_ci				return -EFAULT;
6638c2ecf20Sopenharmony_ci			return 0;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
6678c2ecf20Sopenharmony_ci					       spot,
6688c2ecf20Sopenharmony_ci					       GFP_ATOMIC);
6698c2ecf20Sopenharmony_ci		if (ret_val != 0)
6708c2ecf20Sopenharmony_ci			return ret_val;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return -EINVAL;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/**
6778c2ecf20Sopenharmony_ci * calipso_pad_write - Writes pad bytes in TLV format
6788c2ecf20Sopenharmony_ci * @buf: the buffer
6798c2ecf20Sopenharmony_ci * @offset: offset from start of buffer to write padding
6808c2ecf20Sopenharmony_ci * @count: number of pad bytes to write
6818c2ecf20Sopenharmony_ci *
6828c2ecf20Sopenharmony_ci * Description:
6838c2ecf20Sopenharmony_ci * Write @count bytes of TLV padding into @buffer starting at offset @offset.
6848c2ecf20Sopenharmony_ci * @count should be less than 8 - see RFC 4942.
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_cistatic int calipso_pad_write(unsigned char *buf, unsigned int offset,
6888c2ecf20Sopenharmony_ci			     unsigned int count)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(count >= 8))
6918c2ecf20Sopenharmony_ci		return -EINVAL;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	switch (count) {
6948c2ecf20Sopenharmony_ci	case 0:
6958c2ecf20Sopenharmony_ci		break;
6968c2ecf20Sopenharmony_ci	case 1:
6978c2ecf20Sopenharmony_ci		buf[offset] = IPV6_TLV_PAD1;
6988c2ecf20Sopenharmony_ci		break;
6998c2ecf20Sopenharmony_ci	default:
7008c2ecf20Sopenharmony_ci		buf[offset] = IPV6_TLV_PADN;
7018c2ecf20Sopenharmony_ci		buf[offset + 1] = count - 2;
7028c2ecf20Sopenharmony_ci		if (count > 2)
7038c2ecf20Sopenharmony_ci			memset(buf + offset + 2, 0, count - 2);
7048c2ecf20Sopenharmony_ci		break;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/**
7108c2ecf20Sopenharmony_ci * calipso_genopt - Generate a CALIPSO option
7118c2ecf20Sopenharmony_ci * @buf: the option buffer
7128c2ecf20Sopenharmony_ci * @start: offset from which to write
7138c2ecf20Sopenharmony_ci * @buf_len: the size of opt_buf
7148c2ecf20Sopenharmony_ci * @doi_def: the CALIPSO DOI to use
7158c2ecf20Sopenharmony_ci * @secattr: the security attributes
7168c2ecf20Sopenharmony_ci *
7178c2ecf20Sopenharmony_ci * Description:
7188c2ecf20Sopenharmony_ci * Generate a CALIPSO option using the DOI definition and security attributes
7198c2ecf20Sopenharmony_ci * passed to the function. This also generates upto three bytes of leading
7208c2ecf20Sopenharmony_ci * padding that ensures that the option is 4n + 2 aligned.  It returns the
7218c2ecf20Sopenharmony_ci * number of bytes written (including any initial padding).
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_cistatic int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len,
7248c2ecf20Sopenharmony_ci			  const struct calipso_doi *doi_def,
7258c2ecf20Sopenharmony_ci			  const struct netlbl_lsm_secattr *secattr)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	int ret_val;
7288c2ecf20Sopenharmony_ci	u32 len, pad;
7298c2ecf20Sopenharmony_ci	u16 crc;
7308c2ecf20Sopenharmony_ci	static const unsigned char padding[4] = {2, 1, 0, 3};
7318c2ecf20Sopenharmony_ci	unsigned char *calipso;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* CALIPSO has 4n + 2 alignment */
7348c2ecf20Sopenharmony_ci	pad = padding[start & 3];
7358c2ecf20Sopenharmony_ci	if (buf_len <= start + pad + CALIPSO_HDR_LEN)
7368c2ecf20Sopenharmony_ci		return -ENOSPC;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
7398c2ecf20Sopenharmony_ci		return -EPERM;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	len = CALIPSO_HDR_LEN;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
7448c2ecf20Sopenharmony_ci		ret_val = calipso_map_cat_hton(doi_def,
7458c2ecf20Sopenharmony_ci					       secattr,
7468c2ecf20Sopenharmony_ci					       buf + start + pad + len,
7478c2ecf20Sopenharmony_ci					       buf_len - start - pad - len);
7488c2ecf20Sopenharmony_ci		if (ret_val < 0)
7498c2ecf20Sopenharmony_ci			return ret_val;
7508c2ecf20Sopenharmony_ci		len += ret_val;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	calipso_pad_write(buf, start, pad);
7548c2ecf20Sopenharmony_ci	calipso = buf + start + pad;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	calipso[0] = IPV6_TLV_CALIPSO;
7578c2ecf20Sopenharmony_ci	calipso[1] = len - 2;
7588c2ecf20Sopenharmony_ci	*(__be32 *)(calipso + 2) = htonl(doi_def->doi);
7598c2ecf20Sopenharmony_ci	calipso[6] = (len - CALIPSO_HDR_LEN) / 4;
7608c2ecf20Sopenharmony_ci	calipso[7] = secattr->attr.mls.lvl;
7618c2ecf20Sopenharmony_ci	crc = ~crc_ccitt(0xffff, calipso, len);
7628c2ecf20Sopenharmony_ci	calipso[8] = crc & 0xff;
7638c2ecf20Sopenharmony_ci	calipso[9] = (crc >> 8) & 0xff;
7648c2ecf20Sopenharmony_ci	return pad + len;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci/* Hop-by-hop hdr helper functions
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/**
7718c2ecf20Sopenharmony_ci * calipso_opt_update - Replaces socket's hop options with a new set
7728c2ecf20Sopenharmony_ci * @sk: the socket
7738c2ecf20Sopenharmony_ci * @hop: new hop options
7748c2ecf20Sopenharmony_ci *
7758c2ecf20Sopenharmony_ci * Description:
7768c2ecf20Sopenharmony_ci * Replaces @sk's hop options with @hop.  @hop may be NULL to leave
7778c2ecf20Sopenharmony_ci * the socket with no hop options.
7788c2ecf20Sopenharmony_ci *
7798c2ecf20Sopenharmony_ci */
7808c2ecf20Sopenharmony_cistatic int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	txopts = ipv6_renew_options(sk, old, IPV6_HOPOPTS, hop);
7858c2ecf20Sopenharmony_ci	txopt_put(old);
7868c2ecf20Sopenharmony_ci	if (IS_ERR(txopts))
7878c2ecf20Sopenharmony_ci		return PTR_ERR(txopts);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	txopts = ipv6_update_options(sk, txopts);
7908c2ecf20Sopenharmony_ci	if (txopts) {
7918c2ecf20Sopenharmony_ci		atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
7928c2ecf20Sopenharmony_ci		txopt_put(txopts);
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return 0;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/**
7998c2ecf20Sopenharmony_ci * calipso_tlv_len - Returns the length of the TLV
8008c2ecf20Sopenharmony_ci * @opt: the option header
8018c2ecf20Sopenharmony_ci * @offset: offset of the TLV within the header
8028c2ecf20Sopenharmony_ci *
8038c2ecf20Sopenharmony_ci * Description:
8048c2ecf20Sopenharmony_ci * Returns the length of the TLV option at offset @offset within
8058c2ecf20Sopenharmony_ci * the option header @opt.  Checks that the entire TLV fits inside
8068c2ecf20Sopenharmony_ci * the option header, returns a negative value if this is not the case.
8078c2ecf20Sopenharmony_ci */
8088c2ecf20Sopenharmony_cistatic int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	unsigned char *tlv = (unsigned char *)opt;
8118c2ecf20Sopenharmony_ci	unsigned int opt_len = ipv6_optlen(opt), tlv_len;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (offset < sizeof(*opt) || offset >= opt_len)
8148c2ecf20Sopenharmony_ci		return -EINVAL;
8158c2ecf20Sopenharmony_ci	if (tlv[offset] == IPV6_TLV_PAD1)
8168c2ecf20Sopenharmony_ci		return 1;
8178c2ecf20Sopenharmony_ci	if (offset + 1 >= opt_len)
8188c2ecf20Sopenharmony_ci		return -EINVAL;
8198c2ecf20Sopenharmony_ci	tlv_len = tlv[offset + 1] + 2;
8208c2ecf20Sopenharmony_ci	if (offset + tlv_len > opt_len)
8218c2ecf20Sopenharmony_ci		return -EINVAL;
8228c2ecf20Sopenharmony_ci	return tlv_len;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/**
8268c2ecf20Sopenharmony_ci * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header
8278c2ecf20Sopenharmony_ci * @hop: the hop options header
8288c2ecf20Sopenharmony_ci * @start: on return holds the offset of any leading padding
8298c2ecf20Sopenharmony_ci * @end: on return holds the offset of the first non-pad TLV after CALIPSO
8308c2ecf20Sopenharmony_ci *
8318c2ecf20Sopenharmony_ci * Description:
8328c2ecf20Sopenharmony_ci * Finds the space occupied by a CALIPSO option (including any leading and
8338c2ecf20Sopenharmony_ci * trailing padding).
8348c2ecf20Sopenharmony_ci *
8358c2ecf20Sopenharmony_ci * If a CALIPSO option exists set @start and @end to the
8368c2ecf20Sopenharmony_ci * offsets within @hop of the start of padding before the first
8378c2ecf20Sopenharmony_ci * CALIPSO option and the end of padding after the first CALIPSO
8388c2ecf20Sopenharmony_ci * option.  In this case the function returns 0.
8398c2ecf20Sopenharmony_ci *
8408c2ecf20Sopenharmony_ci * In the absence of a CALIPSO option, @start and @end will be
8418c2ecf20Sopenharmony_ci * set to the start and end of any trailing padding in the header.
8428c2ecf20Sopenharmony_ci * This is useful when appending a new option, as the caller may want
8438c2ecf20Sopenharmony_ci * to overwrite some of this padding.  In this case the function will
8448c2ecf20Sopenharmony_ci * return -ENOENT.
8458c2ecf20Sopenharmony_ci */
8468c2ecf20Sopenharmony_cistatic int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start,
8478c2ecf20Sopenharmony_ci			    unsigned int *end)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	int ret_val = -ENOENT, tlv_len;
8508c2ecf20Sopenharmony_ci	unsigned int opt_len, offset, offset_s = 0, offset_e = 0;
8518c2ecf20Sopenharmony_ci	unsigned char *opt = (unsigned char *)hop;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	opt_len = ipv6_optlen(hop);
8548c2ecf20Sopenharmony_ci	offset = sizeof(*hop);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	while (offset < opt_len) {
8578c2ecf20Sopenharmony_ci		tlv_len = calipso_tlv_len(hop, offset);
8588c2ecf20Sopenharmony_ci		if (tlv_len < 0)
8598c2ecf20Sopenharmony_ci			return tlv_len;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		switch (opt[offset]) {
8628c2ecf20Sopenharmony_ci		case IPV6_TLV_PAD1:
8638c2ecf20Sopenharmony_ci		case IPV6_TLV_PADN:
8648c2ecf20Sopenharmony_ci			if (offset_e)
8658c2ecf20Sopenharmony_ci				offset_e = offset;
8668c2ecf20Sopenharmony_ci			break;
8678c2ecf20Sopenharmony_ci		case IPV6_TLV_CALIPSO:
8688c2ecf20Sopenharmony_ci			ret_val = 0;
8698c2ecf20Sopenharmony_ci			offset_e = offset;
8708c2ecf20Sopenharmony_ci			break;
8718c2ecf20Sopenharmony_ci		default:
8728c2ecf20Sopenharmony_ci			if (offset_e == 0)
8738c2ecf20Sopenharmony_ci				offset_s = offset;
8748c2ecf20Sopenharmony_ci			else
8758c2ecf20Sopenharmony_ci				goto out;
8768c2ecf20Sopenharmony_ci		}
8778c2ecf20Sopenharmony_ci		offset += tlv_len;
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ciout:
8818c2ecf20Sopenharmony_ci	if (offset_s)
8828c2ecf20Sopenharmony_ci		*start = offset_s + calipso_tlv_len(hop, offset_s);
8838c2ecf20Sopenharmony_ci	else
8848c2ecf20Sopenharmony_ci		*start = sizeof(*hop);
8858c2ecf20Sopenharmony_ci	if (offset_e)
8868c2ecf20Sopenharmony_ci		*end = offset_e + calipso_tlv_len(hop, offset_e);
8878c2ecf20Sopenharmony_ci	else
8888c2ecf20Sopenharmony_ci		*end = opt_len;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	return ret_val;
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci/**
8948c2ecf20Sopenharmony_ci * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr
8958c2ecf20Sopenharmony_ci * @hop: the original hop options header
8968c2ecf20Sopenharmony_ci * @doi_def: the CALIPSO DOI to use
8978c2ecf20Sopenharmony_ci * @secattr: the specific security attributes of the socket
8988c2ecf20Sopenharmony_ci *
8998c2ecf20Sopenharmony_ci * Description:
9008c2ecf20Sopenharmony_ci * Creates a new hop options header based on @hop with a
9018c2ecf20Sopenharmony_ci * CALIPSO option added to it.  If @hop already contains a CALIPSO
9028c2ecf20Sopenharmony_ci * option this is overwritten, otherwise the new option is appended
9038c2ecf20Sopenharmony_ci * after any existing options.  If @hop is NULL then the new header
9048c2ecf20Sopenharmony_ci * will contain just the CALIPSO option and any needed padding.
9058c2ecf20Sopenharmony_ci *
9068c2ecf20Sopenharmony_ci */
9078c2ecf20Sopenharmony_cistatic struct ipv6_opt_hdr *
9088c2ecf20Sopenharmony_cicalipso_opt_insert(struct ipv6_opt_hdr *hop,
9098c2ecf20Sopenharmony_ci		   const struct calipso_doi *doi_def,
9108c2ecf20Sopenharmony_ci		   const struct netlbl_lsm_secattr *secattr)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	unsigned int start, end, buf_len, pad, hop_len;
9138c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *new;
9148c2ecf20Sopenharmony_ci	int ret_val;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (hop) {
9178c2ecf20Sopenharmony_ci		hop_len = ipv6_optlen(hop);
9188c2ecf20Sopenharmony_ci		ret_val = calipso_opt_find(hop, &start, &end);
9198c2ecf20Sopenharmony_ci		if (ret_val && ret_val != -ENOENT)
9208c2ecf20Sopenharmony_ci			return ERR_PTR(ret_val);
9218c2ecf20Sopenharmony_ci	} else {
9228c2ecf20Sopenharmony_ci		hop_len = 0;
9238c2ecf20Sopenharmony_ci		start = sizeof(*hop);
9248c2ecf20Sopenharmony_ci		end = 0;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD;
9288c2ecf20Sopenharmony_ci	new = kzalloc(buf_len, GFP_ATOMIC);
9298c2ecf20Sopenharmony_ci	if (!new)
9308c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	if (start > sizeof(*hop))
9338c2ecf20Sopenharmony_ci		memcpy(new, hop, start);
9348c2ecf20Sopenharmony_ci	ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def,
9358c2ecf20Sopenharmony_ci				 secattr);
9368c2ecf20Sopenharmony_ci	if (ret_val < 0) {
9378c2ecf20Sopenharmony_ci		kfree(new);
9388c2ecf20Sopenharmony_ci		return ERR_PTR(ret_val);
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	buf_len = start + ret_val;
9428c2ecf20Sopenharmony_ci	/* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */
9438c2ecf20Sopenharmony_ci	pad = ((buf_len & 4) + (end & 7)) & 7;
9448c2ecf20Sopenharmony_ci	calipso_pad_write((unsigned char *)new, buf_len, pad);
9458c2ecf20Sopenharmony_ci	buf_len += pad;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	if (end != hop_len) {
9488c2ecf20Sopenharmony_ci		memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end);
9498c2ecf20Sopenharmony_ci		buf_len += hop_len - end;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci	new->nexthdr = 0;
9528c2ecf20Sopenharmony_ci	new->hdrlen = buf_len / 8 - 1;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return new;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/**
9588c2ecf20Sopenharmony_ci * calipso_opt_del - Removes the CALIPSO option from an option header
9598c2ecf20Sopenharmony_ci * @hop: the original header
9608c2ecf20Sopenharmony_ci * @new: the new header
9618c2ecf20Sopenharmony_ci *
9628c2ecf20Sopenharmony_ci * Description:
9638c2ecf20Sopenharmony_ci * Creates a new header based on @hop without any CALIPSO option.  If @hop
9648c2ecf20Sopenharmony_ci * doesn't contain a CALIPSO option it returns -ENOENT.  If @hop contains
9658c2ecf20Sopenharmony_ci * no other non-padding options, it returns zero with @new set to NULL.
9668c2ecf20Sopenharmony_ci * Otherwise it returns zero, creates a new header without the CALIPSO
9678c2ecf20Sopenharmony_ci * option (and removing as much padding as possible) and returns with
9688c2ecf20Sopenharmony_ci * @new set to that header.
9698c2ecf20Sopenharmony_ci *
9708c2ecf20Sopenharmony_ci */
9718c2ecf20Sopenharmony_cistatic int calipso_opt_del(struct ipv6_opt_hdr *hop,
9728c2ecf20Sopenharmony_ci			   struct ipv6_opt_hdr **new)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	int ret_val;
9758c2ecf20Sopenharmony_ci	unsigned int start, end, delta, pad, hop_len;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	ret_val = calipso_opt_find(hop, &start, &end);
9788c2ecf20Sopenharmony_ci	if (ret_val)
9798c2ecf20Sopenharmony_ci		return ret_val;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	hop_len = ipv6_optlen(hop);
9828c2ecf20Sopenharmony_ci	if (start == sizeof(*hop) && end == hop_len) {
9838c2ecf20Sopenharmony_ci		/* There's no other option in the header so return NULL */
9848c2ecf20Sopenharmony_ci		*new = NULL;
9858c2ecf20Sopenharmony_ci		return 0;
9868c2ecf20Sopenharmony_ci	}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	delta = (end - start) & ~7;
9898c2ecf20Sopenharmony_ci	*new = kzalloc(hop_len - delta, GFP_ATOMIC);
9908c2ecf20Sopenharmony_ci	if (!*new)
9918c2ecf20Sopenharmony_ci		return -ENOMEM;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	memcpy(*new, hop, start);
9948c2ecf20Sopenharmony_ci	(*new)->hdrlen -= delta / 8;
9958c2ecf20Sopenharmony_ci	pad = (end - start) & 7;
9968c2ecf20Sopenharmony_ci	calipso_pad_write((unsigned char *)*new, start, pad);
9978c2ecf20Sopenharmony_ci	if (end != hop_len)
9988c2ecf20Sopenharmony_ci		memcpy((char *)*new + start + pad, (char *)hop + end,
9998c2ecf20Sopenharmony_ci		       hop_len - end);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/**
10058c2ecf20Sopenharmony_ci * calipso_opt_getattr - Get the security attributes from a memory block
10068c2ecf20Sopenharmony_ci * @calipso: the CALIPSO option
10078c2ecf20Sopenharmony_ci * @secattr: the security attributes
10088c2ecf20Sopenharmony_ci *
10098c2ecf20Sopenharmony_ci * Description:
10108c2ecf20Sopenharmony_ci * Inspect @calipso and return the security attributes in @secattr.
10118c2ecf20Sopenharmony_ci * Returns zero on success and negative values on failure.
10128c2ecf20Sopenharmony_ci *
10138c2ecf20Sopenharmony_ci */
10148c2ecf20Sopenharmony_cistatic int calipso_opt_getattr(const unsigned char *calipso,
10158c2ecf20Sopenharmony_ci			       struct netlbl_lsm_secattr *secattr)
10168c2ecf20Sopenharmony_ci{
10178c2ecf20Sopenharmony_ci	int ret_val = -ENOMSG;
10188c2ecf20Sopenharmony_ci	u32 doi, len = calipso[1], cat_len = calipso[6] * 4;
10198c2ecf20Sopenharmony_ci	struct calipso_doi *doi_def;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (cat_len + 8 > len)
10228c2ecf20Sopenharmony_ci		return -EINVAL;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0)
10258c2ecf20Sopenharmony_ci		return 0;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	doi = get_unaligned_be32(calipso + 2);
10288c2ecf20Sopenharmony_ci	rcu_read_lock();
10298c2ecf20Sopenharmony_ci	doi_def = calipso_doi_search(doi);
10308c2ecf20Sopenharmony_ci	if (!doi_def)
10318c2ecf20Sopenharmony_ci		goto getattr_return;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	secattr->attr.mls.lvl = calipso[7];
10348c2ecf20Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	if (cat_len) {
10378c2ecf20Sopenharmony_ci		ret_val = calipso_map_cat_ntoh(doi_def,
10388c2ecf20Sopenharmony_ci					       calipso + 10,
10398c2ecf20Sopenharmony_ci					       cat_len,
10408c2ecf20Sopenharmony_ci					       secattr);
10418c2ecf20Sopenharmony_ci		if (ret_val != 0) {
10428c2ecf20Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
10438c2ecf20Sopenharmony_ci			goto getattr_return;
10448c2ecf20Sopenharmony_ci		}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci		if (secattr->attr.mls.cat)
10478c2ecf20Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	secattr->type = NETLBL_NLTYPE_CALIPSO;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cigetattr_return:
10538c2ecf20Sopenharmony_ci	rcu_read_unlock();
10548c2ecf20Sopenharmony_ci	return ret_val;
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/* sock functions.
10588c2ecf20Sopenharmony_ci */
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci/**
10618c2ecf20Sopenharmony_ci * calipso_sock_getattr - Get the security attributes from a sock
10628c2ecf20Sopenharmony_ci * @sk: the sock
10638c2ecf20Sopenharmony_ci * @secattr: the security attributes
10648c2ecf20Sopenharmony_ci *
10658c2ecf20Sopenharmony_ci * Description:
10668c2ecf20Sopenharmony_ci * Query @sk to see if there is a CALIPSO option attached to the sock and if
10678c2ecf20Sopenharmony_ci * there is return the CALIPSO security attributes in @secattr.  This function
10688c2ecf20Sopenharmony_ci * requires that @sk be locked, or privately held, but it does not do any
10698c2ecf20Sopenharmony_ci * locking itself.  Returns zero on success and negative values on failure.
10708c2ecf20Sopenharmony_ci *
10718c2ecf20Sopenharmony_ci */
10728c2ecf20Sopenharmony_cistatic int calipso_sock_getattr(struct sock *sk,
10738c2ecf20Sopenharmony_ci				struct netlbl_lsm_secattr *secattr)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *hop;
10768c2ecf20Sopenharmony_ci	int opt_len, len, ret_val = -ENOMSG, offset;
10778c2ecf20Sopenharmony_ci	unsigned char *opt;
10788c2ecf20Sopenharmony_ci	struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	if (!txopts || !txopts->hopopt)
10818c2ecf20Sopenharmony_ci		goto done;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	hop = txopts->hopopt;
10848c2ecf20Sopenharmony_ci	opt = (unsigned char *)hop;
10858c2ecf20Sopenharmony_ci	opt_len = ipv6_optlen(hop);
10868c2ecf20Sopenharmony_ci	offset = sizeof(*hop);
10878c2ecf20Sopenharmony_ci	while (offset < opt_len) {
10888c2ecf20Sopenharmony_ci		len = calipso_tlv_len(hop, offset);
10898c2ecf20Sopenharmony_ci		if (len < 0) {
10908c2ecf20Sopenharmony_ci			ret_val = len;
10918c2ecf20Sopenharmony_ci			goto done;
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci		switch (opt[offset]) {
10948c2ecf20Sopenharmony_ci		case IPV6_TLV_CALIPSO:
10958c2ecf20Sopenharmony_ci			if (len < CALIPSO_HDR_LEN)
10968c2ecf20Sopenharmony_ci				ret_val = -EINVAL;
10978c2ecf20Sopenharmony_ci			else
10988c2ecf20Sopenharmony_ci				ret_val = calipso_opt_getattr(&opt[offset],
10998c2ecf20Sopenharmony_ci							      secattr);
11008c2ecf20Sopenharmony_ci			goto done;
11018c2ecf20Sopenharmony_ci		default:
11028c2ecf20Sopenharmony_ci			offset += len;
11038c2ecf20Sopenharmony_ci			break;
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_cidone:
11078c2ecf20Sopenharmony_ci	txopt_put(txopts);
11088c2ecf20Sopenharmony_ci	return ret_val;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci/**
11128c2ecf20Sopenharmony_ci * calipso_sock_setattr - Add a CALIPSO option to a socket
11138c2ecf20Sopenharmony_ci * @sk: the socket
11148c2ecf20Sopenharmony_ci * @doi_def: the CALIPSO DOI to use
11158c2ecf20Sopenharmony_ci * @secattr: the specific security attributes of the socket
11168c2ecf20Sopenharmony_ci *
11178c2ecf20Sopenharmony_ci * Description:
11188c2ecf20Sopenharmony_ci * Set the CALIPSO option on the given socket using the DOI definition and
11198c2ecf20Sopenharmony_ci * security attributes passed to the function.  This function requires
11208c2ecf20Sopenharmony_ci * exclusive access to @sk, which means it either needs to be in the
11218c2ecf20Sopenharmony_ci * process of being created or locked.  Returns zero on success and negative
11228c2ecf20Sopenharmony_ci * values on failure.
11238c2ecf20Sopenharmony_ci *
11248c2ecf20Sopenharmony_ci */
11258c2ecf20Sopenharmony_cistatic int calipso_sock_setattr(struct sock *sk,
11268c2ecf20Sopenharmony_ci				const struct calipso_doi *doi_def,
11278c2ecf20Sopenharmony_ci				const struct netlbl_lsm_secattr *secattr)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	int ret_val;
11308c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *old, *new;
11318c2ecf20Sopenharmony_ci	struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	old = NULL;
11348c2ecf20Sopenharmony_ci	if (txopts)
11358c2ecf20Sopenharmony_ci		old = txopts->hopopt;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	new = calipso_opt_insert(old, doi_def, secattr);
11388c2ecf20Sopenharmony_ci	txopt_put(txopts);
11398c2ecf20Sopenharmony_ci	if (IS_ERR(new))
11408c2ecf20Sopenharmony_ci		return PTR_ERR(new);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	ret_val = calipso_opt_update(sk, new);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	kfree(new);
11458c2ecf20Sopenharmony_ci	return ret_val;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci/**
11498c2ecf20Sopenharmony_ci * calipso_sock_delattr - Delete the CALIPSO option from a socket
11508c2ecf20Sopenharmony_ci * @sk: the socket
11518c2ecf20Sopenharmony_ci *
11528c2ecf20Sopenharmony_ci * Description:
11538c2ecf20Sopenharmony_ci * Removes the CALIPSO option from a socket, if present.
11548c2ecf20Sopenharmony_ci *
11558c2ecf20Sopenharmony_ci */
11568c2ecf20Sopenharmony_cistatic void calipso_sock_delattr(struct sock *sk)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *new_hop;
11598c2ecf20Sopenharmony_ci	struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (!txopts || !txopts->hopopt)
11628c2ecf20Sopenharmony_ci		goto done;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	if (calipso_opt_del(txopts->hopopt, &new_hop))
11658c2ecf20Sopenharmony_ci		goto done;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	calipso_opt_update(sk, new_hop);
11688c2ecf20Sopenharmony_ci	kfree(new_hop);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cidone:
11718c2ecf20Sopenharmony_ci	txopt_put(txopts);
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci/* request sock functions.
11758c2ecf20Sopenharmony_ci */
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci/**
11788c2ecf20Sopenharmony_ci * calipso_req_setattr - Add a CALIPSO option to a connection request socket
11798c2ecf20Sopenharmony_ci * @req: the connection request socket
11808c2ecf20Sopenharmony_ci * @doi_def: the CALIPSO DOI to use
11818c2ecf20Sopenharmony_ci * @secattr: the specific security attributes of the socket
11828c2ecf20Sopenharmony_ci *
11838c2ecf20Sopenharmony_ci * Description:
11848c2ecf20Sopenharmony_ci * Set the CALIPSO option on the given socket using the DOI definition and
11858c2ecf20Sopenharmony_ci * security attributes passed to the function.  Returns zero on success and
11868c2ecf20Sopenharmony_ci * negative values on failure.
11878c2ecf20Sopenharmony_ci *
11888c2ecf20Sopenharmony_ci */
11898c2ecf20Sopenharmony_cistatic int calipso_req_setattr(struct request_sock *req,
11908c2ecf20Sopenharmony_ci			       const struct calipso_doi *doi_def,
11918c2ecf20Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	struct ipv6_txoptions *txopts;
11948c2ecf20Sopenharmony_ci	struct inet_request_sock *req_inet = inet_rsk(req);
11958c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *old, *new;
11968c2ecf20Sopenharmony_ci	struct sock *sk = sk_to_full_sk(req_to_sk(req));
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt)
11998c2ecf20Sopenharmony_ci		old = req_inet->ipv6_opt->hopopt;
12008c2ecf20Sopenharmony_ci	else
12018c2ecf20Sopenharmony_ci		old = NULL;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	new = calipso_opt_insert(old, doi_def, secattr);
12048c2ecf20Sopenharmony_ci	if (IS_ERR(new))
12058c2ecf20Sopenharmony_ci		return PTR_ERR(new);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	kfree(new);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	if (IS_ERR(txopts))
12128c2ecf20Sopenharmony_ci		return PTR_ERR(txopts);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	txopts = xchg(&req_inet->ipv6_opt, txopts);
12158c2ecf20Sopenharmony_ci	if (txopts) {
12168c2ecf20Sopenharmony_ci		atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
12178c2ecf20Sopenharmony_ci		txopt_put(txopts);
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	return 0;
12218c2ecf20Sopenharmony_ci}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci/**
12248c2ecf20Sopenharmony_ci * calipso_req_delattr - Delete the CALIPSO option from a request socket
12258c2ecf20Sopenharmony_ci * @reg: the request socket
12268c2ecf20Sopenharmony_ci *
12278c2ecf20Sopenharmony_ci * Description:
12288c2ecf20Sopenharmony_ci * Removes the CALIPSO option from a request socket, if present.
12298c2ecf20Sopenharmony_ci *
12308c2ecf20Sopenharmony_ci */
12318c2ecf20Sopenharmony_cistatic void calipso_req_delattr(struct request_sock *req)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	struct inet_request_sock *req_inet = inet_rsk(req);
12348c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *new;
12358c2ecf20Sopenharmony_ci	struct ipv6_txoptions *txopts;
12368c2ecf20Sopenharmony_ci	struct sock *sk = sk_to_full_sk(req_to_sk(req));
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt)
12398c2ecf20Sopenharmony_ci		return;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new))
12428c2ecf20Sopenharmony_ci		return; /* Nothing to do */
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	if (!IS_ERR(txopts)) {
12478c2ecf20Sopenharmony_ci		txopts = xchg(&req_inet->ipv6_opt, txopts);
12488c2ecf20Sopenharmony_ci		if (txopts) {
12498c2ecf20Sopenharmony_ci			atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
12508c2ecf20Sopenharmony_ci			txopt_put(txopts);
12518c2ecf20Sopenharmony_ci		}
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci	kfree(new);
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci/* skbuff functions.
12578c2ecf20Sopenharmony_ci */
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci/**
12608c2ecf20Sopenharmony_ci * calipso_skbuff_optptr - Find the CALIPSO option in the packet
12618c2ecf20Sopenharmony_ci * @skb: the packet
12628c2ecf20Sopenharmony_ci *
12638c2ecf20Sopenharmony_ci * Description:
12648c2ecf20Sopenharmony_ci * Parse the packet's IP header looking for a CALIPSO option.  Returns a pointer
12658c2ecf20Sopenharmony_ci * to the start of the CALIPSO option on success, NULL if one if not found.
12668c2ecf20Sopenharmony_ci *
12678c2ecf20Sopenharmony_ci */
12688c2ecf20Sopenharmony_cistatic unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb)
12698c2ecf20Sopenharmony_ci{
12708c2ecf20Sopenharmony_ci	const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb);
12718c2ecf20Sopenharmony_ci	int offset;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	if (ip6_hdr->nexthdr != NEXTHDR_HOP)
12748c2ecf20Sopenharmony_ci		return NULL;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO);
12778c2ecf20Sopenharmony_ci	if (offset >= 0)
12788c2ecf20Sopenharmony_ci		return (unsigned char *)ip6_hdr + offset;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	return NULL;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci/**
12848c2ecf20Sopenharmony_ci * calipso_skbuff_setattr - Set the CALIPSO option on a packet
12858c2ecf20Sopenharmony_ci * @skb: the packet
12868c2ecf20Sopenharmony_ci * @doi_def: the CALIPSO DOI to use
12878c2ecf20Sopenharmony_ci * @secattr: the security attributes
12888c2ecf20Sopenharmony_ci *
12898c2ecf20Sopenharmony_ci * Description:
12908c2ecf20Sopenharmony_ci * Set the CALIPSO option on the given packet based on the security attributes.
12918c2ecf20Sopenharmony_ci * Returns a pointer to the IP header on success and NULL on failure.
12928c2ecf20Sopenharmony_ci *
12938c2ecf20Sopenharmony_ci */
12948c2ecf20Sopenharmony_cistatic int calipso_skbuff_setattr(struct sk_buff *skb,
12958c2ecf20Sopenharmony_ci				  const struct calipso_doi *doi_def,
12968c2ecf20Sopenharmony_ci				  const struct netlbl_lsm_secattr *secattr)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	int ret_val;
12998c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6_hdr;
13008c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *hop;
13018c2ecf20Sopenharmony_ci	unsigned char buf[CALIPSO_MAX_BUFFER];
13028c2ecf20Sopenharmony_ci	int len_delta, new_end, pad, payload;
13038c2ecf20Sopenharmony_ci	unsigned int start, end;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	ip6_hdr = ipv6_hdr(skb);
13068c2ecf20Sopenharmony_ci	if (ip6_hdr->nexthdr == NEXTHDR_HOP) {
13078c2ecf20Sopenharmony_ci		hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
13088c2ecf20Sopenharmony_ci		ret_val = calipso_opt_find(hop, &start, &end);
13098c2ecf20Sopenharmony_ci		if (ret_val && ret_val != -ENOENT)
13108c2ecf20Sopenharmony_ci			return ret_val;
13118c2ecf20Sopenharmony_ci	} else {
13128c2ecf20Sopenharmony_ci		start = 0;
13138c2ecf20Sopenharmony_ci		end = 0;
13148c2ecf20Sopenharmony_ci	}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	memset(buf, 0, sizeof(buf));
13178c2ecf20Sopenharmony_ci	ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr);
13188c2ecf20Sopenharmony_ci	if (ret_val < 0)
13198c2ecf20Sopenharmony_ci		return ret_val;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	new_end = start + ret_val;
13228c2ecf20Sopenharmony_ci	/* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */
13238c2ecf20Sopenharmony_ci	pad = ((new_end & 4) + (end & 7)) & 7;
13248c2ecf20Sopenharmony_ci	len_delta = new_end - (int)end + pad;
13258c2ecf20Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
13268c2ecf20Sopenharmony_ci	if (ret_val < 0)
13278c2ecf20Sopenharmony_ci		return ret_val;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	ip6_hdr = ipv6_hdr(skb); /* Reset as skb_cow() may have moved it */
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	if (len_delta) {
13328c2ecf20Sopenharmony_ci		if (len_delta > 0)
13338c2ecf20Sopenharmony_ci			skb_push(skb, len_delta);
13348c2ecf20Sopenharmony_ci		else
13358c2ecf20Sopenharmony_ci			skb_pull(skb, -len_delta);
13368c2ecf20Sopenharmony_ci		memmove((char *)ip6_hdr - len_delta, ip6_hdr,
13378c2ecf20Sopenharmony_ci			sizeof(*ip6_hdr) + start);
13388c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
13398c2ecf20Sopenharmony_ci		ip6_hdr = ipv6_hdr(skb);
13408c2ecf20Sopenharmony_ci		payload = ntohs(ip6_hdr->payload_len);
13418c2ecf20Sopenharmony_ci		ip6_hdr->payload_len = htons(payload + len_delta);
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
13458c2ecf20Sopenharmony_ci	if (start == 0) {
13468c2ecf20Sopenharmony_ci		struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		new_hop->nexthdr = ip6_hdr->nexthdr;
13498c2ecf20Sopenharmony_ci		new_hop->hdrlen = len_delta / 8 - 1;
13508c2ecf20Sopenharmony_ci		ip6_hdr->nexthdr = NEXTHDR_HOP;
13518c2ecf20Sopenharmony_ci	} else {
13528c2ecf20Sopenharmony_ci		hop->hdrlen += len_delta / 8;
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci	memcpy((char *)hop + start, buf + (start & 3), new_end - start);
13558c2ecf20Sopenharmony_ci	calipso_pad_write((unsigned char *)hop, new_end, pad);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	return 0;
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci/**
13618c2ecf20Sopenharmony_ci * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
13628c2ecf20Sopenharmony_ci * @skb: the packet
13638c2ecf20Sopenharmony_ci *
13648c2ecf20Sopenharmony_ci * Description:
13658c2ecf20Sopenharmony_ci * Removes any and all CALIPSO options from the given packet.  Returns zero on
13668c2ecf20Sopenharmony_ci * success, negative values on failure.
13678c2ecf20Sopenharmony_ci *
13688c2ecf20Sopenharmony_ci */
13698c2ecf20Sopenharmony_cistatic int calipso_skbuff_delattr(struct sk_buff *skb)
13708c2ecf20Sopenharmony_ci{
13718c2ecf20Sopenharmony_ci	int ret_val;
13728c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6_hdr;
13738c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *old_hop;
13748c2ecf20Sopenharmony_ci	u32 old_hop_len, start = 0, end = 0, delta, size, pad;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	if (!calipso_skbuff_optptr(skb))
13778c2ecf20Sopenharmony_ci		return 0;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/* since we are changing the packet we should make a copy */
13808c2ecf20Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb));
13818c2ecf20Sopenharmony_ci	if (ret_val < 0)
13828c2ecf20Sopenharmony_ci		return ret_val;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	ip6_hdr = ipv6_hdr(skb);
13858c2ecf20Sopenharmony_ci	old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
13868c2ecf20Sopenharmony_ci	old_hop_len = ipv6_optlen(old_hop);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	ret_val = calipso_opt_find(old_hop, &start, &end);
13898c2ecf20Sopenharmony_ci	if (ret_val)
13908c2ecf20Sopenharmony_ci		return ret_val;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	if (start == sizeof(*old_hop) && end == old_hop_len) {
13938c2ecf20Sopenharmony_ci		/* There's no other option in the header so we delete
13948c2ecf20Sopenharmony_ci		 * the whole thing. */
13958c2ecf20Sopenharmony_ci		delta = old_hop_len;
13968c2ecf20Sopenharmony_ci		size = sizeof(*ip6_hdr);
13978c2ecf20Sopenharmony_ci		ip6_hdr->nexthdr = old_hop->nexthdr;
13988c2ecf20Sopenharmony_ci	} else {
13998c2ecf20Sopenharmony_ci		delta = (end - start) & ~7;
14008c2ecf20Sopenharmony_ci		if (delta)
14018c2ecf20Sopenharmony_ci			old_hop->hdrlen -= delta / 8;
14028c2ecf20Sopenharmony_ci		pad = (end - start) & 7;
14038c2ecf20Sopenharmony_ci		size = sizeof(*ip6_hdr) + start + pad;
14048c2ecf20Sopenharmony_ci		calipso_pad_write((unsigned char *)old_hop, start, pad);
14058c2ecf20Sopenharmony_ci	}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	if (delta) {
14088c2ecf20Sopenharmony_ci		skb_pull(skb, delta);
14098c2ecf20Sopenharmony_ci		memmove((char *)ip6_hdr + delta, ip6_hdr, size);
14108c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	return 0;
14148c2ecf20Sopenharmony_ci}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_cistatic const struct netlbl_calipso_ops ops = {
14178c2ecf20Sopenharmony_ci	.doi_add          = calipso_doi_add,
14188c2ecf20Sopenharmony_ci	.doi_free         = calipso_doi_free,
14198c2ecf20Sopenharmony_ci	.doi_remove       = calipso_doi_remove,
14208c2ecf20Sopenharmony_ci	.doi_getdef       = calipso_doi_getdef,
14218c2ecf20Sopenharmony_ci	.doi_putdef       = calipso_doi_putdef,
14228c2ecf20Sopenharmony_ci	.doi_walk         = calipso_doi_walk,
14238c2ecf20Sopenharmony_ci	.sock_getattr     = calipso_sock_getattr,
14248c2ecf20Sopenharmony_ci	.sock_setattr     = calipso_sock_setattr,
14258c2ecf20Sopenharmony_ci	.sock_delattr     = calipso_sock_delattr,
14268c2ecf20Sopenharmony_ci	.req_setattr      = calipso_req_setattr,
14278c2ecf20Sopenharmony_ci	.req_delattr      = calipso_req_delattr,
14288c2ecf20Sopenharmony_ci	.opt_getattr      = calipso_opt_getattr,
14298c2ecf20Sopenharmony_ci	.skbuff_optptr    = calipso_skbuff_optptr,
14308c2ecf20Sopenharmony_ci	.skbuff_setattr   = calipso_skbuff_setattr,
14318c2ecf20Sopenharmony_ci	.skbuff_delattr   = calipso_skbuff_delattr,
14328c2ecf20Sopenharmony_ci	.cache_invalidate = calipso_cache_invalidate,
14338c2ecf20Sopenharmony_ci	.cache_add        = calipso_cache_add
14348c2ecf20Sopenharmony_ci};
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/**
14378c2ecf20Sopenharmony_ci * calipso_init - Initialize the CALIPSO module
14388c2ecf20Sopenharmony_ci *
14398c2ecf20Sopenharmony_ci * Description:
14408c2ecf20Sopenharmony_ci * Initialize the CALIPSO module and prepare it for use.  Returns zero on
14418c2ecf20Sopenharmony_ci * success and negative values on failure.
14428c2ecf20Sopenharmony_ci *
14438c2ecf20Sopenharmony_ci */
14448c2ecf20Sopenharmony_ciint __init calipso_init(void)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	int ret_val;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	ret_val = calipso_cache_init();
14498c2ecf20Sopenharmony_ci	if (!ret_val)
14508c2ecf20Sopenharmony_ci		netlbl_calipso_ops_register(&ops);
14518c2ecf20Sopenharmony_ci	return ret_val;
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_civoid calipso_exit(void)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	netlbl_calipso_ops_register(NULL);
14578c2ecf20Sopenharmony_ci	calipso_cache_invalidate();
14588c2ecf20Sopenharmony_ci	kfree(calipso_cache);
14598c2ecf20Sopenharmony_ci}
1460