Skip to content

Commit

Permalink
netlabel: Replace protocol/NetLabel linking with refrerence counts
Browse files Browse the repository at this point in the history
NetLabel has always had a list of backpointers in the CIPSO DOI definition
structure which pointed to the NetLabel LSM domain mapping structures which
referenced the CIPSO DOI struct.  The rationale for this was that when an
administrator removed a CIPSO DOI from the system all of the associated
NetLabel LSM domain mappings should be removed as well; a list of
backpointers made this a simple operation.

Unfortunately, while the backpointers did make the removal easier they were
a bit of a mess from an implementation point of view which was making
further development difficult.  Since the removal of a CIPSO DOI is a
realtively rare event it seems to make sense to remove this backpointer
list as the optimization was hurting us more then it was helping.  However,
we still need to be able to track when a CIPSO DOI definition is being used
so replace the backpointer list with a reference count.  In order to
preserve the current functionality of removing the associated LSM domain
mappings when a CIPSO DOI is removed we walk the LSM domain mapping table,
removing the relevant entries.

Signed-off-by: Paul Moore <[email protected]>
Reviewed-by: James Morris <[email protected]>
  • Loading branch information
pcmoore committed Oct 10, 2008
1 parent a813429 commit b1edeb1
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 266 deletions.
21 changes: 11 additions & 10 deletions include/net/cipso_ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <linux/net.h>
#include <linux/skbuff.h>
#include <net/netlabel.h>
#include <asm/atomic.h>

/* known doi values */
#define CIPSO_V4_DOI_UNKNOWN 0x00000000
Expand Down Expand Up @@ -79,10 +80,9 @@ struct cipso_v4_doi {
} map;
u8 tags[CIPSO_V4_TAG_MAXCNT];

u32 valid;
atomic_t refcount;
struct list_head list;
struct rcu_head rcu;
struct list_head dom_list;
};

/* Standard CIPSO mapping table */
Expand Down Expand Up @@ -128,25 +128,26 @@ extern int cipso_v4_rbm_strictvalid;

#ifdef CONFIG_NETLABEL
int cipso_v4_doi_add(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_remove(u32 doi,
struct netlbl_audit *audit_info,
void (*callback) (struct rcu_head * head));
void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi);
void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_walk(u32 *skip_cnt,
int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
void *cb_arg);
int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain);
int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
const char *domain);
#else
static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
{
return -ENOSYS;
}

static inline void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
{
return;
}

static inline int cipso_v4_doi_remove(u32 doi,
struct netlbl_audit *audit_info,
void (*callback) (struct rcu_head * head))
struct netlbl_audit *audit_info)
{
return 0;
}
Expand Down
235 changes: 98 additions & 137 deletions net/ipv4/cipso_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,7 @@
#include <asm/bug.h>
#include <asm/unaligned.h>

struct cipso_v4_domhsh_entry {
char *domain;
u32 valid;
struct list_head list;
struct rcu_head rcu;
};

/* List of available DOI definitions */
/* XXX - Updates should be minimal so having a single lock for the
* cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
* okay. */
/* XXX - This currently assumes a minimal number of different DOIs in use,
* if in practice there are a lot of different DOIs this list should
* probably be turned into a hash table or something similar so we
Expand Down Expand Up @@ -193,25 +183,6 @@ static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
bitmap[byte_spot] &= ~bitmask;
}

/**
* cipso_v4_doi_domhsh_free - Frees a domain list entry
* @entry: the entry's RCU field
*
* Description:
* This function is designed to be used as a callback to the call_rcu()
* function so that the memory allocated to a domain list entry can be released
* safely.
*
*/
static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
{
struct cipso_v4_domhsh_entry *ptr;

ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
kfree(ptr->domain);
kfree(ptr);
}

/**
* cipso_v4_cache_entry_free - Frees a cache entry
* @entry: the entry to free
Expand Down Expand Up @@ -457,7 +428,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
struct cipso_v4_doi *iter;

list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
if (iter->doi == doi && iter->valid)
if (iter->doi == doi && atomic_read(&iter->refcount))
return iter;
return NULL;
}
Expand Down Expand Up @@ -501,9 +472,8 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
}
}

doi_def->valid = 1;
atomic_set(&doi_def->refcount, 1);
INIT_RCU_HEAD(&doi_def->rcu);
INIT_LIST_HEAD(&doi_def->dom_list);

spin_lock(&cipso_v4_doi_list_lock);
if (cipso_v4_doi_search(doi_def->doi) != NULL)
Expand All @@ -518,60 +488,130 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
return -EEXIST;
}

/**
* cipso_v4_doi_free - Frees a DOI definition
* @entry: the entry's RCU field
*
* Description:
* This function frees all of the memory associated with a DOI definition.
*
*/
void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
{
if (doi_def == NULL)
return;

switch (doi_def->type) {
case CIPSO_V4_MAP_STD:
kfree(doi_def->map.std->lvl.cipso);
kfree(doi_def->map.std->lvl.local);
kfree(doi_def->map.std->cat.cipso);
kfree(doi_def->map.std->cat.local);
break;
}
kfree(doi_def);
}

/**
* cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
* @entry: the entry's RCU field
*
* Description:
* This function is designed to be used as a callback to the call_rcu()
* function so that the memory allocated to the DOI definition can be released
* safely.
*
*/
static void cipso_v4_doi_free_rcu(struct rcu_head *entry)
{
struct cipso_v4_doi *doi_def;

doi_def = container_of(entry, struct cipso_v4_doi, rcu);
cipso_v4_doi_free(doi_def);
}

