xref: /linux/net/ipv4/cipso_ipv4.c (revision 91b1ed0afdbffbda88c472ef72af37e19b7876fb)
1446fda4fSPaul Moore /*
2446fda4fSPaul Moore  * CIPSO - Commercial IP Security Option
3446fda4fSPaul Moore  *
4446fda4fSPaul Moore  * This is an implementation of the CIPSO 2.2 protocol as specified in
5446fda4fSPaul Moore  * draft-ietf-cipso-ipsecurity-01.txt with additional tag types as found in
6446fda4fSPaul Moore  * FIPS-188, copies of both documents can be found in the Documentation
7446fda4fSPaul Moore  * directory.  While CIPSO never became a full IETF RFC standard many vendors
8446fda4fSPaul Moore  * have chosen to adopt the protocol and over the years it has become a
9446fda4fSPaul Moore  * de-facto standard for labeled networking.
10446fda4fSPaul Moore  *
11446fda4fSPaul Moore  * Author: Paul Moore <paul.moore@hp.com>
12446fda4fSPaul Moore  *
13446fda4fSPaul Moore  */
14446fda4fSPaul Moore 
15446fda4fSPaul Moore /*
16446fda4fSPaul Moore  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
17446fda4fSPaul Moore  *
18446fda4fSPaul Moore  * This program is free software;  you can redistribute it and/or modify
19446fda4fSPaul Moore  * it under the terms of the GNU General Public License as published by
20446fda4fSPaul Moore  * the Free Software Foundation; either version 2 of the License, or
21446fda4fSPaul Moore  * (at your option) any later version.
22446fda4fSPaul Moore  *
23446fda4fSPaul Moore  * This program is distributed in the hope that it will be useful,
24446fda4fSPaul Moore  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
25446fda4fSPaul Moore  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
26446fda4fSPaul Moore  * the GNU General Public License for more details.
27446fda4fSPaul Moore  *
28446fda4fSPaul Moore  * You should have received a copy of the GNU General Public License
29446fda4fSPaul Moore  * along with this program;  if not, write to the Free Software
30446fda4fSPaul Moore  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31446fda4fSPaul Moore  *
32446fda4fSPaul Moore  */
33446fda4fSPaul Moore 
34446fda4fSPaul Moore #include <linux/init.h>
35446fda4fSPaul Moore #include <linux/types.h>
36446fda4fSPaul Moore #include <linux/rcupdate.h>
37446fda4fSPaul Moore #include <linux/list.h>
38446fda4fSPaul Moore #include <linux/spinlock.h>
39446fda4fSPaul Moore #include <linux/string.h>
40446fda4fSPaul Moore #include <linux/jhash.h>
41446fda4fSPaul Moore #include <net/ip.h>
42446fda4fSPaul Moore #include <net/icmp.h>
43446fda4fSPaul Moore #include <net/tcp.h>
44446fda4fSPaul Moore #include <net/netlabel.h>
45446fda4fSPaul Moore #include <net/cipso_ipv4.h>
46ffb733c6Spaul.moore@hp.com #include <asm/atomic.h>
47446fda4fSPaul Moore #include <asm/bug.h>
48446fda4fSPaul Moore 
49446fda4fSPaul Moore struct cipso_v4_domhsh_entry {
50446fda4fSPaul Moore 	char *domain;
51446fda4fSPaul Moore 	u32 valid;
52446fda4fSPaul Moore 	struct list_head list;
53446fda4fSPaul Moore 	struct rcu_head rcu;
54446fda4fSPaul Moore };
55446fda4fSPaul Moore 
56446fda4fSPaul Moore /* List of available DOI definitions */
57446fda4fSPaul Moore /* XXX - Updates should be minimal so having a single lock for the
58446fda4fSPaul Moore  * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
59446fda4fSPaul Moore  * okay. */
60446fda4fSPaul Moore /* XXX - This currently assumes a minimal number of different DOIs in use,
61446fda4fSPaul Moore  * if in practice there are a lot of different DOIs this list should
62446fda4fSPaul Moore  * probably be turned into a hash table or something similar so we
63446fda4fSPaul Moore  * can do quick lookups. */
648ce11e6aSAdrian Bunk static DEFINE_SPINLOCK(cipso_v4_doi_list_lock);
65446fda4fSPaul Moore static struct list_head cipso_v4_doi_list = LIST_HEAD_INIT(cipso_v4_doi_list);
66446fda4fSPaul Moore 
67446fda4fSPaul Moore /* Label mapping cache */
68446fda4fSPaul Moore int cipso_v4_cache_enabled = 1;
69446fda4fSPaul Moore int cipso_v4_cache_bucketsize = 10;
70446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETBITS     7
71446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETS        (1 << CIPSO_V4_CACHE_BUCKETBITS)
72446fda4fSPaul Moore #define CIPSO_V4_CACHE_REORDERLIMIT   10
73446fda4fSPaul Moore struct cipso_v4_map_cache_bkt {
74446fda4fSPaul Moore 	spinlock_t lock;
75446fda4fSPaul Moore 	u32 size;
76446fda4fSPaul Moore 	struct list_head list;
77446fda4fSPaul Moore };
78446fda4fSPaul Moore struct cipso_v4_map_cache_entry {
79446fda4fSPaul Moore 	u32 hash;
80446fda4fSPaul Moore 	unsigned char *key;
81446fda4fSPaul Moore 	size_t key_len;
82446fda4fSPaul Moore 
83ffb733c6Spaul.moore@hp.com 	struct netlbl_lsm_cache *lsm_data;
84446fda4fSPaul Moore 
85446fda4fSPaul Moore 	u32 activity;
86446fda4fSPaul Moore 	struct list_head list;
87446fda4fSPaul Moore };
88446fda4fSPaul Moore static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL;
89446fda4fSPaul Moore 
90446fda4fSPaul Moore /* Restricted bitmap (tag #1) flags */
91446fda4fSPaul Moore int cipso_v4_rbm_optfmt = 0;
92446fda4fSPaul Moore int cipso_v4_rbm_strictvalid = 1;
93446fda4fSPaul Moore 
94446fda4fSPaul Moore /*
95446fda4fSPaul Moore  * Helper Functions
96446fda4fSPaul Moore  */
97446fda4fSPaul Moore 
98446fda4fSPaul Moore /**
99446fda4fSPaul Moore  * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit
100446fda4fSPaul Moore  * @bitmap: the bitmap
101446fda4fSPaul Moore  * @bitmap_len: length in bits
102446fda4fSPaul Moore  * @offset: starting offset
103446fda4fSPaul Moore  * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit
104446fda4fSPaul Moore  *
105446fda4fSPaul Moore  * Description:
106446fda4fSPaul Moore  * Starting at @offset, walk the bitmap from left to right until either the
107446fda4fSPaul Moore  * desired bit is found or we reach the end.  Return the bit offset, -1 if
108446fda4fSPaul Moore  * not found, or -2 if error.
109446fda4fSPaul Moore  */
110446fda4fSPaul Moore static int cipso_v4_bitmap_walk(const unsigned char *bitmap,
111446fda4fSPaul Moore 				u32 bitmap_len,
112446fda4fSPaul Moore 				u32 offset,
113446fda4fSPaul Moore 				u8 state)
114446fda4fSPaul Moore {
115446fda4fSPaul Moore 	u32 bit_spot;
116446fda4fSPaul Moore 	u32 byte_offset;
117446fda4fSPaul Moore 	unsigned char bitmask;
118446fda4fSPaul Moore 	unsigned char byte;
119446fda4fSPaul Moore 
120446fda4fSPaul Moore 	/* gcc always rounds to zero when doing integer division */
121446fda4fSPaul Moore 	byte_offset = offset / 8;
122446fda4fSPaul Moore 	byte = bitmap[byte_offset];
123446fda4fSPaul Moore 	bit_spot = offset;
124446fda4fSPaul Moore 	bitmask = 0x80 >> (offset % 8);
125446fda4fSPaul Moore 
126446fda4fSPaul Moore 	while (bit_spot < bitmap_len) {
127446fda4fSPaul Moore 		if ((state && (byte & bitmask) == bitmask) ||
128446fda4fSPaul Moore 		    (state == 0 && (byte & bitmask) == 0))
129446fda4fSPaul Moore 			return bit_spot;
130446fda4fSPaul Moore 
131446fda4fSPaul Moore 		bit_spot++;
132446fda4fSPaul Moore 		bitmask >>= 1;
133446fda4fSPaul Moore 		if (bitmask == 0) {
134446fda4fSPaul Moore 			byte = bitmap[++byte_offset];
135446fda4fSPaul Moore 			bitmask = 0x80;
136446fda4fSPaul Moore 		}
137446fda4fSPaul Moore 	}
138446fda4fSPaul Moore 
139446fda4fSPaul Moore 	return -1;
140446fda4fSPaul Moore }
141446fda4fSPaul Moore 
142446fda4fSPaul Moore /**
143446fda4fSPaul Moore  * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap
144446fda4fSPaul Moore  * @bitmap: the bitmap
145446fda4fSPaul Moore  * @bit: the bit
146446fda4fSPaul Moore  * @state: if non-zero, set the bit (1) else clear the bit (0)
147446fda4fSPaul Moore  *
148446fda4fSPaul Moore  * Description:
149446fda4fSPaul Moore  * Set a single bit in the bitmask.  Returns zero on success, negative values
150446fda4fSPaul Moore  * on error.
151446fda4fSPaul Moore  */
152446fda4fSPaul Moore static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
153446fda4fSPaul Moore 				   u32 bit,
154446fda4fSPaul Moore 				   u8 state)
155446fda4fSPaul Moore {
156446fda4fSPaul Moore 	u32 byte_spot;
157446fda4fSPaul Moore 	u8 bitmask;
158446fda4fSPaul Moore 
159446fda4fSPaul Moore 	/* gcc always rounds to zero when doing integer division */
160446fda4fSPaul Moore 	byte_spot = bit / 8;
161446fda4fSPaul Moore 	bitmask = 0x80 >> (bit % 8);
162446fda4fSPaul Moore 	if (state)
163446fda4fSPaul Moore 		bitmap[byte_spot] |= bitmask;
164446fda4fSPaul Moore 	else
165446fda4fSPaul Moore 		bitmap[byte_spot] &= ~bitmask;
166446fda4fSPaul Moore }
167446fda4fSPaul Moore 
168446fda4fSPaul Moore /**
169446fda4fSPaul Moore  * cipso_v4_doi_domhsh_free - Frees a domain list entry
170446fda4fSPaul Moore  * @entry: the entry's RCU field
171446fda4fSPaul Moore  *
172446fda4fSPaul Moore  * Description:
173446fda4fSPaul Moore  * This function is designed to be used as a callback to the call_rcu()
174446fda4fSPaul Moore  * function so that the memory allocated to a domain list entry can be released
175446fda4fSPaul Moore  * safely.
176446fda4fSPaul Moore  *
177446fda4fSPaul Moore  */
178446fda4fSPaul Moore static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
179446fda4fSPaul Moore {
180446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *ptr;
181446fda4fSPaul Moore 
182446fda4fSPaul Moore 	ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
183446fda4fSPaul Moore 	kfree(ptr->domain);
184446fda4fSPaul Moore 	kfree(ptr);
185446fda4fSPaul Moore }
186446fda4fSPaul Moore 
187446fda4fSPaul Moore /**
188446fda4fSPaul Moore  * cipso_v4_cache_entry_free - Frees a cache entry
189446fda4fSPaul Moore  * @entry: the entry to free
190446fda4fSPaul Moore  *
191446fda4fSPaul Moore  * Description:
192ffb733c6Spaul.moore@hp.com  * This function frees the memory associated with a cache entry including the
193ffb733c6Spaul.moore@hp.com  * LSM cache data if there are no longer any users, i.e. reference count == 0.
194446fda4fSPaul Moore  *
195446fda4fSPaul Moore  */
196446fda4fSPaul Moore static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
197446fda4fSPaul Moore {
198ffb733c6Spaul.moore@hp.com 	if (entry->lsm_data)
199ffb733c6Spaul.moore@hp.com 		netlbl_secattr_cache_free(entry->lsm_data);
200446fda4fSPaul Moore 	kfree(entry->key);
201446fda4fSPaul Moore 	kfree(entry);
202446fda4fSPaul Moore }
203446fda4fSPaul Moore 
204446fda4fSPaul Moore /**
205446fda4fSPaul Moore  * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache
206446fda4fSPaul Moore  * @key: the hash key
207446fda4fSPaul Moore  * @key_len: the length of the key in bytes
208446fda4fSPaul Moore  *
209446fda4fSPaul Moore  * Description:
210446fda4fSPaul Moore  * The CIPSO tag hashing function.  Returns a 32-bit hash value.
211446fda4fSPaul Moore  *
212446fda4fSPaul Moore  */
213446fda4fSPaul Moore static u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len)
214446fda4fSPaul Moore {
215446fda4fSPaul Moore 	return jhash(key, key_len, 0);
216446fda4fSPaul Moore }
217446fda4fSPaul Moore 
218446fda4fSPaul Moore /*
219446fda4fSPaul Moore  * Label Mapping Cache Functions
220446fda4fSPaul Moore  */
221446fda4fSPaul Moore 
222446fda4fSPaul Moore /**
223446fda4fSPaul Moore  * cipso_v4_cache_init - Initialize the CIPSO cache
224446fda4fSPaul Moore  *
225446fda4fSPaul Moore  * Description:
226446fda4fSPaul Moore  * Initializes the CIPSO label mapping cache, this function should be called
227446fda4fSPaul Moore  * before any of the other functions defined in this file.  Returns zero on
228446fda4fSPaul Moore  * success, negative values on error.
229446fda4fSPaul Moore  *
230446fda4fSPaul Moore  */
231446fda4fSPaul Moore static int cipso_v4_cache_init(void)
232446fda4fSPaul Moore {
233446fda4fSPaul Moore 	u32 iter;
234446fda4fSPaul Moore 
235446fda4fSPaul Moore 	cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS,
236446fda4fSPaul Moore 				 sizeof(struct cipso_v4_map_cache_bkt),
237446fda4fSPaul Moore 				 GFP_KERNEL);
238446fda4fSPaul Moore 	if (cipso_v4_cache == NULL)
239446fda4fSPaul Moore 		return -ENOMEM;
240446fda4fSPaul Moore 
241446fda4fSPaul Moore 	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
242446fda4fSPaul Moore 		spin_lock_init(&cipso_v4_cache[iter].lock);
243446fda4fSPaul Moore 		cipso_v4_cache[iter].size = 0;
244446fda4fSPaul Moore 		INIT_LIST_HEAD(&cipso_v4_cache[iter].list);
245446fda4fSPaul Moore 	}
246446fda4fSPaul Moore 
247446fda4fSPaul Moore 	return 0;
248446fda4fSPaul Moore }
249446fda4fSPaul Moore 
250446fda4fSPaul Moore /**
251446fda4fSPaul Moore  * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache
252446fda4fSPaul Moore  *
253446fda4fSPaul Moore  * Description:
254446fda4fSPaul Moore  * Invalidates and frees any entries in the CIPSO cache.  Returns zero on
255446fda4fSPaul Moore  * success and negative values on failure.
256446fda4fSPaul Moore  *
257446fda4fSPaul Moore  */
258446fda4fSPaul Moore void cipso_v4_cache_invalidate(void)
259446fda4fSPaul Moore {
260446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry, *tmp_entry;
261446fda4fSPaul Moore 	u32 iter;
262446fda4fSPaul Moore 
263446fda4fSPaul Moore 	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
264609c92feSPaul Moore 		spin_lock_bh(&cipso_v4_cache[iter].lock);
265446fda4fSPaul Moore 		list_for_each_entry_safe(entry,
266446fda4fSPaul Moore 					 tmp_entry,
267446fda4fSPaul Moore 					 &cipso_v4_cache[iter].list, list) {
268446fda4fSPaul Moore 			list_del(&entry->list);
269446fda4fSPaul Moore 			cipso_v4_cache_entry_free(entry);
270446fda4fSPaul Moore 		}
271446fda4fSPaul Moore 		cipso_v4_cache[iter].size = 0;
272609c92feSPaul Moore 		spin_unlock_bh(&cipso_v4_cache[iter].lock);
273446fda4fSPaul Moore 	}
274446fda4fSPaul Moore 
275446fda4fSPaul Moore 	return;
276446fda4fSPaul Moore }
277446fda4fSPaul Moore 
278446fda4fSPaul Moore /**
279446fda4fSPaul Moore  * cipso_v4_cache_check - Check the CIPSO cache for a label mapping
280446fda4fSPaul Moore  * @key: the buffer to check
281446fda4fSPaul Moore  * @key_len: buffer length in bytes
282446fda4fSPaul Moore  * @secattr: the security attribute struct to use
283446fda4fSPaul Moore  *
284446fda4fSPaul Moore  * Description:
285446fda4fSPaul Moore  * This function checks the cache to see if a label mapping already exists for
286446fda4fSPaul Moore  * the given key.  If there is a match then the cache is adjusted and the
287446fda4fSPaul Moore  * @secattr struct is populated with the correct LSM security attributes.  The
288446fda4fSPaul Moore  * cache is adjusted in the following manner if the entry is not already the
289446fda4fSPaul Moore  * first in the cache bucket:
290446fda4fSPaul Moore  *
291446fda4fSPaul Moore  *  1. The cache entry's activity counter is incremented
292446fda4fSPaul Moore  *  2. The previous (higher ranking) entry's activity counter is decremented
293446fda4fSPaul Moore  *  3. If the difference between the two activity counters is geater than
294446fda4fSPaul Moore  *     CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped
295446fda4fSPaul Moore  *
296446fda4fSPaul Moore  * Returns zero on success, -ENOENT for a cache miss, and other negative values
297446fda4fSPaul Moore  * on error.
298446fda4fSPaul Moore  *
299446fda4fSPaul Moore  */
300446fda4fSPaul Moore static int cipso_v4_cache_check(const unsigned char *key,
301446fda4fSPaul Moore 				u32 key_len,
302446fda4fSPaul Moore 				struct netlbl_lsm_secattr *secattr)
303446fda4fSPaul Moore {
304446fda4fSPaul Moore 	u32 bkt;
305446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry;
306446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *prev_entry = NULL;
307446fda4fSPaul Moore 	u32 hash;
308446fda4fSPaul Moore 
309446fda4fSPaul Moore 	if (!cipso_v4_cache_enabled)
310446fda4fSPaul Moore 		return -ENOENT;
311446fda4fSPaul Moore 
312446fda4fSPaul Moore 	hash = cipso_v4_map_cache_hash(key, key_len);
313446fda4fSPaul Moore 	bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
314609c92feSPaul Moore 	spin_lock_bh(&cipso_v4_cache[bkt].lock);
315446fda4fSPaul Moore 	list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) {
316446fda4fSPaul Moore 		if (entry->hash == hash &&
317446fda4fSPaul Moore 		    entry->key_len == key_len &&
318446fda4fSPaul Moore 		    memcmp(entry->key, key, key_len) == 0) {
319446fda4fSPaul Moore 			entry->activity += 1;
320ffb733c6Spaul.moore@hp.com 			atomic_inc(&entry->lsm_data->refcount);
321ffb733c6Spaul.moore@hp.com 			secattr->cache = entry->lsm_data;
322701a90baSPaul Moore 			secattr->flags |= NETLBL_SECATTR_CACHE;
323446fda4fSPaul Moore 			if (prev_entry == NULL) {
324609c92feSPaul Moore 				spin_unlock_bh(&cipso_v4_cache[bkt].lock);
325446fda4fSPaul Moore 				return 0;
326446fda4fSPaul Moore 			}
327446fda4fSPaul Moore 
328446fda4fSPaul Moore 			if (prev_entry->activity > 0)
329446fda4fSPaul Moore 				prev_entry->activity -= 1;
330446fda4fSPaul Moore 			if (entry->activity > prev_entry->activity &&
331446fda4fSPaul Moore 			    entry->activity - prev_entry->activity >
332446fda4fSPaul Moore 			    CIPSO_V4_CACHE_REORDERLIMIT) {
333446fda4fSPaul Moore 				__list_del(entry->list.prev, entry->list.next);
334446fda4fSPaul Moore 				__list_add(&entry->list,
335446fda4fSPaul Moore 					   prev_entry->list.prev,
336446fda4fSPaul Moore 					   &prev_entry->list);
337446fda4fSPaul Moore 			}
338446fda4fSPaul Moore 
339609c92feSPaul Moore 			spin_unlock_bh(&cipso_v4_cache[bkt].lock);
340446fda4fSPaul Moore 			return 0;
341446fda4fSPaul Moore 		}
342446fda4fSPaul Moore 		prev_entry = entry;
343446fda4fSPaul Moore 	}
344609c92feSPaul Moore 	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
345446fda4fSPaul Moore 
346446fda4fSPaul Moore 	return -ENOENT;
347446fda4fSPaul Moore }
348446fda4fSPaul Moore 
349446fda4fSPaul Moore /**
350446fda4fSPaul Moore  * cipso_v4_cache_add - Add an entry to the CIPSO cache
351446fda4fSPaul Moore  * @skb: the packet
352446fda4fSPaul Moore  * @secattr: the packet's security attributes
353446fda4fSPaul Moore  *
354446fda4fSPaul Moore  * Description:
355446fda4fSPaul Moore  * Add a new entry into the CIPSO label mapping cache.  Add the new entry to
356446fda4fSPaul Moore  * head of the cache bucket's list, if the cache bucket is out of room remove
357446fda4fSPaul Moore  * the last entry in the list first.  It is important to note that there is
358446fda4fSPaul Moore  * currently no checking for duplicate keys.  Returns zero on success,
359446fda4fSPaul Moore  * negative values on failure.
360446fda4fSPaul Moore  *
361446fda4fSPaul Moore  */
362446fda4fSPaul Moore int cipso_v4_cache_add(const struct sk_buff *skb,
363446fda4fSPaul Moore 		       const struct netlbl_lsm_secattr *secattr)
364446fda4fSPaul Moore {
365446fda4fSPaul Moore 	int ret_val = -EPERM;
366446fda4fSPaul Moore 	u32 bkt;
367446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry = NULL;
368446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *old_entry = NULL;
369446fda4fSPaul Moore 	unsigned char *cipso_ptr;
370446fda4fSPaul Moore 	u32 cipso_ptr_len;
371446fda4fSPaul Moore 
372446fda4fSPaul Moore 	if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0)
373446fda4fSPaul Moore 		return 0;
374446fda4fSPaul Moore 
375446fda4fSPaul Moore 	cipso_ptr = CIPSO_V4_OPTPTR(skb);
376446fda4fSPaul Moore 	cipso_ptr_len = cipso_ptr[1];
377446fda4fSPaul Moore 
378446fda4fSPaul Moore 	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
379446fda4fSPaul Moore 	if (entry == NULL)
380446fda4fSPaul Moore 		return -ENOMEM;
381fac5d731SArnaldo Carvalho de Melo 	entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
382446fda4fSPaul Moore 	if (entry->key == NULL) {
383446fda4fSPaul Moore 		ret_val = -ENOMEM;
384446fda4fSPaul Moore 		goto cache_add_failure;
385446fda4fSPaul Moore 	}
386446fda4fSPaul Moore 	entry->key_len = cipso_ptr_len;
387446fda4fSPaul Moore 	entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
388ffb733c6Spaul.moore@hp.com 	atomic_inc(&secattr->cache->refcount);
389ffb733c6Spaul.moore@hp.com 	entry->lsm_data = secattr->cache;
390446fda4fSPaul Moore 
391446fda4fSPaul Moore 	bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
392609c92feSPaul Moore 	spin_lock_bh(&cipso_v4_cache[bkt].lock);
393446fda4fSPaul Moore 	if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) {
394446fda4fSPaul Moore 		list_add(&entry->list, &cipso_v4_cache[bkt].list);
395446fda4fSPaul Moore 		cipso_v4_cache[bkt].size += 1;
396446fda4fSPaul Moore 	} else {
397446fda4fSPaul Moore 		old_entry = list_entry(cipso_v4_cache[bkt].list.prev,
398446fda4fSPaul Moore 				       struct cipso_v4_map_cache_entry, list);
399446fda4fSPaul Moore 		list_del(&old_entry->list);
400446fda4fSPaul Moore 		list_add(&entry->list, &cipso_v4_cache[bkt].list);
401446fda4fSPaul Moore 		cipso_v4_cache_entry_free(old_entry);
402446fda4fSPaul Moore 	}
403609c92feSPaul Moore 	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
404446fda4fSPaul Moore 
405446fda4fSPaul Moore 	return 0;
406446fda4fSPaul Moore 
407446fda4fSPaul Moore cache_add_failure:
408446fda4fSPaul Moore 	if (entry)
409446fda4fSPaul Moore 		cipso_v4_cache_entry_free(entry);
410446fda4fSPaul Moore 	return ret_val;
411446fda4fSPaul Moore }
412446fda4fSPaul Moore 
413446fda4fSPaul Moore /*
414446fda4fSPaul Moore  * DOI List Functions
415446fda4fSPaul Moore  */
416446fda4fSPaul Moore 
417446fda4fSPaul Moore /**
418446fda4fSPaul Moore  * cipso_v4_doi_search - Searches for a DOI definition
419446fda4fSPaul Moore  * @doi: the DOI to search for
420446fda4fSPaul Moore  *
421446fda4fSPaul Moore  * Description:
422446fda4fSPaul Moore  * Search the DOI definition list for a DOI definition with a DOI value that
423446fda4fSPaul Moore  * matches @doi.  The caller is responsibile for calling rcu_read_[un]lock().
424446fda4fSPaul Moore  * Returns a pointer to the DOI definition on success and NULL on failure.
425446fda4fSPaul Moore  */
426446fda4fSPaul Moore static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
427446fda4fSPaul Moore {
428446fda4fSPaul Moore 	struct cipso_v4_doi *iter;
429446fda4fSPaul Moore 
430446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
431446fda4fSPaul Moore 		if (iter->doi == doi && iter->valid)
432446fda4fSPaul Moore 			return iter;
433446fda4fSPaul Moore 	return NULL;
434446fda4fSPaul Moore }
435446fda4fSPaul Moore 
436446fda4fSPaul Moore /**
437446fda4fSPaul Moore  * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
438446fda4fSPaul Moore  * @doi_def: the DOI structure
439446fda4fSPaul Moore  *
440446fda4fSPaul Moore  * Description:
441446fda4fSPaul Moore  * The caller defines a new DOI for use by the CIPSO engine and calls this
442446fda4fSPaul Moore  * function to add it to the list of acceptable domains.  The caller must
443446fda4fSPaul Moore  * ensure that the mapping table specified in @doi_def->map meets all of the
444446fda4fSPaul Moore  * requirements of the mapping type (see cipso_ipv4.h for details).  Returns
445446fda4fSPaul Moore  * zero on success and non-zero on failure.
446446fda4fSPaul Moore  *
447446fda4fSPaul Moore  */
448446fda4fSPaul Moore int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
449446fda4fSPaul Moore {
4506ce61a7cSPaul Moore 	u32 iter;
4516ce61a7cSPaul Moore 
452446fda4fSPaul Moore 	if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN)
453446fda4fSPaul Moore 		return -EINVAL;
4546ce61a7cSPaul Moore 	for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
4556ce61a7cSPaul Moore 		switch (doi_def->tags[iter]) {
4566ce61a7cSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
4576ce61a7cSPaul Moore 			break;
4586ce61a7cSPaul Moore 		case CIPSO_V4_TAG_INVALID:
4596ce61a7cSPaul Moore 			if (iter == 0)
4606ce61a7cSPaul Moore 				return -EINVAL;
4616ce61a7cSPaul Moore 			break;
4626ce61a7cSPaul Moore 		default:
4636ce61a7cSPaul Moore 			return -EINVAL;
4646ce61a7cSPaul Moore 		}
4656ce61a7cSPaul Moore 	}
466446fda4fSPaul Moore 
467446fda4fSPaul Moore 	doi_def->valid = 1;
468446fda4fSPaul Moore 	INIT_RCU_HEAD(&doi_def->rcu);
469446fda4fSPaul Moore 	INIT_LIST_HEAD(&doi_def->dom_list);
470446fda4fSPaul Moore 
471446fda4fSPaul Moore 	rcu_read_lock();
472446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
473446fda4fSPaul Moore 		goto doi_add_failure_rlock;
474446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
475446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
476446fda4fSPaul Moore 		goto doi_add_failure_slock;
477446fda4fSPaul Moore 	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
478446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
479446fda4fSPaul Moore 	rcu_read_unlock();
480446fda4fSPaul Moore 
481446fda4fSPaul Moore 	return 0;
482446fda4fSPaul Moore 
483446fda4fSPaul Moore doi_add_failure_slock:
484446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
485446fda4fSPaul Moore doi_add_failure_rlock:
486446fda4fSPaul Moore 	rcu_read_unlock();
487446fda4fSPaul Moore 	return -EEXIST;
488446fda4fSPaul Moore }
489446fda4fSPaul Moore 
490446fda4fSPaul Moore /**
491446fda4fSPaul Moore  * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
492446fda4fSPaul Moore  * @doi: the DOI value
49332f50cdeSPaul Moore  * @audit_secid: the LSM secid to use in the audit message
494446fda4fSPaul Moore  * @callback: the DOI cleanup/free callback
495446fda4fSPaul Moore  *
496446fda4fSPaul Moore  * Description:
497446fda4fSPaul Moore  * Removes a DOI definition from the CIPSO engine, @callback is called to
498446fda4fSPaul Moore  * free any memory.  The NetLabel routines will be called to release their own
499446fda4fSPaul Moore  * LSM domain mappings as well as our own domain list.  Returns zero on
500446fda4fSPaul Moore  * success and negative values on failure.
501446fda4fSPaul Moore  *
502446fda4fSPaul Moore  */
50332f50cdeSPaul Moore int cipso_v4_doi_remove(u32 doi,
50495d4e6beSPaul Moore 			struct netlbl_audit *audit_info,
50532f50cdeSPaul Moore 			void (*callback) (struct rcu_head * head))
506446fda4fSPaul Moore {
507446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def;
508446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *dom_iter;
509446fda4fSPaul Moore 
510446fda4fSPaul Moore 	rcu_read_lock();
511446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi) != NULL) {
512446fda4fSPaul Moore 		spin_lock(&cipso_v4_doi_list_lock);
513446fda4fSPaul Moore 		doi_def = cipso_v4_doi_search(doi);
514446fda4fSPaul Moore 		if (doi_def == NULL) {
515446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
516446fda4fSPaul Moore 			rcu_read_unlock();
517446fda4fSPaul Moore 			return -ENOENT;
518446fda4fSPaul Moore 		}
519446fda4fSPaul Moore 		doi_def->valid = 0;
520446fda4fSPaul Moore 		list_del_rcu(&doi_def->list);
521446fda4fSPaul Moore 		spin_unlock(&cipso_v4_doi_list_lock);
522446fda4fSPaul Moore 		list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
523446fda4fSPaul Moore 			if (dom_iter->valid)
52432f50cdeSPaul Moore 				netlbl_domhsh_remove(dom_iter->domain,
52595d4e6beSPaul Moore 						     audit_info);
526446fda4fSPaul Moore 		cipso_v4_cache_invalidate();
527446fda4fSPaul Moore 		rcu_read_unlock();
528446fda4fSPaul Moore 
529446fda4fSPaul Moore 		call_rcu(&doi_def->rcu, callback);
530446fda4fSPaul Moore 		return 0;
531446fda4fSPaul Moore 	}
532446fda4fSPaul Moore 	rcu_read_unlock();
533446fda4fSPaul Moore 
534446fda4fSPaul Moore 	return -ENOENT;
535446fda4fSPaul Moore }
536446fda4fSPaul Moore 
537446fda4fSPaul Moore /**
538446fda4fSPaul Moore  * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
539446fda4fSPaul Moore  * @doi: the DOI value
540446fda4fSPaul Moore  *
541446fda4fSPaul Moore  * Description:
542446fda4fSPaul Moore  * Searches for a valid DOI definition and if one is found it is returned to
543446fda4fSPaul Moore  * the caller.  Otherwise NULL is returned.  The caller must ensure that
544446fda4fSPaul Moore  * rcu_read_lock() is held while accessing the returned definition.
545446fda4fSPaul Moore  *
546446fda4fSPaul Moore  */
547446fda4fSPaul Moore struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
548446fda4fSPaul Moore {
549446fda4fSPaul Moore 	return cipso_v4_doi_search(doi);
550446fda4fSPaul Moore }
551446fda4fSPaul Moore 
552446fda4fSPaul Moore /**
553fcd48280SPaul Moore  * cipso_v4_doi_walk - Iterate through the DOI definitions
554fcd48280SPaul Moore  * @skip_cnt: skip past this number of DOI definitions, updated
555fcd48280SPaul Moore  * @callback: callback for each DOI definition
556fcd48280SPaul Moore  * @cb_arg: argument for the callback function
557446fda4fSPaul Moore  *
558446fda4fSPaul Moore  * Description:
559fcd48280SPaul Moore  * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
560fcd48280SPaul Moore  * For each entry call @callback, if @callback returns a negative value stop
561fcd48280SPaul Moore  * 'walking' through the list and return.  Updates the value in @skip_cnt upon
562fcd48280SPaul Moore  * return.  Returns zero on success, negative values on failure.
563446fda4fSPaul Moore  *
564446fda4fSPaul Moore  */
565fcd48280SPaul Moore int cipso_v4_doi_walk(u32 *skip_cnt,
566fcd48280SPaul Moore 		     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
567fcd48280SPaul Moore 		     void *cb_arg)
568446fda4fSPaul Moore {
569fcd48280SPaul Moore 	int ret_val = -ENOENT;
570446fda4fSPaul Moore 	u32 doi_cnt = 0;
571fcd48280SPaul Moore 	struct cipso_v4_doi *iter_doi;
572446fda4fSPaul Moore 
573446fda4fSPaul Moore 	rcu_read_lock();
574fcd48280SPaul Moore 	list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
575fcd48280SPaul Moore 		if (iter_doi->valid) {
576fcd48280SPaul Moore 			if (doi_cnt++ < *skip_cnt)
577fcd48280SPaul Moore 				continue;
578fcd48280SPaul Moore 			ret_val = callback(iter_doi, cb_arg);
579fcd48280SPaul Moore 			if (ret_val < 0) {
580fcd48280SPaul Moore 				doi_cnt--;
581fcd48280SPaul Moore 				goto doi_walk_return;
582446fda4fSPaul Moore 			}
583446fda4fSPaul Moore 		}
584446fda4fSPaul Moore 
585fcd48280SPaul Moore doi_walk_return:
586446fda4fSPaul Moore 	rcu_read_unlock();
587fcd48280SPaul Moore 	*skip_cnt = doi_cnt;
588fcd48280SPaul Moore 	return ret_val;
589446fda4fSPaul Moore }
590446fda4fSPaul Moore 
591446fda4fSPaul Moore /**
592446fda4fSPaul Moore  * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
593446fda4fSPaul Moore  * @doi_def: the DOI definition
594446fda4fSPaul Moore  * @domain: the domain to add
595446fda4fSPaul Moore  *
596446fda4fSPaul Moore  * Description:
597446fda4fSPaul Moore  * Adds the @domain to the the DOI specified by @doi_def, this function
598446fda4fSPaul Moore  * should only be called by external functions (i.e. NetLabel).  This function
599446fda4fSPaul Moore  * does allocate memory.  Returns zero on success, negative values on failure.
600446fda4fSPaul Moore  *
601446fda4fSPaul Moore  */
602446fda4fSPaul Moore int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
603446fda4fSPaul Moore {
604446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *iter;
605446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *new_dom;
606446fda4fSPaul Moore 
607446fda4fSPaul Moore 	new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
608446fda4fSPaul Moore 	if (new_dom == NULL)
609446fda4fSPaul Moore 		return -ENOMEM;
610446fda4fSPaul Moore 	if (domain) {
611446fda4fSPaul Moore 		new_dom->domain = kstrdup(domain, GFP_KERNEL);
612446fda4fSPaul Moore 		if (new_dom->domain == NULL) {
613446fda4fSPaul Moore 			kfree(new_dom);
614446fda4fSPaul Moore 			return -ENOMEM;
615446fda4fSPaul Moore 		}
616446fda4fSPaul Moore 	}
617446fda4fSPaul Moore 	new_dom->valid = 1;
618446fda4fSPaul Moore 	INIT_RCU_HEAD(&new_dom->rcu);
619446fda4fSPaul Moore 
620446fda4fSPaul Moore 	rcu_read_lock();
621446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
622446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
623446fda4fSPaul Moore 		if (iter->valid &&
624446fda4fSPaul Moore 		    ((domain != NULL && iter->domain != NULL &&
625446fda4fSPaul Moore 		      strcmp(iter->domain, domain) == 0) ||
626446fda4fSPaul Moore 		     (domain == NULL && iter->domain == NULL))) {
627446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
628446fda4fSPaul Moore 			rcu_read_unlock();
629446fda4fSPaul Moore 			kfree(new_dom->domain);
630446fda4fSPaul Moore 			kfree(new_dom);
631446fda4fSPaul Moore 			return -EEXIST;
632446fda4fSPaul Moore 		}
633446fda4fSPaul Moore 	list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
634446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
635446fda4fSPaul Moore 	rcu_read_unlock();
636446fda4fSPaul Moore 
637446fda4fSPaul Moore 	return 0;
638446fda4fSPaul Moore }
639446fda4fSPaul Moore 
640446fda4fSPaul Moore /**
641446fda4fSPaul Moore  * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
642446fda4fSPaul Moore  * @doi_def: the DOI definition
643446fda4fSPaul Moore  * @domain: the domain to remove
644446fda4fSPaul Moore  *
645446fda4fSPaul Moore  * Description:
646446fda4fSPaul Moore  * Removes the @domain from the DOI specified by @doi_def, this function
647446fda4fSPaul Moore  * should only be called by external functions (i.e. NetLabel).   Returns zero
648446fda4fSPaul Moore  * on success and negative values on error.
649446fda4fSPaul Moore  *
650446fda4fSPaul Moore  */
651446fda4fSPaul Moore int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
652446fda4fSPaul Moore 			       const char *domain)
653446fda4fSPaul Moore {
654446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *iter;
655446fda4fSPaul Moore 
656446fda4fSPaul Moore 	rcu_read_lock();
657446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
658446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
659446fda4fSPaul Moore 		if (iter->valid &&
660446fda4fSPaul Moore 		    ((domain != NULL && iter->domain != NULL &&
661446fda4fSPaul Moore 		      strcmp(iter->domain, domain) == 0) ||
662446fda4fSPaul Moore 		     (domain == NULL && iter->domain == NULL))) {
663446fda4fSPaul Moore 			iter->valid = 0;
664446fda4fSPaul Moore 			list_del_rcu(&iter->list);
665446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
666446fda4fSPaul Moore 			rcu_read_unlock();
667446fda4fSPaul Moore 			call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
668446fda4fSPaul Moore 
669446fda4fSPaul Moore 			return 0;
670446fda4fSPaul Moore 		}
671446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
672446fda4fSPaul Moore 	rcu_read_unlock();
673446fda4fSPaul Moore 
674446fda4fSPaul Moore 	return -ENOENT;
675446fda4fSPaul Moore }
676446fda4fSPaul Moore 
677446fda4fSPaul Moore /*
678446fda4fSPaul Moore  * Label Mapping Functions
679446fda4fSPaul Moore  */
680446fda4fSPaul Moore 
681446fda4fSPaul Moore /**
682446fda4fSPaul Moore  * cipso_v4_map_lvl_valid - Checks to see if the given level is understood
683446fda4fSPaul Moore  * @doi_def: the DOI definition
684446fda4fSPaul Moore  * @level: the level to check
685446fda4fSPaul Moore  *
686446fda4fSPaul Moore  * Description:
687446fda4fSPaul Moore  * Checks the given level against the given DOI definition and returns a
688446fda4fSPaul Moore  * negative value if the level does not have a valid mapping and a zero value
689446fda4fSPaul Moore  * if the level is defined by the DOI.
690446fda4fSPaul Moore  *
691446fda4fSPaul Moore  */
692446fda4fSPaul Moore static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
693446fda4fSPaul Moore {
694446fda4fSPaul Moore 	switch (doi_def->type) {
695446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
696446fda4fSPaul Moore 		return 0;
697446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
698446fda4fSPaul Moore 		if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL)
699446fda4fSPaul Moore 			return 0;
700446fda4fSPaul Moore 		break;
701446fda4fSPaul Moore 	}
702446fda4fSPaul Moore 
703446fda4fSPaul Moore 	return -EFAULT;
704446fda4fSPaul Moore }
705446fda4fSPaul Moore 
706446fda4fSPaul Moore /**
707446fda4fSPaul Moore  * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network
708446fda4fSPaul Moore  * @doi_def: the DOI definition
709446fda4fSPaul Moore  * @host_lvl: the host MLS level
710446fda4fSPaul Moore  * @net_lvl: the network/CIPSO MLS level
711446fda4fSPaul Moore  *
712446fda4fSPaul Moore  * Description:
713446fda4fSPaul Moore  * Perform a label mapping to translate a local MLS level to the correct
714446fda4fSPaul Moore  * CIPSO level using the given DOI definition.  Returns zero on success,
715446fda4fSPaul Moore  * negative values otherwise.
716446fda4fSPaul Moore  *
717446fda4fSPaul Moore  */
718446fda4fSPaul Moore static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
719446fda4fSPaul Moore 				 u32 host_lvl,
720446fda4fSPaul Moore 				 u32 *net_lvl)
721446fda4fSPaul Moore {
722446fda4fSPaul Moore 	switch (doi_def->type) {
723446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
724446fda4fSPaul Moore 		*net_lvl = host_lvl;
725446fda4fSPaul Moore 		return 0;
726446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
727446fda4fSPaul Moore 		if (host_lvl < doi_def->map.std->lvl.local_size) {
728446fda4fSPaul Moore 			*net_lvl = doi_def->map.std->lvl.local[host_lvl];
729446fda4fSPaul Moore 			return 0;
730446fda4fSPaul Moore 		}
731446fda4fSPaul Moore 		break;
732446fda4fSPaul Moore 	}
733446fda4fSPaul Moore 
734446fda4fSPaul Moore 	return -EINVAL;
735446fda4fSPaul Moore }
736446fda4fSPaul Moore 
737446fda4fSPaul Moore /**
738446fda4fSPaul Moore  * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host
739446fda4fSPaul Moore  * @doi_def: the DOI definition
740446fda4fSPaul Moore  * @net_lvl: the network/CIPSO MLS level
741446fda4fSPaul Moore  * @host_lvl: the host MLS level
742446fda4fSPaul Moore  *
743446fda4fSPaul Moore  * Description:
744446fda4fSPaul Moore  * Perform a label mapping to translate a CIPSO level to the correct local MLS
745446fda4fSPaul Moore  * level using the given DOI definition.  Returns zero on success, negative
746446fda4fSPaul Moore  * values otherwise.
747446fda4fSPaul Moore  *
748446fda4fSPaul Moore  */
749446fda4fSPaul Moore static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
750446fda4fSPaul Moore 				 u32 net_lvl,
751446fda4fSPaul Moore 				 u32 *host_lvl)
752446fda4fSPaul Moore {
753446fda4fSPaul Moore 	struct cipso_v4_std_map_tbl *map_tbl;
754446fda4fSPaul Moore 
755446fda4fSPaul Moore 	switch (doi_def->type) {
756446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
757446fda4fSPaul Moore 		*host_lvl = net_lvl;
758446fda4fSPaul Moore 		return 0;
759446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
760446fda4fSPaul Moore 		map_tbl = doi_def->map.std;
761446fda4fSPaul Moore 		if (net_lvl < map_tbl->lvl.cipso_size &&
762446fda4fSPaul Moore 		    map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
763446fda4fSPaul Moore 			*host_lvl = doi_def->map.std->lvl.cipso[net_lvl];
764446fda4fSPaul Moore 			return 0;
765446fda4fSPaul Moore 		}
766446fda4fSPaul Moore 		break;
767446fda4fSPaul Moore 	}
768446fda4fSPaul Moore 
769446fda4fSPaul Moore 	return -EINVAL;
770446fda4fSPaul Moore }
771446fda4fSPaul Moore 
772446fda4fSPaul Moore /**
773446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid
774446fda4fSPaul Moore  * @doi_def: the DOI definition
775446fda4fSPaul Moore  * @bitmap: category bitmap
776446fda4fSPaul Moore  * @bitmap_len: bitmap length in bytes
777446fda4fSPaul Moore  *
778446fda4fSPaul Moore  * Description:
779446fda4fSPaul Moore  * Checks the given category bitmap against the given DOI definition and
780446fda4fSPaul Moore  * returns a negative value if any of the categories in the bitmap do not have
781446fda4fSPaul Moore  * a valid mapping and a zero value if all of the categories are valid.
782446fda4fSPaul Moore  *
783446fda4fSPaul Moore  */
784446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
785446fda4fSPaul Moore 				      const unsigned char *bitmap,
786446fda4fSPaul Moore 				      u32 bitmap_len)
787446fda4fSPaul Moore {
788446fda4fSPaul Moore 	int cat = -1;
789446fda4fSPaul Moore 	u32 bitmap_len_bits = bitmap_len * 8;
790044a68edSPaul Moore 	u32 cipso_cat_size;
791044a68edSPaul Moore 	u32 *cipso_array;
792446fda4fSPaul Moore 
793446fda4fSPaul Moore 	switch (doi_def->type) {
794446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
795446fda4fSPaul Moore 		return 0;
796446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
797044a68edSPaul Moore 		cipso_cat_size = doi_def->map.std->cat.cipso_size;
798044a68edSPaul Moore 		cipso_array = doi_def->map.std->cat.cipso;
799446fda4fSPaul Moore 		for (;;) {
800446fda4fSPaul Moore 			cat = cipso_v4_bitmap_walk(bitmap,
801446fda4fSPaul Moore 						   bitmap_len_bits,
802446fda4fSPaul Moore 						   cat + 1,
803446fda4fSPaul Moore 						   1);
804446fda4fSPaul Moore 			if (cat < 0)
805446fda4fSPaul Moore 				break;
806446fda4fSPaul Moore 			if (cat >= cipso_cat_size ||
807446fda4fSPaul Moore 			    cipso_array[cat] >= CIPSO_V4_INV_CAT)
808446fda4fSPaul Moore 				return -EFAULT;
809446fda4fSPaul Moore 		}
810446fda4fSPaul Moore 
811446fda4fSPaul Moore 		if (cat == -1)
812446fda4fSPaul Moore 			return 0;
813446fda4fSPaul Moore 		break;
814446fda4fSPaul Moore 	}
815446fda4fSPaul Moore 
816446fda4fSPaul Moore 	return -EFAULT;
817446fda4fSPaul Moore }
818446fda4fSPaul Moore 
819446fda4fSPaul Moore /**
820446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network
821446fda4fSPaul Moore  * @doi_def: the DOI definition
822446fda4fSPaul Moore  * @host_cat: the category bitmap in host format
823446fda4fSPaul Moore  * @host_cat_len: the length of the host's category bitmap in bytes
824446fda4fSPaul Moore  * @net_cat: the zero'd out category bitmap in network/CIPSO format
825446fda4fSPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
826446fda4fSPaul Moore  *
827446fda4fSPaul Moore  * Description:
828446fda4fSPaul Moore  * Perform a label mapping to translate a local MLS category bitmap to the
829446fda4fSPaul Moore  * correct CIPSO bitmap using the given DOI definition.  Returns the minimum
830446fda4fSPaul Moore  * size in bytes of the network bitmap on success, negative values otherwise.
831446fda4fSPaul Moore  *
832446fda4fSPaul Moore  */
833446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
834446fda4fSPaul Moore 				     const unsigned char *host_cat,
835446fda4fSPaul Moore 				     u32 host_cat_len,
836446fda4fSPaul Moore 				     unsigned char *net_cat,
837446fda4fSPaul Moore 				     u32 net_cat_len)
838446fda4fSPaul Moore {
839446fda4fSPaul Moore 	int host_spot = -1;
840446fda4fSPaul Moore 	u32 net_spot;
841446fda4fSPaul Moore 	u32 net_spot_max = 0;
842446fda4fSPaul Moore 	u32 host_clen_bits = host_cat_len * 8;
843446fda4fSPaul Moore 	u32 net_clen_bits = net_cat_len * 8;
844044a68edSPaul Moore 	u32 host_cat_size;
845044a68edSPaul Moore 	u32 *host_cat_array;
846446fda4fSPaul Moore 
847446fda4fSPaul Moore 	switch (doi_def->type) {
848446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
849ea614d7fSPaul Moore 		net_spot_max = host_cat_len;
850ea614d7fSPaul Moore 		while (net_spot_max > 0 && host_cat[net_spot_max - 1] == 0)
851446fda4fSPaul Moore 			net_spot_max--;
852446fda4fSPaul Moore 		if (net_spot_max > net_cat_len)
853446fda4fSPaul Moore 			return -EINVAL;
854446fda4fSPaul Moore 		memcpy(net_cat, host_cat, net_spot_max);
855446fda4fSPaul Moore 		return net_spot_max;
856446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
857044a68edSPaul Moore 		host_cat_size = doi_def->map.std->cat.local_size;
858044a68edSPaul Moore 		host_cat_array = doi_def->map.std->cat.local;
859446fda4fSPaul Moore 		for (;;) {
860446fda4fSPaul Moore 			host_spot = cipso_v4_bitmap_walk(host_cat,
861446fda4fSPaul Moore 							 host_clen_bits,
862446fda4fSPaul Moore 							 host_spot + 1,
863446fda4fSPaul Moore 							 1);
864446fda4fSPaul Moore 			if (host_spot < 0)
865446fda4fSPaul Moore 				break;
866446fda4fSPaul Moore 			if (host_spot >= host_cat_size)
867446fda4fSPaul Moore 				return -EPERM;
868446fda4fSPaul Moore 
869446fda4fSPaul Moore 			net_spot = host_cat_array[host_spot];
870446fda4fSPaul Moore 			if (net_spot >= net_clen_bits)
871446fda4fSPaul Moore 				return -ENOSPC;
872446fda4fSPaul Moore 			cipso_v4_bitmap_setbit(net_cat, net_spot, 1);
873446fda4fSPaul Moore 
874446fda4fSPaul Moore 			if (net_spot > net_spot_max)
875446fda4fSPaul Moore 				net_spot_max = net_spot;
876446fda4fSPaul Moore 		}
877446fda4fSPaul Moore 
878446fda4fSPaul Moore 		if (host_spot == -2)
879446fda4fSPaul Moore 			return -EFAULT;
880446fda4fSPaul Moore 
881446fda4fSPaul Moore 		if (++net_spot_max % 8)
882446fda4fSPaul Moore 			return net_spot_max / 8 + 1;
883446fda4fSPaul Moore 		return net_spot_max / 8;
884446fda4fSPaul Moore 	}
885446fda4fSPaul Moore 
886446fda4fSPaul Moore 	return -EINVAL;
887446fda4fSPaul Moore }
888446fda4fSPaul Moore 
889446fda4fSPaul Moore /**
890446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host
891446fda4fSPaul Moore  * @doi_def: the DOI definition
892446fda4fSPaul Moore  * @net_cat: the category bitmap in network/CIPSO format
893446fda4fSPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
894446fda4fSPaul Moore  * @host_cat: the zero'd out category bitmap in host format
895446fda4fSPaul Moore  * @host_cat_len: the length of the host's category bitmap in bytes
896446fda4fSPaul Moore  *
897446fda4fSPaul Moore  * Description:
898446fda4fSPaul Moore  * Perform a label mapping to translate a CIPSO bitmap to the correct local
899446fda4fSPaul Moore  * MLS category bitmap using the given DOI definition.  Returns the minimum
900446fda4fSPaul Moore  * size in bytes of the host bitmap on success, negative values otherwise.
901446fda4fSPaul Moore  *
902446fda4fSPaul Moore  */
903446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
904446fda4fSPaul Moore 				     const unsigned char *net_cat,
905446fda4fSPaul Moore 				     u32 net_cat_len,
906446fda4fSPaul Moore 				     unsigned char *host_cat,
907446fda4fSPaul Moore 				     u32 host_cat_len)
908446fda4fSPaul Moore {
909446fda4fSPaul Moore 	u32 host_spot;
910446fda4fSPaul Moore 	u32 host_spot_max = 0;
911446fda4fSPaul Moore 	int net_spot = -1;
912446fda4fSPaul Moore 	u32 net_clen_bits = net_cat_len * 8;
913446fda4fSPaul Moore 	u32 host_clen_bits = host_cat_len * 8;
914044a68edSPaul Moore 	u32 net_cat_size;
915044a68edSPaul Moore 	u32 *net_cat_array;
916446fda4fSPaul Moore 
917446fda4fSPaul Moore 	switch (doi_def->type) {
918446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
919446fda4fSPaul Moore 		if (net_cat_len > host_cat_len)
920446fda4fSPaul Moore 			return -EINVAL;
921446fda4fSPaul Moore 		memcpy(host_cat, net_cat, net_cat_len);
922446fda4fSPaul Moore 		return net_cat_len;
923446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
924044a68edSPaul Moore 		net_cat_size = doi_def->map.std->cat.cipso_size;
925044a68edSPaul Moore 		net_cat_array = doi_def->map.std->cat.cipso;
926446fda4fSPaul Moore 		for (;;) {
927446fda4fSPaul Moore 			net_spot = cipso_v4_bitmap_walk(net_cat,
928446fda4fSPaul Moore 							net_clen_bits,
929446fda4fSPaul Moore 							net_spot + 1,
930446fda4fSPaul Moore 							1);
931446fda4fSPaul Moore 			if (net_spot < 0)
932446fda4fSPaul Moore 				break;
933446fda4fSPaul Moore 			if (net_spot >= net_cat_size ||
934446fda4fSPaul Moore 			    net_cat_array[net_spot] >= CIPSO_V4_INV_CAT)
935446fda4fSPaul Moore 				return -EPERM;
936446fda4fSPaul Moore 
937446fda4fSPaul Moore 			host_spot = net_cat_array[net_spot];
938446fda4fSPaul Moore 			if (host_spot >= host_clen_bits)
939446fda4fSPaul Moore 				return -ENOSPC;
940446fda4fSPaul Moore 			cipso_v4_bitmap_setbit(host_cat, host_spot, 1);
941446fda4fSPaul Moore 
942446fda4fSPaul Moore 			if (host_spot > host_spot_max)
943446fda4fSPaul Moore 				host_spot_max = host_spot;
944446fda4fSPaul Moore 		}
945446fda4fSPaul Moore 
946446fda4fSPaul Moore 		if (net_spot == -2)
947446fda4fSPaul Moore 			return -EFAULT;
948446fda4fSPaul Moore 
949446fda4fSPaul Moore 		if (++host_spot_max % 8)
950446fda4fSPaul Moore 			return host_spot_max / 8 + 1;
951446fda4fSPaul Moore 		return host_spot_max / 8;
952446fda4fSPaul Moore 	}
953446fda4fSPaul Moore 
954446fda4fSPaul Moore 	return -EINVAL;
955446fda4fSPaul Moore }
956446fda4fSPaul Moore 
957446fda4fSPaul Moore /*
958446fda4fSPaul Moore  * Protocol Handling Functions
959446fda4fSPaul Moore  */
960446fda4fSPaul Moore 
961*91b1ed0aSPaul Moore #define CIPSO_V4_OPT_LEN_MAX          40
962446fda4fSPaul Moore #define CIPSO_V4_HDR_LEN              6
963446fda4fSPaul Moore 
964446fda4fSPaul Moore /**
965446fda4fSPaul Moore  * cipso_v4_gentag_hdr - Generate a CIPSO option header
966446fda4fSPaul Moore  * @doi_def: the DOI definition
967*91b1ed0aSPaul Moore  * @len: the total tag length in bytes, not including this header
968446fda4fSPaul Moore  * @buf: the CIPSO option buffer
969446fda4fSPaul Moore  *
970446fda4fSPaul Moore  * Description:
971*91b1ed0aSPaul Moore  * Write a CIPSO header into the beginning of @buffer.
972446fda4fSPaul Moore  *
973446fda4fSPaul Moore  */
974*91b1ed0aSPaul Moore static void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def,
975*91b1ed0aSPaul Moore 				unsigned char *buf,
976*91b1ed0aSPaul Moore 				u32 len)
977446fda4fSPaul Moore {
978446fda4fSPaul Moore 	buf[0] = IPOPT_CIPSO;
979446fda4fSPaul Moore 	buf[1] = CIPSO_V4_HDR_LEN + len;
980714e85beSAl Viro 	*(__be32 *)&buf[2] = htonl(doi_def->doi);
981446fda4fSPaul Moore }
982446fda4fSPaul Moore 
983446fda4fSPaul Moore /**
984446fda4fSPaul Moore  * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1)
985446fda4fSPaul Moore  * @doi_def: the DOI definition
986446fda4fSPaul Moore  * @secattr: the security attributes
987446fda4fSPaul Moore  * @buffer: the option buffer
988446fda4fSPaul Moore  * @buffer_len: length of buffer in bytes
989446fda4fSPaul Moore  *
990446fda4fSPaul Moore  * Description:
991446fda4fSPaul Moore  * Generate a CIPSO option using the restricted bitmap tag, tag type #1.  The
992446fda4fSPaul Moore  * actual buffer length may be larger than the indicated size due to
993*91b1ed0aSPaul Moore  * translation between host and network category bitmaps.  Returns the size of
994*91b1ed0aSPaul Moore  * the tag on success, negative values on failure.
995446fda4fSPaul Moore  *
996446fda4fSPaul Moore  */
997446fda4fSPaul Moore static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
998446fda4fSPaul Moore 			       const struct netlbl_lsm_secattr *secattr,
999*91b1ed0aSPaul Moore 			       unsigned char *buffer,
1000*91b1ed0aSPaul Moore 			       u32 buffer_len)
1001446fda4fSPaul Moore {
1002701a90baSPaul Moore 	int ret_val;
1003*91b1ed0aSPaul Moore 	u32 tag_len;
1004446fda4fSPaul Moore 	u32 level;
1005446fda4fSPaul Moore 
1006701a90baSPaul Moore 	if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
1007701a90baSPaul Moore 		return -EPERM;
1008701a90baSPaul Moore 
1009*91b1ed0aSPaul Moore 	ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
1010*91b1ed0aSPaul Moore 	if (ret_val != 0)
1011*91b1ed0aSPaul Moore 		return ret_val;
1012446fda4fSPaul Moore 
1013*91b1ed0aSPaul Moore 	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
1014446fda4fSPaul Moore 		ret_val = cipso_v4_map_cat_rbm_hton(doi_def,
1015446fda4fSPaul Moore 						    secattr->mls_cat,
1016446fda4fSPaul Moore 						    secattr->mls_cat_len,
1017*91b1ed0aSPaul Moore 						    &buffer[4],
1018*91b1ed0aSPaul Moore 						    buffer_len - 4);
1019446fda4fSPaul Moore 		if (ret_val < 0)
1020*91b1ed0aSPaul Moore 			return ret_val;
1021446fda4fSPaul Moore 
1022446fda4fSPaul Moore 		/* This will send packets using the "optimized" format when
1023446fda4fSPaul Moore 		 * possibile as specified in  section 3.4.2.6 of the
1024446fda4fSPaul Moore 		 * CIPSO draft. */
1025701a90baSPaul Moore 		if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10)
1026*91b1ed0aSPaul Moore 			tag_len = 14;
1027701a90baSPaul Moore 		else
1028*91b1ed0aSPaul Moore 			tag_len = 4 + ret_val;
1029*91b1ed0aSPaul Moore 	} else
1030*91b1ed0aSPaul Moore 		tag_len = 4;
1031446fda4fSPaul Moore 
1032*91b1ed0aSPaul Moore 	buffer[0] = 0x01;
1033*91b1ed0aSPaul Moore 	buffer[1] = tag_len;
1034*91b1ed0aSPaul Moore 	buffer[3] = level;
1035446fda4fSPaul Moore 
1036*91b1ed0aSPaul Moore 	return tag_len;
1037446fda4fSPaul Moore }
1038446fda4fSPaul Moore 
1039446fda4fSPaul Moore /**
1040446fda4fSPaul Moore  * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag
1041446fda4fSPaul Moore  * @doi_def: the DOI definition
1042446fda4fSPaul Moore  * @tag: the CIPSO tag
1043446fda4fSPaul Moore  * @secattr: the security attributes
1044446fda4fSPaul Moore  *
1045446fda4fSPaul Moore  * Description:
1046446fda4fSPaul Moore  * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security
1047446fda4fSPaul Moore  * attributes in @secattr.  Return zero on success, negatives values on
1048446fda4fSPaul Moore  * failure.
1049446fda4fSPaul Moore  *
1050446fda4fSPaul Moore  */
1051446fda4fSPaul Moore static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
1052446fda4fSPaul Moore 				 const unsigned char *tag,
1053446fda4fSPaul Moore 				 struct netlbl_lsm_secattr *secattr)
1054446fda4fSPaul Moore {
1055446fda4fSPaul Moore 	int ret_val;
1056446fda4fSPaul Moore 	u8 tag_len = tag[1];
1057446fda4fSPaul Moore 	u32 level;
1058446fda4fSPaul Moore 
1059446fda4fSPaul Moore 	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
1060446fda4fSPaul Moore 	if (ret_val != 0)
1061446fda4fSPaul Moore 		return ret_val;
1062446fda4fSPaul Moore 	secattr->mls_lvl = level;
1063701a90baSPaul Moore 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
1064446fda4fSPaul Moore 
1065446fda4fSPaul Moore 	if (tag_len > 4) {
1066446fda4fSPaul Moore 		switch (doi_def->type) {
1067446fda4fSPaul Moore 		case CIPSO_V4_MAP_PASS:
1068446fda4fSPaul Moore 			secattr->mls_cat_len = tag_len - 4;
1069446fda4fSPaul Moore 			break;
1070446fda4fSPaul Moore 		case CIPSO_V4_MAP_STD:
1071446fda4fSPaul Moore 			secattr->mls_cat_len =
1072446fda4fSPaul Moore 				doi_def->map.std->cat.local_size;
1073446fda4fSPaul Moore 			break;
1074446fda4fSPaul Moore 		}
1075446fda4fSPaul Moore 		secattr->mls_cat = kzalloc(secattr->mls_cat_len, GFP_ATOMIC);
1076446fda4fSPaul Moore 		if (secattr->mls_cat == NULL)
1077446fda4fSPaul Moore 			return -ENOMEM;
1078446fda4fSPaul Moore 
1079446fda4fSPaul Moore 		ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
1080446fda4fSPaul Moore 						    &tag[4],
1081446fda4fSPaul Moore 						    tag_len - 4,
1082446fda4fSPaul Moore 						    secattr->mls_cat,
1083446fda4fSPaul Moore 						    secattr->mls_cat_len);
1084446fda4fSPaul Moore 		if (ret_val < 0) {
1085446fda4fSPaul Moore 			kfree(secattr->mls_cat);
1086446fda4fSPaul Moore 			return ret_val;
1087701a90baSPaul Moore 		} else if (ret_val > 0) {
1088446fda4fSPaul Moore 			secattr->mls_cat_len = ret_val;
1089701a90baSPaul Moore 			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
1090701a90baSPaul Moore 		}
1091446fda4fSPaul Moore 	}
1092446fda4fSPaul Moore 
1093446fda4fSPaul Moore 	return 0;
1094446fda4fSPaul Moore }
1095446fda4fSPaul Moore 
1096446fda4fSPaul Moore /**
1097446fda4fSPaul Moore  * cipso_v4_validate - Validate a CIPSO option
1098446fda4fSPaul Moore  * @option: the start of the option, on error it is set to point to the error
1099446fda4fSPaul Moore  *
1100446fda4fSPaul Moore  * Description:
1101446fda4fSPaul Moore  * This routine is called to validate a CIPSO option, it checks all of the
1102446fda4fSPaul Moore  * fields to ensure that they are at least valid, see the draft snippet below
1103446fda4fSPaul Moore  * for details.  If the option is valid then a zero value is returned and
1104446fda4fSPaul Moore  * the value of @option is unchanged.  If the option is invalid then a
1105446fda4fSPaul Moore  * non-zero value is returned and @option is adjusted to point to the
1106446fda4fSPaul Moore  * offending portion of the option.  From the IETF draft ...
1107446fda4fSPaul Moore  *
1108446fda4fSPaul Moore  *  "If any field within the CIPSO options, such as the DOI identifier, is not
1109446fda4fSPaul Moore  *   recognized the IP datagram is discarded and an ICMP 'parameter problem'
1110446fda4fSPaul Moore  *   (type 12) is generated and returned.  The ICMP code field is set to 'bad
1111446fda4fSPaul Moore  *   parameter' (code 0) and the pointer is set to the start of the CIPSO field
1112446fda4fSPaul Moore  *   that is unrecognized."
1113446fda4fSPaul Moore  *
1114446fda4fSPaul Moore  */
1115446fda4fSPaul Moore int cipso_v4_validate(unsigned char **option)
1116446fda4fSPaul Moore {
1117446fda4fSPaul Moore 	unsigned char *opt = *option;
1118446fda4fSPaul Moore 	unsigned char *tag;
1119446fda4fSPaul Moore 	unsigned char opt_iter;
1120446fda4fSPaul Moore 	unsigned char err_offset = 0;
1121446fda4fSPaul Moore 	u8 opt_len;
1122446fda4fSPaul Moore 	u8 tag_len;
1123446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def = NULL;
1124446fda4fSPaul Moore 	u32 tag_iter;
1125446fda4fSPaul Moore 
1126446fda4fSPaul Moore 	/* caller already checks for length values that are too large */
1127446fda4fSPaul Moore 	opt_len = opt[1];
1128446fda4fSPaul Moore 	if (opt_len < 8) {
1129446fda4fSPaul Moore 		err_offset = 1;
1130446fda4fSPaul Moore 		goto validate_return;
1131446fda4fSPaul Moore 	}
1132446fda4fSPaul Moore 
1133446fda4fSPaul Moore 	rcu_read_lock();
1134714e85beSAl Viro 	doi_def = cipso_v4_doi_getdef(ntohl(*((__be32 *)&opt[2])));
1135446fda4fSPaul Moore 	if (doi_def == NULL) {
1136446fda4fSPaul Moore 		err_offset = 2;
1137446fda4fSPaul Moore 		goto validate_return_locked;
1138446fda4fSPaul Moore 	}
1139446fda4fSPaul Moore 
1140446fda4fSPaul Moore 	opt_iter = 6;
1141446fda4fSPaul Moore 	tag = opt + opt_iter;
1142446fda4fSPaul Moore 	while (opt_iter < opt_len) {
1143446fda4fSPaul Moore 		for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
1144446fda4fSPaul Moore 			if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID ||
1145446fda4fSPaul Moore 			    ++tag_iter == CIPSO_V4_TAG_MAXCNT) {
1146446fda4fSPaul Moore 				err_offset = opt_iter;
1147446fda4fSPaul Moore 				goto validate_return_locked;
1148446fda4fSPaul Moore 			}
1149446fda4fSPaul Moore 
1150446fda4fSPaul Moore 		tag_len = tag[1];
1151446fda4fSPaul Moore 		if (tag_len > (opt_len - opt_iter)) {
1152446fda4fSPaul Moore 			err_offset = opt_iter + 1;
1153446fda4fSPaul Moore 			goto validate_return_locked;
1154446fda4fSPaul Moore 		}
1155446fda4fSPaul Moore 
1156446fda4fSPaul Moore 		switch (tag[0]) {
1157446fda4fSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
1158446fda4fSPaul Moore 			if (tag_len < 4) {
1159446fda4fSPaul Moore 				err_offset = opt_iter + 1;
1160446fda4fSPaul Moore 				goto validate_return_locked;
1161446fda4fSPaul Moore 			}
1162446fda4fSPaul Moore 
1163446fda4fSPaul Moore 			/* We are already going to do all the verification
1164446fda4fSPaul Moore 			 * necessary at the socket layer so from our point of
1165446fda4fSPaul Moore 			 * view it is safe to turn these checks off (and less
1166446fda4fSPaul Moore 			 * work), however, the CIPSO draft says we should do
1167446fda4fSPaul Moore 			 * all the CIPSO validations here but it doesn't
1168446fda4fSPaul Moore 			 * really specify _exactly_ what we need to validate
1169446fda4fSPaul Moore 			 * ... so, just make it a sysctl tunable. */
1170446fda4fSPaul Moore 			if (cipso_v4_rbm_strictvalid) {
1171446fda4fSPaul Moore 				if (cipso_v4_map_lvl_valid(doi_def,
1172446fda4fSPaul Moore 							   tag[3]) < 0) {
1173446fda4fSPaul Moore 					err_offset = opt_iter + 3;
1174446fda4fSPaul Moore 					goto validate_return_locked;
1175446fda4fSPaul Moore 				}
1176446fda4fSPaul Moore 				if (tag_len > 4 &&
1177446fda4fSPaul Moore 				    cipso_v4_map_cat_rbm_valid(doi_def,
1178446fda4fSPaul Moore 							    &tag[4],
1179446fda4fSPaul Moore 							    tag_len - 4) < 0) {
1180446fda4fSPaul Moore 					err_offset = opt_iter + 4;
1181446fda4fSPaul Moore 					goto validate_return_locked;
1182446fda4fSPaul Moore 				}
1183446fda4fSPaul Moore 			}
1184446fda4fSPaul Moore 			break;
1185446fda4fSPaul Moore 		default:
1186446fda4fSPaul Moore 			err_offset = opt_iter;
1187446fda4fSPaul Moore 			goto validate_return_locked;
1188446fda4fSPaul Moore 		}
1189446fda4fSPaul Moore 
1190446fda4fSPaul Moore 		tag += tag_len;
1191446fda4fSPaul Moore 		opt_iter += tag_len;
1192446fda4fSPaul Moore 	}
1193446fda4fSPaul Moore 
1194446fda4fSPaul Moore validate_return_locked:
1195446fda4fSPaul Moore 	rcu_read_unlock();
1196446fda4fSPaul Moore validate_return:
1197446fda4fSPaul Moore 	*option = opt + err_offset;
1198446fda4fSPaul Moore 	return err_offset;
1199446fda4fSPaul Moore }
1200446fda4fSPaul Moore 
1201446fda4fSPaul Moore /**
1202446fda4fSPaul Moore  * cipso_v4_error - Send the correct reponse for a bad packet
1203446fda4fSPaul Moore  * @skb: the packet
1204446fda4fSPaul Moore  * @error: the error code
1205446fda4fSPaul Moore  * @gateway: CIPSO gateway flag
1206446fda4fSPaul Moore  *
1207446fda4fSPaul Moore  * Description:
1208446fda4fSPaul Moore  * Based on the error code given in @error, send an ICMP error message back to
1209446fda4fSPaul Moore  * the originating host.  From the IETF draft ...
1210446fda4fSPaul Moore  *
1211446fda4fSPaul Moore  *  "If the contents of the CIPSO [option] are valid but the security label is
1212446fda4fSPaul Moore  *   outside of the configured host or port label range, the datagram is
1213446fda4fSPaul Moore  *   discarded and an ICMP 'destination unreachable' (type 3) is generated and
1214446fda4fSPaul Moore  *   returned.  The code field of the ICMP is set to 'communication with
1215446fda4fSPaul Moore  *   destination network administratively prohibited' (code 9) or to
1216446fda4fSPaul Moore  *   'communication with destination host administratively prohibited'
1217446fda4fSPaul Moore  *   (code 10).  The value of the code is dependent on whether the originator
1218446fda4fSPaul Moore  *   of the ICMP message is acting as a CIPSO host or a CIPSO gateway.  The
1219446fda4fSPaul Moore  *   recipient of the ICMP message MUST be able to handle either value.  The
1220446fda4fSPaul Moore  *   same procedure is performed if a CIPSO [option] can not be added to an
1221446fda4fSPaul Moore  *   IP packet because it is too large to fit in the IP options area."
1222446fda4fSPaul Moore  *
1223446fda4fSPaul Moore  *  "If the error is triggered by receipt of an ICMP message, the message is
1224446fda4fSPaul Moore  *   discarded and no response is permitted (consistent with general ICMP
1225446fda4fSPaul Moore  *   processing rules)."
1226446fda4fSPaul Moore  *
1227446fda4fSPaul Moore  */
1228446fda4fSPaul Moore void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
1229446fda4fSPaul Moore {
1230446fda4fSPaul Moore 	if (skb->nh.iph->protocol == IPPROTO_ICMP || error != -EACCES)
1231446fda4fSPaul Moore 		return;
1232446fda4fSPaul Moore 
1233446fda4fSPaul Moore 	if (gateway)
1234446fda4fSPaul Moore 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
1235446fda4fSPaul Moore 	else
1236446fda4fSPaul Moore 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
1237446fda4fSPaul Moore }
1238446fda4fSPaul Moore 
1239446fda4fSPaul Moore /**
1240446fda4fSPaul Moore  * cipso_v4_socket_setattr - Add a CIPSO option to a socket
1241446fda4fSPaul Moore  * @sock: the socket
1242446fda4fSPaul Moore  * @doi_def: the CIPSO DOI to use
1243446fda4fSPaul Moore  * @secattr: the specific security attributes of the socket
1244446fda4fSPaul Moore  *
1245446fda4fSPaul Moore  * Description:
1246446fda4fSPaul Moore  * Set the CIPSO option on the given socket using the DOI definition and
1247446fda4fSPaul Moore  * security attributes passed to the function.  This function requires
1248446fda4fSPaul Moore  * exclusive access to @sock->sk, which means it either needs to be in the
1249446fda4fSPaul Moore  * process of being created or locked via lock_sock(sock->sk).  Returns zero on
1250446fda4fSPaul Moore  * success and negative values on failure.
1251446fda4fSPaul Moore  *
1252446fda4fSPaul Moore  */
1253446fda4fSPaul Moore int cipso_v4_socket_setattr(const struct socket *sock,
1254446fda4fSPaul Moore 			    const struct cipso_v4_doi *doi_def,
1255446fda4fSPaul Moore 			    const struct netlbl_lsm_secattr *secattr)
1256446fda4fSPaul Moore {
1257446fda4fSPaul Moore 	int ret_val = -EPERM;
1258446fda4fSPaul Moore 	u32 iter;
1259*91b1ed0aSPaul Moore 	unsigned char *buf;
1260446fda4fSPaul Moore 	u32 buf_len = 0;
1261446fda4fSPaul Moore 	u32 opt_len;
1262446fda4fSPaul Moore 	struct ip_options *opt = NULL;
1263446fda4fSPaul Moore 	struct sock *sk;
1264446fda4fSPaul Moore 	struct inet_sock *sk_inet;
1265446fda4fSPaul Moore 	struct inet_connection_sock *sk_conn;
1266446fda4fSPaul Moore 
1267446fda4fSPaul Moore 	/* In the case of sock_create_lite(), the sock->sk field is not
1268446fda4fSPaul Moore 	 * defined yet but it is not a problem as the only users of these
1269446fda4fSPaul Moore 	 * "lite" PF_INET sockets are functions which do an accept() call
1270446fda4fSPaul Moore 	 * afterwards so we will label the socket as part of the accept(). */
1271446fda4fSPaul Moore 	sk = sock->sk;
1272446fda4fSPaul Moore 	if (sk == NULL)
1273446fda4fSPaul Moore 		return 0;
1274446fda4fSPaul Moore 
1275*91b1ed0aSPaul Moore 	/* We allocate the maximum CIPSO option size here so we are probably
1276*91b1ed0aSPaul Moore 	 * being a little wasteful, but it makes our life _much_ easier later
1277*91b1ed0aSPaul Moore 	 * on and after all we are only talking about 40 bytes. */
1278*91b1ed0aSPaul Moore 	buf_len = CIPSO_V4_OPT_LEN_MAX;
1279*91b1ed0aSPaul Moore 	buf = kmalloc(buf_len, GFP_ATOMIC);
1280*91b1ed0aSPaul Moore 	if (buf == NULL) {
1281*91b1ed0aSPaul Moore 		ret_val = -ENOMEM;
1282*91b1ed0aSPaul Moore 		goto socket_setattr_failure;
1283*91b1ed0aSPaul Moore 	}
1284*91b1ed0aSPaul Moore 
1285446fda4fSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
1286446fda4fSPaul Moore 	 * really a good assumption to make but since we only support the MAC
1287446fda4fSPaul Moore 	 * tags right now it is a safe assumption. */
1288446fda4fSPaul Moore 	iter = 0;
1289446fda4fSPaul Moore 	do {
1290*91b1ed0aSPaul Moore 		memset(buf, 0, buf_len);
1291446fda4fSPaul Moore 		switch (doi_def->tags[iter]) {
1292446fda4fSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
1293446fda4fSPaul Moore 			ret_val = cipso_v4_gentag_rbm(doi_def,
1294446fda4fSPaul Moore 						   secattr,
1295*91b1ed0aSPaul Moore 						   &buf[CIPSO_V4_HDR_LEN],
1296*91b1ed0aSPaul Moore 						   buf_len - CIPSO_V4_HDR_LEN);
1297446fda4fSPaul Moore 			break;
1298446fda4fSPaul Moore 		default:
1299446fda4fSPaul Moore 			ret_val = -EPERM;
1300446fda4fSPaul Moore 			goto socket_setattr_failure;
1301446fda4fSPaul Moore 		}
1302446fda4fSPaul Moore 
1303446fda4fSPaul Moore 		iter++;
1304*91b1ed0aSPaul Moore 	} while (ret_val < 0 &&
1305446fda4fSPaul Moore 		 iter < CIPSO_V4_TAG_MAXCNT &&
1306446fda4fSPaul Moore 		 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
1307*91b1ed0aSPaul Moore 	if (ret_val < 0)
1308446fda4fSPaul Moore 		goto socket_setattr_failure;
1309*91b1ed0aSPaul Moore 	cipso_v4_gentag_hdr(doi_def, buf, ret_val);
1310*91b1ed0aSPaul Moore 	buf_len = CIPSO_V4_HDR_LEN + ret_val;
1311446fda4fSPaul Moore 
1312446fda4fSPaul Moore 	/* We can't use ip_options_get() directly because it makes a call to
1313446fda4fSPaul Moore 	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
1314f8687afeSPaul Moore 	 * we won't always have CAP_NET_RAW even though we _always_ want to
1315f8687afeSPaul Moore 	 * set the IPOPT_CIPSO option. */
1316446fda4fSPaul Moore 	opt_len = (buf_len + 3) & ~3;
1317446fda4fSPaul Moore 	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
1318446fda4fSPaul Moore 	if (opt == NULL) {
1319446fda4fSPaul Moore 		ret_val = -ENOMEM;
1320446fda4fSPaul Moore 		goto socket_setattr_failure;
1321446fda4fSPaul Moore 	}
1322446fda4fSPaul Moore 	memcpy(opt->__data, buf, buf_len);
1323446fda4fSPaul Moore 	opt->optlen = opt_len;
1324446fda4fSPaul Moore 	opt->is_data = 1;
1325f8687afeSPaul Moore 	opt->cipso = sizeof(struct iphdr);
1326446fda4fSPaul Moore 	kfree(buf);
1327446fda4fSPaul Moore 	buf = NULL;
1328446fda4fSPaul Moore 
1329446fda4fSPaul Moore 	sk_inet = inet_sk(sk);
1330446fda4fSPaul Moore 	if (sk_inet->is_icsk) {
1331446fda4fSPaul Moore 		sk_conn = inet_csk(sk);
1332446fda4fSPaul Moore 		if (sk_inet->opt)
1333446fda4fSPaul Moore 			sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen;
1334446fda4fSPaul Moore 		sk_conn->icsk_ext_hdr_len += opt->optlen;
1335446fda4fSPaul Moore 		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
1336446fda4fSPaul Moore 	}
1337446fda4fSPaul Moore 	opt = xchg(&sk_inet->opt, opt);
1338446fda4fSPaul Moore 	kfree(opt);
1339446fda4fSPaul Moore 
1340446fda4fSPaul Moore 	return 0;
1341446fda4fSPaul Moore 
1342446fda4fSPaul Moore socket_setattr_failure:
1343446fda4fSPaul Moore 	kfree(buf);
1344446fda4fSPaul Moore 	kfree(opt);
1345446fda4fSPaul Moore 	return ret_val;
1346446fda4fSPaul Moore }
1347446fda4fSPaul Moore 
1348446fda4fSPaul Moore /**
134914a72f53SPaul Moore  * cipso_v4_sock_getattr - Get the security attributes from a sock
135014a72f53SPaul Moore  * @sk: the sock
135114a72f53SPaul Moore  * @secattr: the security attributes
135214a72f53SPaul Moore  *
135314a72f53SPaul Moore  * Description:
135414a72f53SPaul Moore  * Query @sk to see if there is a CIPSO option attached to the sock and if
135514a72f53SPaul Moore  * there is return the CIPSO security attributes in @secattr.  This function
135614a72f53SPaul Moore  * requires that @sk be locked, or privately held, but it does not do any
135714a72f53SPaul Moore  * locking itself.  Returns zero on success and negative values on failure.
135814a72f53SPaul Moore  *
135914a72f53SPaul Moore  */
136014a72f53SPaul Moore int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
136114a72f53SPaul Moore {
136214a72f53SPaul Moore 	int ret_val = -ENOMSG;
136314a72f53SPaul Moore 	struct inet_sock *sk_inet;
136414a72f53SPaul Moore 	unsigned char *cipso_ptr;
136514a72f53SPaul Moore 	u32 doi;
136614a72f53SPaul Moore 	struct cipso_v4_doi *doi_def;
136714a72f53SPaul Moore 
136814a72f53SPaul Moore 	sk_inet = inet_sk(sk);
136914a72f53SPaul Moore 	if (sk_inet->opt == NULL || sk_inet->opt->cipso == 0)
137014a72f53SPaul Moore 		return -ENOMSG;
137114a72f53SPaul Moore 	cipso_ptr = sk_inet->opt->__data + sk_inet->opt->cipso -
137214a72f53SPaul Moore 		sizeof(struct iphdr);
137314a72f53SPaul Moore 	ret_val = cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr);
137414a72f53SPaul Moore 	if (ret_val == 0)
137514a72f53SPaul Moore 		return ret_val;
137614a72f53SPaul Moore 
1377714e85beSAl Viro 	doi = ntohl(*(__be32 *)&cipso_ptr[2]);
137814a72f53SPaul Moore 	rcu_read_lock();
137914a72f53SPaul Moore 	doi_def = cipso_v4_doi_getdef(doi);
138014a72f53SPaul Moore 	if (doi_def == NULL) {
138114a72f53SPaul Moore 		rcu_read_unlock();
138214a72f53SPaul Moore 		return -ENOMSG;
138314a72f53SPaul Moore 	}
1384*91b1ed0aSPaul Moore 
1385*91b1ed0aSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
1386*91b1ed0aSPaul Moore 	 * really a good assumption to make but since we only support the MAC
1387*91b1ed0aSPaul Moore 	 * tags right now it is a safe assumption. */
138814a72f53SPaul Moore 	switch (cipso_ptr[6]) {
138914a72f53SPaul Moore 	case CIPSO_V4_TAG_RBITMAP:
139014a72f53SPaul Moore 		ret_val = cipso_v4_parsetag_rbm(doi_def,
139114a72f53SPaul Moore 						&cipso_ptr[6],
139214a72f53SPaul Moore 						secattr);
139314a72f53SPaul Moore 		break;
139414a72f53SPaul Moore 	}
139514a72f53SPaul Moore 	rcu_read_unlock();
139614a72f53SPaul Moore 
139714a72f53SPaul Moore 	return ret_val;
139814a72f53SPaul Moore }
139914a72f53SPaul Moore 
140014a72f53SPaul Moore /**
1401446fda4fSPaul Moore  * cipso_v4_socket_getattr - Get the security attributes from a socket
1402446fda4fSPaul Moore  * @sock: the socket
1403446fda4fSPaul Moore  * @secattr: the security attributes
1404446fda4fSPaul Moore  *
1405446fda4fSPaul Moore  * Description:
1406446fda4fSPaul Moore  * Query @sock to see if there is a CIPSO option attached to the socket and if
1407446fda4fSPaul Moore  * there is return the CIPSO security attributes in @secattr.  Returns zero on
1408446fda4fSPaul Moore  * success and negative values on failure.
1409446fda4fSPaul Moore  *
1410446fda4fSPaul Moore  */
1411446fda4fSPaul Moore int cipso_v4_socket_getattr(const struct socket *sock,
1412446fda4fSPaul Moore 			    struct netlbl_lsm_secattr *secattr)
1413446fda4fSPaul Moore {
141414a72f53SPaul Moore 	int ret_val;
1415446fda4fSPaul Moore 
141614a72f53SPaul Moore 	lock_sock(sock->sk);
141714a72f53SPaul Moore 	ret_val = cipso_v4_sock_getattr(sock->sk, secattr);
141814a72f53SPaul Moore 	release_sock(sock->sk);
1419446fda4fSPaul Moore 
1420446fda4fSPaul Moore 	return ret_val;
1421446fda4fSPaul Moore }
1422446fda4fSPaul Moore 
1423446fda4fSPaul Moore /**
1424446fda4fSPaul Moore  * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option
1425446fda4fSPaul Moore  * @skb: the packet
1426446fda4fSPaul Moore  * @secattr: the security attributes
1427446fda4fSPaul Moore  *
1428446fda4fSPaul Moore  * Description:
1429446fda4fSPaul Moore  * Parse the given packet's CIPSO option and return the security attributes.
1430446fda4fSPaul Moore  * Returns zero on success and negative values on failure.
1431446fda4fSPaul Moore  *
1432446fda4fSPaul Moore  */
1433446fda4fSPaul Moore int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
1434446fda4fSPaul Moore 			    struct netlbl_lsm_secattr *secattr)
1435446fda4fSPaul Moore {
1436446fda4fSPaul Moore 	int ret_val = -ENOMSG;
1437446fda4fSPaul Moore 	unsigned char *cipso_ptr;
1438446fda4fSPaul Moore 	u32 doi;
1439446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def;
1440446fda4fSPaul Moore 
1441446fda4fSPaul Moore 	cipso_ptr = CIPSO_V4_OPTPTR(skb);
1442446fda4fSPaul Moore 	if (cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr) == 0)
1443446fda4fSPaul Moore 		return 0;
1444446fda4fSPaul Moore 
1445714e85beSAl Viro 	doi = ntohl(*(__be32 *)&cipso_ptr[2]);
1446446fda4fSPaul Moore 	rcu_read_lock();
1447446fda4fSPaul Moore 	doi_def = cipso_v4_doi_getdef(doi);
1448446fda4fSPaul Moore 	if (doi_def == NULL)
1449446fda4fSPaul Moore 		goto skbuff_getattr_return;
1450*91b1ed0aSPaul Moore 
1451*91b1ed0aSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
1452*91b1ed0aSPaul Moore 	 * really a good assumption to make but since we only support the MAC
1453*91b1ed0aSPaul Moore 	 * tags right now it is a safe assumption. */
1454446fda4fSPaul Moore 	switch (cipso_ptr[6]) {
1455446fda4fSPaul Moore 	case CIPSO_V4_TAG_RBITMAP:
1456446fda4fSPaul Moore 		ret_val = cipso_v4_parsetag_rbm(doi_def,
1457446fda4fSPaul Moore 						&cipso_ptr[6],
1458446fda4fSPaul Moore 						secattr);
1459446fda4fSPaul Moore 		break;
1460446fda4fSPaul Moore 	}
1461446fda4fSPaul Moore 
1462446fda4fSPaul Moore skbuff_getattr_return:
1463446fda4fSPaul Moore 	rcu_read_unlock();
1464446fda4fSPaul Moore 	return ret_val;
1465446fda4fSPaul Moore }
1466446fda4fSPaul Moore 
1467446fda4fSPaul Moore /*
1468446fda4fSPaul Moore  * Setup Functions
1469446fda4fSPaul Moore  */
1470446fda4fSPaul Moore 
1471446fda4fSPaul Moore /**
1472446fda4fSPaul Moore  * cipso_v4_init - Initialize the CIPSO module
1473446fda4fSPaul Moore  *
1474446fda4fSPaul Moore  * Description:
1475446fda4fSPaul Moore  * Initialize the CIPSO module and prepare it for use.  Returns zero on success
1476446fda4fSPaul Moore  * and negative values on failure.
1477446fda4fSPaul Moore  *
1478446fda4fSPaul Moore  */
1479446fda4fSPaul Moore static int __init cipso_v4_init(void)
1480446fda4fSPaul Moore {
1481446fda4fSPaul Moore 	int ret_val;
1482446fda4fSPaul Moore 
1483446fda4fSPaul Moore 	ret_val = cipso_v4_cache_init();
1484446fda4fSPaul Moore 	if (ret_val != 0)
1485446fda4fSPaul Moore 		panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n",
1486446fda4fSPaul Moore 		      ret_val);
1487446fda4fSPaul Moore 
1488446fda4fSPaul Moore 	return 0;
1489446fda4fSPaul Moore }
1490446fda4fSPaul Moore 
1491446fda4fSPaul Moore subsys_initcall(cipso_v4_init);
1492