diff mbox

[RFC,5/9] snet: introduce snet_event.c and snet_event.h

Message ID 1262437456-24476-6-git-send-email-sam@synack.fr
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Samir Bellabes Jan. 2, 2010, 1:04 p.m. UTC
This patch adds the snet's subsystem responsive of managing events

snet is using the word 'event' for a couple of values [syscall, protocol]. For
example, [listen, tcp] or [sendmsg, dccp] are events.
This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/include/snet_event.h |   20 +++
 security/snet/snet_event.c         |  229 ++++++++++++++++++++++++++++++++++++
 2 files changed, 249 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/include/snet_event.h
 create mode 100644 security/snet/snet_event.c

Comments

Evgeniy Polyakov Jan. 2, 2010, 8:09 p.m. UTC | #1
Hi.

On Sat, Jan 02, 2010 at 02:04:12PM +0100, Samir Bellabes (sam@synack.fr) wrote:
> This patch adds the snet's subsystem responsive of managing events
> 
> snet is using the word 'event' for a couple of values [syscall, protocol]. For
> example, [listen, tcp] or [sendmsg, dccp] are events.
> This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
> in order to manage which events have to be protected.
> With the help of the communication's subsystem, managing orders are coming from
> userspace.
> 
> Signed-off-by: Samir Bellabes <sam@synack.fr>
> ---

> +
> +extern unsigned int event_hash_size;
> +

> +
> +static struct list_head *event_hash;
> +static rwlock_t event_hash_lock = __RW_LOCK_UNLOCKED();
> +

Those are way too generic names, please use snet_ prefix as you did in
other places.


I believe snet should be converted to RCU instead of rw locks.

> +/* lookup for a event_hash - before using this function, lock event_hash_lock */
> +static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
> +						   const u8 protocol)
> +{
> +	unsigned int h = 0;
> +	struct list_head *l;
> +	struct snet_event_entry *s;
> +	struct snet_event t;
> +
> +	if (!event_hash)
> +		return NULL;
> +
> +	/* building the element to look for */
> +	t.syscall = syscall;
> +	t.protocol = protocol;
> +
> +	/* computing its hash value */
> +	h = jhash(&t, sizeof(struct snet_event), 0) % event_hash_size;

You can have better distribution if not simply cut off the remainder.
Also it is a rather expensive operation.

> +	l = &event_hash[h];
> +
> +	list_for_each_entry(s, l, list) {
> +		if ((s->se.protocol == protocol) &&
> +		    (s->se.syscall == syscall)) {
> +			return s;
> +		}
> +	}
> +	return NULL;
> +}

Below comments looks a little bit weird, was it generated automatically
by editor?

> +/* void snet_event_dumpall() */
> +/* { */
> +/*	unsigned int i = 0; */
> +/*	struct list_head *l; */
> +/*	struct snet_event_entry *s; */
> +
> +/*	snet_dbg("entering\n"); */
> +/*	read_lock_bh(&event_hash_lock); */
> +/*	for (i = 0; i < (event_hash_size - 1); i++) { */
> +/*		l = &hash[i]; */
> +/*		list_for_each_entry(s, l, list) { */
> +/*			snet_dbg("[%d, %d, %d]\n", i, */
> +/*				 s->se.protocol, s->se.syscall); */
> +/*		} */
> +/*	} */
> +/*	read_unlock_bh(&event_hash_lock); */
> +/*	snet_dbg("exiting\n"); */
> +/*	return; */
> +/* } */