/**
* cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
* @doi: the DOI value
* @audit_secid: the LSM secid to use in the audit message
* @callback: the DOI cleanup/free callback
*
* Description:
* Removes a DOI definition from the CIPSO engine, @callback is called to
* free any memory. The NetLabel routines will be called to release their own
* LSM domain mappings as well as our own domain list. Returns zero on
* success and negative values on failure.
* Removes a DOI definition from the CIPSO engine. The NetLabel routines will
* be called to release their own LSM domain mappings as well as our own
* domain list. Returns zero on success and negative values on failure.
*
*/
int cipso_v4_doi_remove(u32 doi,
struct netlbl_audit *audit_info,
void (*callback) (struct rcu_head * head))
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
{
struct cipso_v4_doi *doi_def;
struct cipso_v4_domhsh_entry *dom_iter;

spin_lock(&cipso_v4_doi_list_lock);
doi_def = cipso_v4_doi_search(doi);
if (doi_def != NULL) {
doi_def->valid = 0;
list_del_rcu(&doi_def->list);
if (doi_def == NULL) {
spin_unlock(&cipso_v4_doi_list_lock);
rcu_read_lock();
list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
if (dom_iter->valid)
netlbl_cfg_map_del(dom_iter->domain,
audit_info);
rcu_read_unlock();
cipso_v4_cache_invalidate();
call_rcu(&doi_def->rcu, callback);
return 0;
return -ENOENT;
}
if (!atomic_dec_and_test(&doi_def->refcount)) {
spin_unlock(&cipso_v4_doi_list_lock);
return -EBUSY;
}
list_del_rcu(&doi_def->list);
spin_unlock(&cipso_v4_doi_list_lock);

return -ENOENT;
cipso_v4_cache_invalidate();
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);

return 0;
}

/**
* cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
* cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
* @doi: the DOI value
*
* Description:
* Searches for a valid DOI definition and if one is found it is returned to
* the caller. Otherwise NULL is returned. The caller must ensure that
* rcu_read_lock() is held while accessing the returned definition.
* rcu_read_lock() is held while accessing the returned definition and the DOI
* definition reference count is decremented when the caller is done.
*
*/
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
{
return cipso_v4_doi_search(doi);
struct cipso_v4_doi *doi_def;

rcu_read_lock();
doi_def = cipso_v4_doi_search(doi);
if (doi_def == NULL)
goto doi_getdef_return;
if (!atomic_inc_not_zero(&doi_def->refcount))
doi_def = NULL;

doi_getdef_return:
rcu_read_unlock();
return doi_def;
}

/**
* cipso_v4_doi_putdef - Releases a reference for the given DOI definition
* @doi_def: the DOI definition
*
* Description:
* Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
*
*/
void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
{
if (doi_def == NULL)
return;

if (!atomic_dec_and_test(&doi_def->refcount))
return;
spin_lock(&cipso_v4_doi_list_lock);
list_del_rcu(&doi_def->list);
spin_unlock(&cipso_v4_doi_list_lock);

cipso_v4_cache_invalidate();
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
}

/**
Expand All @@ -597,7 +637,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt,

rcu_read_lock();
list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
if (iter_doi->valid) {
if (atomic_read(&iter_doi->refcount) > 0) {
if (doi_cnt++ < *skip_cnt)
continue;
ret_val = callback(iter_doi, cb_arg);
Expand All @@ -613,85 +653,6 @@ int cipso_v4_doi_walk(u32 *skip_cnt,
return ret_val;
}

/**
* cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
* @doi_def: the DOI definition
* @domain: the domain to add
*
* Description:
* Adds the @domain to the DOI specified by @doi_def, this function
* should only be called by external functions (i.e. NetLabel). This function
* does allocate memory. Returns zero on success, negative values on failure.
*
*/
int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
{
struct cipso_v4_domhsh_entry *iter;
struct cipso_v4_domhsh_entry *new_dom;

new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
if (new_dom == NULL)
return -ENOMEM;
if (domain) {
new_dom->domain = kstrdup(domain, GFP_KERNEL);
if (new_dom->domain == NULL) {
kfree(new_dom);
return -ENOMEM;
}
}
new_dom->valid = 1;
INIT_RCU_HEAD(&new_dom->rcu);

spin_lock(&cipso_v4_doi_list_lock);
list_for_each_entry(iter, &doi_def->dom_list, list)
if (iter->valid &&
((domain != NULL && iter->domain != NULL &&
strcmp(iter->domain, domain) == 0) ||
(domain == NULL && iter->domain == NULL))) {
spin_unlock(&cipso_v4_doi_list_lock);
kfree(new_dom->domain);
kfree(new_dom);
return -EEXIST;
}
list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
spin_unlock(&cipso_v4_doi_list_lock);

return 0;
}

/**
* cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
* @doi_def: the DOI definition
* @domain: the domain to remove
*
* Description:
* Removes the @domain from the DOI specified by @doi_def, this function
* should only be called by external functions (i.e. NetLabel). Returns zero
* on success and negative values on error.
*
*/
int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
const char *domain)
{
struct cipso_v4_domhsh_entry *iter;

spin_lock(&cipso_v4_doi_list_lock);
list_for_each_entry(iter, &doi_def->dom_list, list)
if (iter->valid &&
((domain != NULL && iter->domain != NULL &&
strcmp(iter->domain, domain) == 0) ||
(domain == NULL && iter->domain == NULL))) {
iter->valid = 0;
list_del_rcu(&iter->list);
spin_unlock(&cipso_v4_doi_list_lock);
call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
return 0;
}
spin_unlock(&cipso_v4_doi_list_lock);

return -ENOENT;
}

/*
* Label Mapping Functions
*/
Expand Down
Loading

0 comments on commit b1edeb1

Please sign in to comment.