> +int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
> +{
> +	struct snet_event_entry *data = NULL;
> +	unsigned int h = 0;
> +
> +	data = kzalloc(sizeof(struct snet_event_entry), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	write_lock_bh(&event_hash_lock);
> +	/* check if event is already registered */
> +	if (!event_hash || __snet_event_lookup(syscall, protocol) != NULL) {
> +		write_unlock_bh(&event_hash_lock);
> +		kfree(data);
> +		return -EINVAL;
> +	}

What about single error exiting point?

> +
> +	data->se.syscall = syscall;
> +	data->se.protocol = protocol;
> +	INIT_LIST_HEAD(&(data->list));
> +	h = jhash(&(data->se), sizeof(struct snet_event), 0) % event_hash_size;
> +	list_add_tail(&data->list, &event_hash[h]);
> +	write_unlock_bh(&event_hash_lock);
> +
> +	return 0;
> +}

> +/* init function */
> +int snet_event_init(void)
> +{
> +	int err = 0, i = 0;
> +
> +	event_hash = kzalloc(sizeof(struct list_head) * event_hash_size,
> +			     GFP_KERNEL);
> +	if (!event_hash) {
> +		printk(KERN_WARNING
> +		       "snet: can't alloc memory for event_hash\n");
> +		err = -ENOMEM;
> +		goto out;
> +	}
> +
> +	for (i = 0; i < event_hash_size; i++)
> +		INIT_LIST_HEAD(&(event_hash[i]));

No need for double braces.
Samir Bellabes Jan. 2, 2010, 11:38 p.m. UTC | #2
Hello Evgeniy,

Evgeniy Polyakov <zbr@ioremap.net> writes:
>> +
>> +extern unsigned int event_hash_size;
>> +
>
>> +
>> +static struct list_head *event_hash;
>> +static rwlock_t event_hash_lock = __RW_LOCK_UNLOCKED();
>> +
>
> Those are way too generic names, please use snet_ prefix as you did in
> other places.

done. thank you

> I believe snet should be converted to RCU instead of rw locks.

I see, I will investigated this idea.

>> +/* lookup for a event_hash - before using this function, lock event_hash_lock */
>> +static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
>> +						   const u8 protocol)
>> +{
>> +	unsigned int h = 0;
>> +	struct list_head *l;
>> +	struct snet_event_entry *s;
>> +	struct snet_event t;
>> +
>> +	if (!event_hash)
>> +		return NULL;
>> +
>> +	/* building the element to look for */
>> +	t.syscall = syscall;
>> +	t.protocol = protocol;
>> +
>> +	/* computing its hash value */
>> +	h = jhash(&t, sizeof(struct snet_event), 0) % event_hash_size;
>
> You can have better distribution if not simply cut off the remainder.
> Also it is a rather expensive operation.

true, I replaced the modulo operation to a bitmask operations.

>> +	l = &event_hash[h];
>> +
>> +	list_for_each_entry(s, l, list) {
>> +		if ((s->se.protocol == protocol) &&
>> +		    (s->se.syscall == syscall)) {
>> +			return s;
>> +		}
>> +	}
>> +	return NULL;
>> +}
>
> Below comments looks a little bit weird, was it generated automatically
> by editor?

no, it's was a unused debug function. I deleted this comment.

>> +/* void snet_event_dumpall() */
>> +/* { */
>> +/*	unsigned int i = 0; */
>> +/*	struct list_head *l; */
>> +/*	struct snet_event_entry *s; */
>> +
>> +/*	snet_dbg("entering\n"); */
>> +/*	read_lock_bh(&event_hash_lock); */
>> +/*	for (i = 0; i < (event_hash_size - 1); i++) { */
>> +/*		l = &hash[i]; */
>> +/*		list_for_each_entry(s, l, list) { */
>> +/*			snet_dbg("[%d, %d, %d]\n", i, */
>> +/*				 s->se.protocol, s->se.syscall); */
>> +/*		} */
>> +/*	} */
>> +/*	read_unlock_bh(&event_hash_lock); */
>> +/*	snet_dbg("exiting\n"); */
>> +/*	return; */
>> +/* } */
>
>> +int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
>> +{
>> +	struct snet_event_entry *data = NULL;
>> +	unsigned int h = 0;
>> +
>> +	data = kzalloc(sizeof(struct snet_event_entry), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	write_lock_bh(&event_hash_lock);
>> +	/* check if event is already registered */
>> +	if (!event_hash || __snet_event_lookup(syscall, protocol) != NULL) {
>> +		write_unlock_bh(&event_hash_lock);
>> +		kfree(data);
>> +		return -EINVAL;
>> +	}
>
> What about single error exiting point?

done, thanks

>> +
>> +	data->se.syscall = syscall;
>> +	data->se.protocol = protocol;
>> +	INIT_LIST_HEAD(&(data->list));
>> +	h = jhash(&(data->se), sizeof(struct snet_event), 0) % event_hash_size;
>> +	list_add_tail(&data->list, &event_hash[h]);
>> +	write_unlock_bh(&event_hash_lock);
>> +
>> +	return 0;
>> +}
>
>> +/* init function */
>> +int snet_event_init(void)
>> +{
>> +	int err = 0, i = 0;
>> +
>> +	event_hash = kzalloc(sizeof(struct list_head) * event_hash_size,
>> +			     GFP_KERNEL);
>> +	if (!event_hash) {
>> +		printk(KERN_WARNING
>> +		       "snet: can't alloc memory for event_hash\n");
>> +		err = -ENOMEM;
>> +		goto out;
>> +	}
>> +
>> +	for (i = 0; i < event_hash_size; i++)
>> +		INIT_LIST_HEAD(&(event_hash[i]));
>
> No need for double braces.

again fixed.

thanks for your time Evgeniy,
sam
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Serge E. Hallyn Jan. 4, 2010, 7:08 p.m. UTC | #3
Quoting Samir Bellabes (sam@synack.fr):
> This patch adds the snet's subsystem responsive of managing events
> 
> snet is using the word 'event' for a couple of values [syscall, protocol]. For
> example, [listen, tcp] or [sendmsg, dccp] are events.
> This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
> in order to manage which events have to be protected.
> With the help of the communication's subsystem, managing orders are coming from
> userspace.
> 
> Signed-off-by: Samir Bellabes <sam@synack.fr>
> ---
>  security/snet/include/snet_event.h |   20 +++
>  security/snet/snet_event.c         |  229 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 249 insertions(+), 0 deletions(-)
>  create mode 100644 security/snet/include/snet_event.h
>  create mode 100644 security/snet/snet_event.c
> 
> diff --git a/security/snet/include/snet_event.h b/security/snet/include/snet_event.h
> new file mode 100644
> index 0000000..2c71ca7
> --- /dev/null
> +++ b/security/snet/include/snet_event.h
> @@ -0,0 +1,20 @@
> +#ifndef _SNET_EVENT_H
> +#define _SNET_EVENT_H
> +#include <linux/skbuff.h>
> +
> +extern unsigned int event_hash_size;
> +
> +/* manipulate the events hash table */
> +int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
> +int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
> +int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
> +int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
> +void snet_event_flush(void);
> +void snet_event_dumpall(void);
> +
> +/* init function */
> +int snet_event_init(void);
> +/* exit funtion */
> +int snet_event_exit(void);
> +
> +#endif /* _SNET_EVENT_H */
> diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
> new file mode 100644
> index 0000000..6ac5646
> --- /dev/null
> +++ b/security/snet/snet_event.c
> @@ -0,0 +1,229 @@
> +#include <linux/spinlock.h>
> +#include <linux/list.h>
> +#include <linux/jhash.h>
> +#include <linux/slab.h>
> +#include <linux/netlink.h>
> +
> +#include "snet.h"
> +#include "snet_event.h"
> +#include "snet_netlink.h"
> +
> +static struct list_head *event_hash;
> +static rwlock_t event_hash_lock = __RW_LOCK_UNLOCKED();
> +
> +struct snet_event_entry {
> +	struct list_head list;
> +	struct snet_event se;
> +};
> +
> +/* lookup for a event_hash - before using this function, lock event_hash_lock */
> +static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
> +						   const u8 protocol)
> +{
> +	unsigned int h = 0;
> +	struct list_head *l;
> +	struct snet_event_entry *s;
> +	struct snet_event t;
> +
> +	if (!event_hash)
> +		return NULL;
> +
> +	/* building the element to look for */
> +	t.syscall = syscall;
> +	t.protocol = protocol;
> +
> +	/* computing its hash value */
> +	h = jhash(&t, sizeof(struct snet_event), 0) % event_hash_size;
> +	l = &event_hash[h];
> +
> +	list_for_each_entry(s, l, list) {
> +		if ((s->se.protocol == protocol) &&
> +		    (s->se.syscall == syscall)) {
> +			return s;
> +		}
> +	}
> +	return NULL;
> +}
> +
> +int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
> +{
> +	unsigned int i = 0, n = 0;
> +	int ret = -1;
> +	unsigned hashs_to_skip = cb->args[0];
> +	unsigned events_to_skip = cb->args[1];
> +	struct list_head *l;
> +	struct snet_event_entry *s;
> +
> +	read_lock_bh(&event_hash_lock);
> +
> +	if (!event_hash)
> +		goto errout;
> +
> +	for (i = 0; i < event_hash_size; i++) {
> +		if (i < hashs_to_skip)
> +			continue;

What is this?

> +		l = &event_hash[i];
> +		n = 0;
> +		list_for_each_entry(s, l, list) {
> +			if (++n < events_to_skip)
> +				continue;
> +			ret = snet_nl_list_fill_info(skb,
> +						     NETLINK_CB(cb->skb).pid,
> +						     cb->nlh->nlmsg_seq,
> +						     NLM_F_MULTI,
> +						     s->se.protocol,
> +						     s->se.syscall);
> +			if (ret < 0)
> +				goto errout;

So if it returns 0, presumably meaning successfully handled, you
want to go on processing any duplicates?

> +		}
> +	}
> +
> +errout:
> +	read_unlock_bh(&event_hash_lock);
> +
> +	cb->args[0] = i;
> +	cb->args[1] = n;
> +	return skb->len;
> +}
> +
> +/* void snet_event_dumpall() */
> +/* { */
> +/*	unsigned int i = 0; */
> +/*	struct list_head *l; */
> +/*	struct snet_event_entry *s; */
> +
> +/*	snet_dbg("entering\n"); */
> +/*	read_lock_bh(&event_hash_lock); */
> +/*	for (i = 0; i < (event_hash_size - 1); i++) { */
> +/*		l = &hash[i]; */
> +/*		list_for_each_entry(s, l, list) { */
> +/*			snet_dbg("[%d, %d, %d]\n", i, */
> +/*				 s->se.protocol, s->se.syscall); */
> +/*		} */
> +/*	} */
> +/*	read_unlock_bh(&event_hash_lock); */
> +/*	snet_dbg("exiting\n"); */
> +/*	return; */
> +/* } */
> +
> +/*
> + * check if a event is registered or not
> + * return 1 if event is registered, 0 if not
> + */
> +int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
> +{
> +	int ret = 0;
> +
> +	read_lock_bh(&event_hash_lock);
> +	if (__snet_event_lookup(syscall, protocol) != NULL)
> +		ret = 1;
> +	read_unlock_bh(&event_hash_lock);
> +	return ret;
> +}
> +
> +/* adding a event */
> +int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
> +{
> +	struct snet_event_entry *data = NULL;
> +	unsigned int h = 0;
> +
> +	data = kzalloc(sizeof(struct snet_event_entry), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	write_lock_bh(&event_hash_lock);
> +	/* check if event is already registered */
> +	if (!event_hash || __snet_event_lookup(syscall, protocol) != NULL) {
> +		write_unlock_bh(&event_hash_lock);
> +		kfree(data);
> +		return -EINVAL;

-EBUSY to help debugging?

> +	}
> +
> +	data->se.syscall = syscall;
> +	data->se.protocol = protocol;
> +	INIT_LIST_HEAD(&(data->list));
> +	h = jhash(&(data->se), sizeof(struct snet_event), 0) % event_hash_size;
> +	list_add_tail(&data->list, &event_hash[h]);
> +	write_unlock_bh(&event_hash_lock);
> +
> +	return 0;
> +}
> +
> +/* removing a event */
> +int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
> +{
> +	struct snet_event_entry *data = NULL;
> +
> +	write_lock_bh(&event_hash_lock);
> +	data = __snet_event_lookup(syscall, protocol);
> +	if (data == NULL) {
> +		write_unlock_bh(&event_hash_lock);
> +		return -EINVAL;
> +	}
> +
> +	list_del(&data->list);
> +	write_unlock_bh(&event_hash_lock);
> +	kfree(data);
> +	return 0;
> +}
> +
> +/* flushing all events */
> +void __snet_event_flush(void)
> +{
> +	struct snet_event_entry *data = NULL;
> +	unsigned int i = 0;
> +
> +	for (i = 0; i < event_hash_size; i++) {
> +		while (!list_empty(&event_hash[i])) {
> +			data = list_entry(event_hash[i].next,
> +					  struct snet_event_entry, list);
> +			list_del(&data->list);
> +			kfree(data);
> +		}
> +	}
> +	return;
> +}
> +
> +void snet_event_flush(void)
> +{
> +	write_lock_bh(&event_hash_lock);
> +	if (event_hash)
> +		__snet_event_flush();
> +	write_unlock_bh(&event_hash_lock);
> +	return;
> +}
> +
> +/* init function */
> +int snet_event_init(void)
> +{
> +	int err = 0, i = 0;
> +
> +	event_hash = kzalloc(sizeof(struct list_head) * event_hash_size,
> +			     GFP_KERNEL);
> +	if (!event_hash) {
> +		printk(KERN_WARNING
> +		       "snet: can't alloc memory for event_hash\n");
> +		err = -ENOMEM;
> +		goto out;
> +	}
> +
> +	for (i = 0; i < event_hash_size; i++)
> +		INIT_LIST_HEAD(&(event_hash[i]));
> +
> +out:
> +	return err;
> +}
> +
> +/* exit function */
> +int snet_event_exit(void)
> +{
> +	write_lock_bh(&event_hash_lock);
> +	if (event_hash) {
> +		__snet_event_flush();
> +		kfree(event_hash);
> +		event_hash = NULL;
> +	}
> +	write_unlock_bh(&event_hash_lock);
> +
> +	return 0;
> +}
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Samir Bellabes Jan. 8, 2010, 7:21 a.m. UTC | #4
"Serge E. Hallyn" <serue@us.ibm.com> writes:

> Quoting Samir Bellabes (sam@synack.fr):
>> +int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
>> +{
>> +	unsigned int i = 0, n = 0;
>> +	int ret = -1;
>> +	unsigned hashs_to_skip = cb->args[0];
>> +	unsigned events_to_skip = cb->args[1];
>> +	struct list_head *l;
>> +	struct snet_event_entry *s;
>> +
>> +	read_lock_bh(&event_hash_lock);
>> +
>> +	if (!event_hash)
>> +		goto errout;
>> +
>> +	for (i = 0; i < event_hash_size; i++) {
>> +		if (i < hashs_to_skip)
>> +			continue;
>
> What is this?

code was duplicated from ctrl_dumpfamily() at net/netlink/genetlink.c
this can be optimized by:
        for (i = hashs_to_skip; i < event_hash_size; i++) {

I will made a patch for ctrl_dumpfamily() right now.

>> +		l = &event_hash[i];
>> +		n = 0;
>> +		list_for_each_entry(s, l, list) {
>> +			if (++n < events_to_skip)
>> +				continue;
>> +			ret = snet_nl_list_fill_info(skb,
>> +						     NETLINK_CB(cb->skb).pid,
>> +						     cb->nlh->nlmsg_seq,
>> +						     NLM_F_MULTI,
>> +						     s->se.protocol,
>> +						     s->se.syscall);
>> +			if (ret < 0)
>> +				goto errout;
>
> So if it returns 0, presumably meaning successfully handled, you
> want to go on processing any duplicates?

first, I found a bug in snet_nl_list_fill_info() which was returning 0
instead of -EMSGSIZE in case there was not enough space to put data.

I'm not sure to understand what may have duplicates, but if you are
talking about the events (struct snet_event_entry), that is not possible
as the insert function checks if the event is already in the hashtable
snet_evh before insertion.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Serge E. Hallyn Jan. 8, 2010, 3:34 p.m. UTC | #5
Quoting Samir Bellabes (sam@synack.fr):
> "Serge E. Hallyn" <serue@us.ibm.com> writes:
> 
> > Quoting Samir Bellabes (sam@synack.fr):
> >> +int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
> >> +{
> >> +	unsigned int i = 0, n = 0;
> >> +	int ret = -1;
> >> +	unsigned hashs_to_skip = cb->args[0];
> >> +	unsigned events_to_skip = cb->args[1];
> >> +	struct list_head *l;
> >> +	struct snet_event_entry *s;
> >> +
> >> +	read_lock_bh(&event_hash_lock);
> >> +
> >> +	if (!event_hash)
> >> +		goto errout;
> >> +
> >> +	for (i = 0; i < event_hash_size; i++) {
> >> +		if (i < hashs_to_skip)
> >> +			continue;
> >
> > What is this?
> 
> code was duplicated from ctrl_dumpfamily() at net/netlink/genetlink.c
> this can be optimized by:
>         for (i = hashs_to_skip; i < event_hash_size; i++) {

Sure, but my question was more general (more naive?) - what are the
hashs_to_skip?

sounds like i should be able to go read the genetlink code for an
answer, thanks.

> I will made a patch for ctrl_dumpfamily() right now.
> 
> >> +		l = &event_hash[i];
> >> +		n = 0;
> >> +		list_for_each_entry(s, l, list) {
> >> +			if (++n < events_to_skip)
> >> +				continue;
> >> +			ret = snet_nl_list_fill_info(skb,
> >> +						     NETLINK_CB(cb->skb).pid,
> >> +						     cb->nlh->nlmsg_seq,
> >> +						     NLM_F_MULTI,
> >> +						     s->se.protocol,
> >> +						     s->se.syscall);
> >> +			if (ret < 0)
> >> +				goto errout;
> >
> > So if it returns 0, presumably meaning successfully handled, you
> > want to go on processing any duplicates?
> 
> first, I found a bug in snet_nl_list_fill_info() which was returning 0
> instead of -EMSGSIZE in case there was not enough space to put data.
> 
> I'm not sure to understand what may have duplicates, but if you are
> talking about the events (struct snet_event_entry), that is not possible
> as the insert function checks if the event is already in the hashtable
> snet_evh before insertion.

Ok, but the way your loop is constructed, if snet_nl_list_fill_info()
returns 0 (success, presumably) you won't break.  Sounds like you want
to.

-serge
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Samir Bellabes Jan. 8, 2010, 5:44 p.m. UTC | #6
"Serge E. Hallyn" <serue@us.ibm.com> writes:

> Quoting Samir Bellabes (sam@synack.fr):
>> "Serge E. Hallyn" <serue@us.ibm.com> writes:
>> 
>> > Quoting Samir Bellabes (sam@synack.fr):
>> >> +int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
>> >> +{
>> >> +	unsigned int i = 0, n = 0;
>> >> +	int ret = -1;
>> >> +	unsigned hashs_to_skip = cb->args[0];
>> >> +	unsigned events_to_skip = cb->args[1];
>> >> +	struct list_head *l;
>> >> +	struct snet_event_entry *s;
>> >> +
>> >> +	read_lock_bh(&event_hash_lock);
>> >> +
>> >> +	if (!event_hash)
>> >> +		goto errout;
>> >> +
>> >> +	for (i = 0; i < event_hash_size; i++) {
>> >> +		if (i < hashs_to_skip)
>> >> +			continue;
>> >
>> > What is this?
>> 
>> code was duplicated from ctrl_dumpfamily() at net/netlink/genetlink.c
>> this can be optimized by:
>>         for (i = hashs_to_skip; i < event_hash_size; i++) {
>
> Sure, but my question was more general (more naive?) - what are the
> hashs_to_skip?
>
> sounds like i should be able to go read the genetlink code for an
> answer, thanks.

if it's not too late.
snet_nl_list(), in snet_netlink.c, is defined as a dumpit() genetlink
operation. A dumpit() operations is called when NLM_DUMP message flag is
set.

the genetlink dumpit() operation receives, as a argument, a
pre-allocated sk_buff buffer, so the purpose of snet_nl_list() is to
fill this buffer. snet_nl_list() is calling snet_event_fill_info() for
this job.

as long as snet_event_fill_info() is not returning 0, it means that it
fills data in the sk_buff, and the dumpit() operations is called again.

the behaviour of sending event list informations is to send a netlink
message by event - remember a event is [syscall, protocol]. so we are
iterate other the hashtable snet_evh:

  [0]               -> list_head: [sys0, proto1], [sys1, proto1]
  [1]               -> list_head: .. 
  [2]               -> list_head: ..
  ...
  [snet_evh_size-1] -> list_head: ..

in order to fill each the pre-allocated sk_buff.

so at each time the snet_event_fill_info() is called again, because of
returning values different from 0, we need to skip previous events which
were already filled in the buffer.
this is the purpose of 

	unsigned hashs_to_skip = cb->args[0];
	unsigned events_to_skip = cb->args[1];

to get the last event filled, and the purpose of :

        cb->args[0] = i;
        cb->args[1] = n;

to store the last event filled, at each call of dumpit()

at this point you may understand the purpose to skip the previous values
in the hashtables, and after the events values at each list.

>> I will made a patch for ctrl_dumpfamily() right now.
>> 
>> >> +		l = &event_hash[i];
>> >> +		n = 0;
>> >> +		list_for_each_entry(s, l, list) {
>> >> +			if (++n < events_to_skip)
>> >> +				continue;
>> >> +			ret = snet_nl_list_fill_info(skb,
>> >> +						     NETLINK_CB(cb->skb).pid,
>> >> +						     cb->nlh->nlmsg_seq,
>> >> +						     NLM_F_MULTI,
>> >> +						     s->se.protocol,
>> >> +						     s->se.syscall);
>> >> +			if (ret < 0)
>> >> +				goto errout;
>> >
>> > So if it returns 0, presumably meaning successfully handled, you
>> > want to go on processing any duplicates?
>> 
>> first, I found a bug in snet_nl_list_fill_info() which was returning 0
>> instead of -EMSGSIZE in case there was not enough space to put data.
>> 
>> I'm not sure to understand what may have duplicates, but if you are
>> talking about the events (struct snet_event_entry), that is not possible
>> as the insert function checks if the event is already in the hashtable
>> snet_evh before insertion.
>
> Ok, but the way your loop is constructed, if snet_nl_list_fill_info()
> returns 0 (success, presumably) you won't break.  Sounds like you want
> to.

No I don't, if it returns 0, we proceed the next event in the current
list.
once the list is empty, we proceed the next hashtable entry.
once the index running other the hashtable is egal to the hashtable size,
it's done.

sam

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Samir Bellabes Jan. 8, 2010, 5:51 p.m. UTC | #7
Samir Bellabes <sam@synack.fr> writes:


> No I don't, if it returns 0, we proceed the next event in the current
> list.

To be sure you shall understand: No I don't want to break.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Serge E. Hallyn Jan. 8, 2010, 6:10 p.m. UTC | #8
Quoting Samir Bellabes (sam@synack.fr):
> Samir Bellabes <sam@synack.fr> writes:
> 
> 
> > No I don't, if it returns 0, we proceed the next event in the current
> > list.
> 
> To be sure you shall understand: No I don't want to break.

Thanks :)

-serge
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/security/snet/include/snet_event.h b/security/snet/include/snet_event.h
new file mode 100644
index 0000000..2c71ca7
--- /dev/null
+++ b/security/snet/include/snet_event.h
@@ -0,0 +1,20 @@ 
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+#include <linux/skbuff.h>
+
+extern unsigned int event_hash_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+int snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..6ac5646
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,229 @@ 
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+
+#include "snet.h"
+#include "snet_event.h"
+#include "snet_netlink.h"
+
+static struct list_head *event_hash;
+static rwlock_t event_hash_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_event_entry {
+	struct list_head list;
+	struct snet_event se;
+};
+
+/* lookup for a event_hash - before using this function, lock event_hash_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+						   const u8 protocol)
+{
+	unsigned int h = 0;
+	struct list_head *l;
+	struct snet_event_entry *s;
+	struct snet_event t;
+
+	if (!event_hash)
+		return NULL;
+
+	/* building the element to look for */
+	t.syscall = syscall;
+	t.protocol = protocol;
+
+	/* computing its hash value */
+	h = jhash(&t, sizeof(struct snet_event), 0) % event_hash_size;
+	l = &event_hash[h];
+
+	list_for_each_entry(s, l, list) {
+		if ((s->se.protocol == protocol) &&
+		    (s->se.syscall == syscall)) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int i = 0, n = 0;
+	int ret = -1;
+	unsigned hashs_to_skip = cb->args[0];
+	unsigned events_to_skip = cb->args[1];
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	read_lock_bh(&event_hash_lock);
+
+	if (!event_hash)
+		goto errout;
+
+	for (i = 0; i < event_hash_size; i++) {
+		if (i < hashs_to_skip)
+			continue;
+		l = &event_hash[i];
+		n = 0;
+		list_for_each_entry(s, l, list) {
+			if (++n < events_to_skip)
+				continue;
+			ret = snet_nl_list_fill_info(skb,
+						     NETLINK_CB(cb->skb).pid,
+						     cb->nlh->nlmsg_seq,
+						     NLM_F_MULTI,
+						     s->se.protocol,
+						     s->se.syscall);
+			if (ret < 0)
+				goto errout;
+		}
+	}
+
+errout:
+	read_unlock_bh(&event_hash_lock);
+
+	cb->args[0] = i;
+	cb->args[1] = n;
+	return skb->len;
+}
+
+/* void snet_event_dumpall() */
+/* { */
+/*	unsigned int i = 0; */
+/*	struct list_head *l; */
+/*	struct snet_event_entry *s; */
+
+/*	snet_dbg("entering\n"); */
+/*	read_lock_bh(&event_hash_lock); */
+/*	for (i = 0; i < (event_hash_size - 1); i++) { */
+/*		l = &hash[i]; */
+/*		list_for_each_entry(s, l, list) { */
+/*			snet_dbg("[%d, %d, %d]\n", i, */
+/*				 s->se.protocol, s->se.syscall); */
+/*		} */
+/*	} */
+/*	read_unlock_bh(&event_hash_lock); */
+/*	snet_dbg("exiting\n"); */
+/*	return; */
+/* } */
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+	int ret = 0;
+
+	read_lock_bh(&event_hash_lock);
+	if (__snet_event_lookup(syscall, protocol) != NULL)
+		ret = 1;
+	read_unlock_bh(&event_hash_lock);
+	return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int h = 0;
+
+	data = kzalloc(sizeof(struct snet_event_entry), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	write_lock_bh(&event_hash_lock);
+	/* check if event is already registered */
+	if (!event_hash || __snet_event_lookup(syscall, protocol) != NULL) {
+		write_unlock_bh(&event_hash_lock);
+		kfree(data);
+		return -EINVAL;
+	}
+
+	data->se.syscall = syscall;
+	data->se.protocol = protocol;
+	INIT_LIST_HEAD(&(data->list));
+	h = jhash(&(data->se), sizeof(struct snet_event), 0) % event_hash_size;
+	list_add_tail(&data->list, &event_hash[h]);
+	write_unlock_bh(&event_hash_lock);
+
+	return 0;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+
+	write_lock_bh(&event_hash_lock);
+	data = __snet_event_lookup(syscall, protocol);
+	if (data == NULL) {
+		write_unlock_bh(&event_hash_lock);
+		return -EINVAL;
+	}
+
+	list_del(&data->list);
+	write_unlock_bh(&event_hash_lock);
+	kfree(data);
+	return 0;
+}
+
+/* flushing all events */
+void __snet_event_flush(void)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int i = 0;
+
+	for (i = 0; i < event_hash_size; i++) {
+		while (!list_empty(&event_hash[i])) {
+			data = list_entry(event_hash[i].next,
+					  struct snet_event_entry, list);
+			list_del(&data->list);
+			kfree(data);
+		}
+	}
+	return;
+}
+
+void snet_event_flush(void)
+{
+	write_lock_bh(&event_hash_lock);
+	if (event_hash)
+		__snet_event_flush();
+	write_unlock_bh(&event_hash_lock);
+	return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+	int err = 0, i = 0;
+
+	event_hash = kzalloc(sizeof(struct list_head) * event_hash_size,
+			     GFP_KERNEL);
+	if (!event_hash) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for event_hash\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < event_hash_size; i++)
+		INIT_LIST_HEAD(&(event_hash[i]));
+
+out:
+	return err;
+}
+
+/* exit function */
+int snet_event_exit(void)
+{
+	write_lock_bh(&event_hash_lock);
+	if (event_hash) {
+		__snet_event_flush();
+		kfree(event_hash);
+		event_hash = NULL;
+	}
+	write_unlock_bh(&event_hash_lock);
+
+	return 0;
+}