* [RFC] [PATCH] Network Events Connector
@ 2007-02-09 4:43 Samir Bellabes
2007-02-17 16:34 ` Evgeniy Polyakov
0 siblings, 1 reply; 8+ messages in thread
From: Samir Bellabes @ 2007-02-09 4:43 UTC (permalink / raw)
To: netdev
Hi,
Here is a new feature which can help firewalls to be more application
aware, so more useful for people.
Our previous discussion about cn_net and firewalls:
http://marc2.theaimsgroup.com/?t=115976957500002&r=1&w=2
Please, I would really like to have feedback and comments on that tool,
in order to improve it.
Thanks a lot,
Samir Bellabes
tree af484e2d54e2dc43312f171efe1426b236e97bd7
parent 1539b98b561754252dd520b98fa03a688a4f81b5
author Samir Bellabes <sam@synack.fr> 1170995340 +0100
committer Samir Bellabes <sam@synack.fr> 1170995340 +0100
[PATCH] Network Events Connector
This patch adds a connector which reports networking's events to
userspace.
It's sending events when a userspace application is using
syscalls so with LSM, we are catching this, at hooks : socket_listen,
socket_bind, socket_connect, socket_shutdown and sk_free_security.
Wanted events are dynamically manage from userspace, in a hashtable. A
daemon, cn_net_daemon, is listening for the connector in userspace.
daemon and doc are available here :
http://people.mandriva.com/~sbellabes/cn_net/
Signed-off-by: Samir Bellabes <sam@synack.fr>
------------------------------------------------------------------------------
drivers/connector/Kconfig | 8
drivers/connector/Makefile | 1
drivers/connector/cn_net.c | 619 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/cn_net.h | 122 ++++++++
include/linux/connector.h | 5
5 files changed, 753 insertions(+), 2 deletions(-)
------------------------------------------------------------------------------
diff --git a/drivers/connector/Kconfig b/drivers/connector/Kconfig
index e0bdc0d..0b04952 100644
--- a/drivers/connector/Kconfig
+++ b/drivers/connector/Kconfig
@@ -18,4 +18,12 @@ config PROC_EVENTS
Provide a connector that reports process events to userspace. Send
events such as fork, exec, id change (uid, gid, suid, etc), and exit.
+config NET_EVENTS
+ boolean "Report network events to userspace"
+ depends on CONNECTOR=y && SECURITY_NETWORK
+ default y
+ ---help---
+ Provide a connector that reports networking's events to userspace.
+ Send events such as DCCP/TCP listen/close and UDP bind/close.
+
endmenu
diff --git a/drivers/connector/Makefile b/drivers/connector/Makefile
index 1f255e4..436bb5d 100644
--- a/drivers/connector/Makefile
+++ b/drivers/connector/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CONNECTOR) += cn.o
obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
+obj-$(CONFIG_NET_EVENTS) += cn_net.o
cn-y += cn_queue.o connector.o
diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
new file mode 100644
index 0000000..1f681f6
--- /dev/null
+++ b/drivers/connector/cn_net.c
@@ -0,0 +1,619 @@
+/*
+ * drivers/connector/cn_net.c
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <net/inet_sock.h>
+#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <linux/in6.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+
+#include <linux/cn_net.h>
+
+static atomic_t net_event_num_listeners = ATOMIC_INIT(0);
+static struct cb_id cn_net_event_id = { CN_IDX_NET, CN_VAL_NET };
+static char cn_net_event_name[] = "cn_net_event";
+static int secondary = 0;
+
+static struct list_head *hash = NULL;
+static rwlock_t hash_lock = RW_LOCK_UNLOCKED;
+static int hash_size = 4;
+module_param(hash_size, uint, 0600);
+
+#if defined(CONFIG_NET_EVENTS)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "cn_net"
+#endif
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/**
+ * is_same_event()
+ * check if two events are the same or not
+ */
+static unsigned int is_same_event(struct event one, struct event two) {
+ return ((one.syscall_num == two.syscall_num) &&
+ (one.protocol == two.protocol));
+}
+
+/**
+ * check_event()
+ * look for a match in the hash table
+ * Returns 1 if data is in the hash table
+ */
+static unsigned int check_event(struct event_list *data) {
+ unsigned int err = 0, h = 0;
+ struct list_head *l;
+ struct event_list *evl;
+
+ h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
+
+ read_lock(&hash_lock);
+ l = &hash[h];
+ list_for_each_entry(evl, l, list) {
+ if (is_same_event(evl->ev, data->ev)) {
+ err = 1;
+ break;
+ }
+ }
+ read_unlock(&hash_lock);
+ return err;
+}
+
+/**
+ * check_wanted_data
+ * We don't send unwanted informations to userspace, according to the
+ * dynamic configuration in the hash table.
+ * @sock: sock which is changing its state
+ * Returns: 1 if data have to be send to userspace
+ */
+static int check_wanted_data(struct sock *sk, enum cn_net_socket syscall_num) {
+ struct event_list *data = NULL;
+ unsigned int err = 0;
+
+ if (!sk)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->ev.syscall_num = syscall_num;
+ data->ev.protocol = sk->sk_protocol;
+ INIT_LIST_HEAD(&(data->list));
+
+ /* check if the event is already registered */
+ err = check_event(data);
+ kfree(data);
+ return err;
+}
+
+/**
+ * dump_event() dumps the entire hash_table in log
+ */
+static void dump_event(struct list_head *hash) {
+ unsigned int i = 0;
+ struct list_head *l = NULL;
+ struct event_list *evl = NULL;
+
+ read_lock(&hash_lock);
+ for (i = 0; i < hash_size; i++) {
+ l = &hash[i];
+ printk(KERN_INFO "----\n");
+ printk(KERN_INFO "%d.\n", i);
+ list_for_each_entry(evl, l, list) {
+ printk(KERN_INFO " %d:%s\n",
+ evl->ev.protocol, syscall_name[evl->ev.syscall_num]);
+ }
+ }
+ read_unlock(&hash_lock);
+
+ return;
+}
+
+/**
+ * deletion of all elements in the hashtable
+ */
+static enum ack_err clean_all_event(struct list_head *hash) {
+ unsigned int i = 0;
+ struct event_list *evl = NULL;
+
+ write_lock(&hash_lock);
+ for (i = 0; i < hash_size; i++) {
+ while(!list_empty(&hash[i])) {
+ evl = list_entry(hash[i].next, struct event_list, list);
+ if (evl) {
+ list_del(&evl->list);
+ kfree(evl);
+ }
+ }
+ }
+ write_unlock(&hash_lock);
+ /* hash table is now empty */
+ return CN_NET_ACK_SUCCES;
+}
+
+/**
+ * add a entry to the hash table
+ * we are checking if we register a same event twice
+ */
+static enum ack_err add_event(struct list_head *hash, struct event ev) {
+
+ struct event_list *data = NULL;
+ unsigned int h = 0;
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
+ if (!data) {
+ err = CN_NET_ACK_ENOMEM;
+ return err;
+ }
+ data->ev.syscall_num = ev.syscall_num;
+ data->ev.protocol = ev.protocol;
+ INIT_LIST_HEAD(&(data->list));
+
+ /* check if the event is already registered */
+ if (check_event(data)) {
+ kfree(data);
+ err = CN_NET_ACK_EINCONFIG;
+ return err;
+ }
+ h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
+
+ write_lock(&hash_lock);
+ list_add_tail(&data->list, &hash[h]);
+ write_unlock(&hash_lock);
+
+ return err;
+}
+
+/**
+ * delete a entry from the hash table
+ * we are checking if we delete a unregistered event
+ */
+static enum ack_err del_event(struct list_head *hash, struct event ev) {
+
+ struct event_list *data;
+ unsigned int h = 0;
+ enum ack_err err = CN_NET_ACK_EINCONFIG;
+ struct list_head *l = NULL;
+ struct event_list *evl = NULL;
+
+ data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
+ if (!data) {
+ err = CN_NET_ACK_ENOMEM;
+ return err;
+ }
+ data->ev.syscall_num = ev.syscall_num;
+ data->ev.protocol = ev.protocol;
+ INIT_LIST_HEAD(&(data->list));
+
+ h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
+
+ write_lock(&hash_lock);
+ l = &hash[h];
+ list_for_each_entry(evl, l, list) {
+ if (is_same_event(evl->ev, data->ev)) {
+ list_del(&evl->list);
+ kfree(evl);
+ err = CN_NET_ACK_SUCCES;
+ break;
+ }
+ }
+ write_unlock(&hash_lock);
+ kfree(data);
+
+ return err;
+}
+
+/**
+ * do_register()
+ * check if userpace protocol version is same as kernel protocol version
+ */
+static enum ack_err do_register(__u32 version) {
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ DEBUGP(KERN_INFO "do_register: %d %d\n",
+ version, CN_NET_VERSION);
+
+ if (version == CN_NET_VERSION)
+ atomic_inc(&net_event_num_listeners);
+ else
+ err = CN_NET_ACK_EBADPROTO;
+ return err;
+}
+
+/**
+ * do_config()
+ * execute config asked by userspace
+ * return enum ack_err
+ */
+static enum ack_err do_config(struct msg_config *cfg) {
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ DEBUGP(KERN_INFO "do_config: %s %s %d\n",
+ config_name[cfg->config_cmd],
+ syscall_name[cfg->ev.syscall_num],
+ cfg->ev.protocol);
+
+ switch (cfg->config_cmd) {
+ case CN_NET_CONFIG_ADD:
+ err = add_event(hash, cfg->ev);
+ break;
+ case CN_NET_CONFIG_DEL:
+ err = del_event(hash, cfg->ev);
+ break;
+ case CN_NET_CONFIG_FLUSH:
+ err = clean_all_event(hash);
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ };
+
+ return err;
+}
+
+/**
+ * cn_net_ack
+ * Send an acknowledgement message to userspace
+ *
+ * Use 0 for SUCCESS, EFOO otherwise. (see enum ack_err)
+ */
+static void cn_net_ack(enum ack_err err, unsigned int rcvd_seq, unsigned int rcvd_ack, enum msg_type msg_type)
+{
+ struct cn_msg *m;
+ struct net_event *net_ev;
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
+
+ if (atomic_read(&net_event_num_listeners) < 1 && err != CN_NET_ACK_EBADPROTO)
+ return;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ net_ev->msg_type = msg_type;
+ ktime_get_real_ts(&net_ev->timestamp);
+ net_ev->net_event_data.ack = err;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = rcvd_seq;
+ m->ack = rcvd_ack + 1;
+ m->len = sizeof(struct net_event);
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+}
+
+/**
+ * cn_net_ctl
+ * connector callback
+ * @data: message receive from userspace via the connector
+ */
+void cn_net_ctl(void *data) {
+ struct cn_msg *m = data;
+ struct net_event *net_ev = NULL;
+ enum msg_type msg_type;
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ if (m->len != sizeof(struct net_event)) {
+ printk(KERN_WARNING "cn_net_ctl : message with bad size, discard\n");
+ return;
+ }
+
+ net_ev = (struct net_event *) m->data;
+
+ if (net_ev->msg_type != CN_NET_LISTEN &&
+ atomic_read(&net_event_num_listeners) < 1) {
+ printk(KERN_WARNING "cn_net_ctl : register first\n");
+ return;
+ }
+ msg_type = CN_NET_ACK; /* default response message type */
+
+ switch (net_ev->msg_type) {
+ case CN_NET_NONE: /* want to play ping pong ? */
+ msg_type = net_ev->msg_type;
+ break;
+ case CN_NET_ACK: /* userspace is ack'ing - check that */
+ /* FIXME: we don't send an ACK to an ACK */
+ /* we just check which message is ack */
+ goto out;
+ break;
+ case CN_NET_DATA: /* CN_NET_DATA can't be used by userspace */
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ case CN_NET_CONFIG: /* configuring kernel's behaviour */
+ err = do_config(&(net_ev->net_event_data.config));
+ break;
+ case CN_NET_LISTEN: /* userspace is registering */
+ err = do_register(net_ev->net_event_data.version);
+ break;
+ case CN_NET_IGNORE: /* userspace is unregistering */
+ atomic_dec(&net_event_num_listeners);
+ break;
+ case CN_NET_DUMP: /* dumping hash table -- debug purpose */
+ dump_event(hash);
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ };
+ cn_net_ack(err, m->seq, m->ack, msg_type);
+out:
+ return;
+}
+
+/**
+ * cn_net_send_event
+ * send data to userspace
+ * @sock: sock which sock->sk->sk_state change to the state identified by @event
+ */
+static void cn_net_send_event(struct sock *sk, struct sockaddr *address,
+ enum cn_net_socket syscall_num) {
+
+ struct net_event *net_ev;
+ struct cn_msg *m;
+ struct inet_sock *inet = inet_sk(sk);
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ struct task_struct *me = current;
+
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
+
+ if (atomic_read(&net_event_num_listeners) < 1)
+ goto out;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ switch (syscall_num) {
+ case CN_NET_SOCKET_LISTEN:
+ case CN_NET_SOCKET_SHUTDOWN:
+ case CN_NET_SK_FREE_SECURITY:
+ switch (sk->sk_family) {
+ case AF_INET:
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ break;
+ case AF_INET6:
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case CN_NET_SOCKET_BIND:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ net_ev->net_event_data.data.sport = ntohs(addr4->sin_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(addr6->sin6_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ case CN_NET_SOCKET_CONNECT:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr4->sin_port);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr6->sin6_port);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ default:
+ /* Bad syscall_num */
+ break;
+ };
+ net_ev->msg_type = CN_NET_DATA;
+ ktime_get_real_ts(&net_ev->timestamp);
+ net_ev->net_event_data.data.uid = me->uid;
+ get_task_comm(net_ev->net_event_data.data.taskname, me);
+ net_ev->net_event_data.data.ev.protocol = sk->sk_protocol;
+ net_ev->net_event_data.data.ev.syscall_num = syscall_num;
+ net_ev->net_event_data.data.family = sk->sk_family;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = get_random_int();
+ m->ack = 0;
+ m->len = sizeof(struct net_event);
+
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+out:
+ return;
+}
+
+static int cn_net_socket_listen(struct socket *sock, int backlog) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_listen\n");
+ if (check_wanted_data(sock->sk, CN_NET_SOCKET_LISTEN) > 0)
+ cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_LISTEN);
+
+ return 0;
+}
+
+static int cn_net_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_bind\n");
+ if (check_wanted_data(sock->sk, CN_NET_SOCKET_BIND) > 0)
+ cn_net_send_event(sock->sk, address, CN_NET_SOCKET_BIND);
+
+ return 0;
+}
+
+static int cn_net_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_connect\n");
+ if (check_wanted_data(sock->sk, CN_NET_SOCKET_CONNECT) > 0)
+ cn_net_send_event(sock->sk, address, CN_NET_SOCKET_CONNECT);
+
+ return 0;
+}
+
+static int cn_net_socket_shutdown(struct socket *sock, int how) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_shutdown\n");
+ if (check_wanted_data(sock->sk, CN_NET_SOCKET_SHUTDOWN) > 0)
+ cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_SHUTDOWN);
+
+ return 0;
+}
+
+static int cn_net_sk_free_security(struct sock *sk) {
+
+ DEBUGP(KERN_INFO "cn_net_sk_free_security\n");
+ if (check_wanted_data(sk, CN_NET_SK_FREE_SECURITY) > 0)
+ cn_net_send_event(sk, NULL, CN_NET_SK_FREE_SECURITY);
+
+ return 0;
+}
+
+static struct security_operations cn_net_security_ops = {
+ .socket_listen = cn_net_socket_listen,
+ .socket_bind = cn_net_socket_bind,
+ .socket_connect = cn_net_socket_connect,
+ .socket_shutdown = cn_net_socket_shutdown,
+ .sk_free_security = cn_net_sk_free_security,
+};
+
+static int __init init(void) {
+ int err, i;
+
+ err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
+
+ if (err) {
+ printk(KERN_WARNING "cn_net: Failure add connector callback\n");
+ goto out;
+ }
+
+ if (register_security(&cn_net_security_ops)) {
+ printk(KERN_INFO "cn_net: Failure registering with kernel\n");
+ if (mod_reg_security(MY_NAME, &cn_net_security_ops)) {
+ printk(KERN_WARNING
+ "cn_net: Failure registering with primary security"
+ " module\n");
+ err = -EINVAL;
+ goto out_security;
+ }
+ secondary = 1;
+ }
+
+
+ hash = kzalloc(sizeof(struct list_head) * hash_size, GFP_KERNEL);
+ if (!hash)
+ goto out_nomem_hash;
+
+ for (i = 0; i < hash_size; i++)
+ INIT_LIST_HEAD(&(hash[i]));
+
+ printk(KERN_INFO "cn_net: module loaded\n");
+
+ return 0;
+
+out_nomem_hash:
+ err = -ENOMEM;
+
+ if (secondary) {
+ if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with"
+ " primary security module\n");
+ } else {
+ if (unregister_security(&cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with "
+ "kernel\n");
+ }
+
+out_security:
+ cn_del_callback(&cn_net_event_id);
+out:
+ return err;
+}
+
+static void __exit fini(void) {
+ if (secondary) {
+ if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with"
+ " primary security module\n");
+ } else {
+ if (unregister_security(&cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with "
+ "kernel\n");
+ }
+
+ cn_del_callback(&cn_net_event_id);
+
+ /* clean memory */
+ if (hash) {
+ clean_all_event(hash);
+ kfree(hash);
+ }
+
+ printk(KERN_INFO "cn_net: network events module unloaded\n");
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_DESCRIPTION("Network events module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
diff --git a/include/linux/cn_net.h b/include/linux/cn_net.h
new file mode 100644
index 0000000..6604053
--- /dev/null
+++ b/include/linux/cn_net.h
@@ -0,0 +1,122 @@
+/*
+ * include/linux/cn_net.h
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef CN_NET_H
+#define CN_NET_H
+
+#include <linux/time.h>
+#include <linux/ipv6.h>
+
+#define CN_NET_VERSION 0x1
+
+#define CN_NET_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct net_event))
+
+char *syscall_name[] = { "LISTEN", "BIND", "CONNECT", "SHUTDOWN", "SK_FREE" };
+char *msg_type_name[] = { "CN_NET_NONE", "CN_NET_ACK", "CN_NET_DATA",
+ "CN_NET_CONFIG", "CN_NET_LISTEN", "CN_NET_IGNORE",
+ "CN_NET_DUMP" };
+char *config_name[] = {"CN_NET_CONFIG_ADD", "CN_NET_CONFIG_DEL", "CN_NET_CONFIG_FLUSH" };
+
+/**
+ * identify which syscall has been called.
+ */
+enum cn_net_socket {
+ CN_NET_SOCKET_LISTEN = 0,
+ CN_NET_SOCKET_BIND = 1,
+ CN_NET_SOCKET_CONNECT = 2,
+ CN_NET_SOCKET_SHUTDOWN = 3,
+ CN_NET_SK_FREE_SECURITY = 4,
+ CN_NET_SOCKET_MAX = 5,
+};
+
+/**
+ * Protocol message type
+ */
+enum msg_type {
+ CN_NET_NONE = 0,
+ CN_NET_ACK = 1,
+ CN_NET_DATA = 2,
+ CN_NET_CONFIG = 3,
+ CN_NET_LISTEN = 4,
+ CN_NET_IGNORE = 5,
+ CN_NET_DUMP = 6,
+};
+
+/**
+ * values for CN_NET_ACK messages
+ */
+enum ack_err {
+ CN_NET_ACK_SUCCES = 0,
+ CN_NET_ACK_ENOMEM = 1,
+ CN_NET_ACK_EINCONFIG = 2,
+ CN_NET_ACK_EINTYPE = 3,
+ CN_NET_ACK_EBADPROTO = 4,
+};
+
+/**
+ * values for CN_NET_CONFIG messages
+ */
+enum config_cmd {
+ CN_NET_CONFIG_ADD = 0,
+ CN_NET_CONFIG_DEL = 1,
+ CN_NET_CONFIG_FLUSH = 2,
+};
+
+struct event {
+ enum cn_net_socket syscall_num;
+ __u8 protocol;
+};
+
+struct event_list {
+ struct list_head list;
+ struct event ev;
+};
+
+struct msg_config {
+ enum config_cmd config_cmd;
+ struct event ev;
+};
+
+struct net_event {
+ enum msg_type msg_type;
+ struct timespec timestamp;
+ union {
+ /* protocol version number */
+ __u32 version;
+
+ /* generic ack for both userspace and kernel */
+ enum ack_err ack;
+
+ /* send data to userspace */
+ struct {
+ struct event ev;
+ uid_t uid;
+ unsigned char taskname[TASK_COMM_LEN];
+ unsigned int family;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } saddr;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } daddr;
+ unsigned int sport;
+ unsigned int dport;
+ } data;
+
+ /* send config to kernel */
+ struct msg_config config;
+ } net_event_data;
+};
+
+#endif /* CN_NET_H */
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 10eb56b..3042360 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -36,9 +36,10 @@ #define CN_IDX_CIFS 0x2
#define CN_VAL_CIFS 0x1
#define CN_W1_IDX 0x3 /* w1 communication */
#define CN_W1_VAL 0x1
+#define CN_IDX_NET 0x4
+#define CN_VAL_NET 0x1
-
-#define CN_NETLINK_USERS 4
+#define CN_NETLINK_USERS 5
/*
* Maximum connector's message size.
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-02-09 4:43 [RFC] [PATCH] Network Events Connector Samir Bellabes
@ 2007-02-17 16:34 ` Evgeniy Polyakov
2007-03-15 0:48 ` Samir Bellabes
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Evgeniy Polyakov @ 2007-02-17 16:34 UTC (permalink / raw)
To: Samir Bellabes; +Cc: netdev
On Fri, Feb 09, 2007 at 05:43:14AM +0100, Samir Bellabes (sam@synack.fr) wrote:
> Hi,
>
> Here is a new feature which can help firewalls to be more application
> aware, so more useful for people.
>
> Our previous discussion about cn_net and firewalls:
> http://marc2.theaimsgroup.com/?t=115976957500002&r=1&w=2
>
> Please, I would really like to have feedback and comments on that tool,
> in order to improve it.
Technical side does have problems.
1. your way to delete and check events is wrong - there is no need to
allocate new event and search for it in the hash table to remove - use
values as is.
2. initialization path has problems - hash is allocated after securty
hooks and connector moduler are registered.
3. why hash table and not rb tree?
4. are you 100% sure there are misalignments and 32/64 bit userspace
problems? you seems to copy some bits from proc connector, which
suffered from that errors in the past.
> Thanks a lot,
> Samir Bellabes
--
Evgeniy Polyakov
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-02-17 16:34 ` Evgeniy Polyakov
@ 2007-03-15 0:48 ` Samir Bellabes
2007-03-15 0:50 ` Samir Bellabes
2007-03-15 0:53 ` Samir Bellabes
2 siblings, 0 replies; 8+ messages in thread
From: Samir Bellabes @ 2007-03-15 0:48 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev
Evgeniy Polyakov <johnpol@2ka.mipt.ru> writes:
> On Fri, Feb 09, 2007 at 05:43:14AM +0100, Samir Bellabes (sam@synack.fr) wrote:
>> Hi,
>>
>> Here is a new feature which can help firewalls to be more application
>> aware, so more useful for people.
>>
>> Our previous discussion about cn_net and firewalls:
>> http://marc2.theaimsgroup.com/?t=115976957500002&r=1&w=2
>>
>> Please, I would really like to have feedback and comments on that tool,
>> in order to improve it.
>
> Technical side does have problems.
> 2. initialization path has problems - hash is allocated after securty
> hooks and connector moduler are registered.
I see. Here is a patch, to apply on top of previous.
commit 7c89bcb9cc07b02023c9a43b80ff1df7cb07b900
Author: Samir Bellabes <sam@synack.fr>
Date: Mon Feb 19 15:16:05 2007 +0100
[PATCH] cn_net: fix initialization path
'hash' is allocated after the registration of security hooks and
connector callback.
Noticed by Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Samir Bellabes <sam@synack.fr>
diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
index 1f681f6..c9eb53e 100644
--- a/drivers/connector/cn_net.c
+++ b/drivers/connector/cn_net.c
@@ -537,13 +537,22 @@ static struct security_operations cn_net
};
static int __init init(void) {
- int err, i;
+ int err = 0, i = 0;
- err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
+ hash = kzalloc(sizeof(struct list_head) * hash_size, GFP_KERNEL);
+ if (!hash) {
+ printk(KERN_WARNING "cn_net: Failure can't alloc memory for hash\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < hash_size; i++)
+ INIT_LIST_HEAD(&(hash[i]));
+ err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
if (err) {
printk(KERN_WARNING "cn_net: Failure add connector callback\n");
- goto out;
+ goto out_callback;
}
if (register_security(&cn_net_security_ops)) {
@@ -558,33 +567,15 @@ static int __init init(void) {
secondary = 1;
}
-
- hash = kzalloc(sizeof(struct list_head) * hash_size, GFP_KERNEL);
- if (!hash)
- goto out_nomem_hash;
-
- for (i = 0; i < hash_size; i++)
- INIT_LIST_HEAD(&(hash[i]));
-
- printk(KERN_INFO "cn_net: module loaded\n");
-
+ printk(KERN_INFO "cn_net: network events module loaded\n");
return 0;
-out_nomem_hash:
- err = -ENOMEM;
-
- if (secondary) {
- if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
- printk(KERN_INFO "cn_net: Failure unregistering with"
- " primary security module\n");
- } else {
- if (unregister_security(&cn_net_security_ops))
- printk(KERN_INFO "cn_net: Failure unregistering with "
- "kernel\n");
- }
-
out_security:
cn_del_callback(&cn_net_event_id);
+
+out_callback:
+ kfree(hash);
+
out:
return err;
}
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-02-17 16:34 ` Evgeniy Polyakov
2007-03-15 0:48 ` Samir Bellabes
@ 2007-03-15 0:50 ` Samir Bellabes
2007-03-15 0:53 ` Samir Bellabes
2 siblings, 0 replies; 8+ messages in thread
From: Samir Bellabes @ 2007-03-15 0:50 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev
Evgeniy Polyakov <johnpol@2ka.mipt.ru> writes:
> On Fri, Feb 09, 2007 at 05:43:14AM +0100, Samir Bellabes (sam@synack.fr) wrote:
>> Hi,
>>
>> Here is a new feature which can help firewalls to be more application
>> aware, so more useful for people.
>>
>> Our previous discussion about cn_net and firewalls:
>> http://marc2.theaimsgroup.com/?t=115976957500002&r=1&w=2
>>
>> Please, I would really like to have feedback and comments on that tool,
>> in order to improve it.
>
> Technical side does have problems.
> 1. your way to delete and check events is wrong - there is no need to
> allocate new event and search for it in the hash table to remove - use
> values as is.
> 3. why hash table and not rb tree?
You are right, I have rewrite this part of the system, using rbtree
Here is patch to apply on top of the previous 'initialisation path'
patch.
commit 5110880bc67e8329671ffa596cd899995c05ec82
Author: Samir Bellabes <sam@synack.fr>
Date: Thu Mar 15 01:42:48 2007 +0100
[PATCH] cn_net: store event's configuration in RB tree
Noticed by Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Samir Bellabes <sam@synack.fr>
diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
index c9eb53e..a15f67b 100644
--- a/drivers/connector/cn_net.c
+++ b/drivers/connector/cn_net.c
@@ -22,7 +22,7 @@ #include <net/inet_sock.h>
#include <linux/in.h>
#include <linux/ipv6.h>
#include <linux/in6.h>
-#include <linux/jhash.h>
+#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/random.h>
@@ -34,10 +34,8 @@ static struct cb_id cn_net_event_id = {
static char cn_net_event_name[] = "cn_net_event";
static int secondary = 0;
-static struct list_head *hash = NULL;
-static rwlock_t hash_lock = RW_LOCK_UNLOCKED;
-static int hash_size = 4;
-module_param(hash_size, uint, 0600);
+struct rb_root event_tree = RB_ROOT;
+static rwlock_t event_lock = RW_LOCK_UNLOCKED;
#if defined(CONFIG_NET_EVENTS)
#define MY_NAME THIS_MODULE->name
@@ -61,172 +59,198 @@ static unsigned int is_same_event(struct
}
/**
- * check_event()
- * look for a match in the hash table
- * Returns 1 if data is in the hash table
+ * lookup_event()
+ * look for a match in the rbtree
+ * returns address of the wanted element if it is in the rbtree, or NULL
*/
-static unsigned int check_event(struct event_list *data) {
- unsigned int err = 0, h = 0;
- struct list_head *l;
- struct event_list *evl;
-
- h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
-
- read_lock(&hash_lock);
- l = &hash[h];
- list_for_each_entry(evl, l, list) {
- if (is_same_event(evl->ev, data->ev)) {
- err = 1;
- break;
+static struct event_node *lookup_event(struct event ev) {
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ read_lock(&event_lock);
+
+ while (rb_node) {
+ struct event_node *cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev)) {
+ read_unlock(&event_lock);
+ return cur;
}
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
}
- read_unlock(&hash_lock);
- return err;
+
+ read_unlock(&event_lock);
+ return NULL;
}
-/**
- * check_wanted_data
- * We don't send unwanted informations to userspace, according to the
- * dynamic configuration in the hash table.
- * @sock: sock which is changing its state
- * Returns: 1 if data have to be send to userspace
+/**
+ * check_wanted_data()
+ * we don't send unwanted informations to userspace, according to the
+ * dynamic configuration in the rbtree
*/
-static int check_wanted_data(struct sock *sk, enum cn_net_socket syscall_num) {
- struct event_list *data = NULL;
- unsigned int err = 0;
-
- if (!sk)
- return -EINVAL;
+static int check_wanted_event(struct sock *sk, enum cn_net_socket syscall_num) {
+ int err = -EINVAL;
+ struct event ev;
- data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->ev.syscall_num = syscall_num;
- data->ev.protocol = sk->sk_protocol;
- INIT_LIST_HEAD(&(data->list));
+ if (!sk)
+ return err;
+ ev.syscall_num = syscall_num;
+ ev.protocol = sk->sk_protocol;
+
/* check if the event is already registered */
- err = check_event(data);
- kfree(data);
+ if (lookup_event(ev))
+ err = 0;
+
return err;
}
/**
- * dump_event() dumps the entire hash_table in log
+ * dump_event()
+ * dump the entire rbtree in log
*/
-static void dump_event(struct list_head *hash) {
- unsigned int i = 0;
- struct list_head *l = NULL;
- struct event_list *evl = NULL;
-
- read_lock(&hash_lock);
- for (i = 0; i < hash_size; i++) {
- l = &hash[i];
- printk(KERN_INFO "----\n");
- printk(KERN_INFO "%d.\n", i);
- list_for_each_entry(evl, l, list) {
- printk(KERN_INFO " %d:%s\n",
- evl->ev.protocol, syscall_name[evl->ev.syscall_num]);
- }
- }
- read_unlock(&hash_lock);
-
- return;
+static void dump_event(void) {
+ struct rb_node *p = NULL ;
+ struct event_node *tmp = NULL;
+
+ read_lock(&event_lock);
+ p = rb_first(&event_tree);
+ while (p) {
+ tmp = rb_entry(p, struct event_node, ev_node);
+ printk(KERN_INFO "%d:%s\n",
+ tmp->ev.protocol, syscall_name[tmp->ev.syscall_num]);
+ p = rb_next(p);
+ };
+ read_unlock(&event_lock);
}
/**
- * deletion of all elements in the hashtable
+ * remove_all_event()
+ * delete all events registered in the rbtree
*/
-static enum ack_err clean_all_event(struct list_head *hash) {
- unsigned int i = 0;
- struct event_list *evl = NULL;
-
- write_lock(&hash_lock);
- for (i = 0; i < hash_size; i++) {
- while(!list_empty(&hash[i])) {
- evl = list_entry(hash[i].next, struct event_list, list);
- if (evl) {
- list_del(&evl->list);
- kfree(evl);
- }
- }
+static enum ack_err remove_all_event(void) {
+ struct rb_node *p = NULL;
+ struct event_node *cur = NULL;
+
+ write_lock(&event_lock);
+ while ((p = rb_first(&event_tree)) != NULL) {
+ cur = rb_entry(p, struct event_node, ev_node);
+ rb_erase(p, &event_tree);
+ kfree(cur);
}
- write_unlock(&hash_lock);
- /* hash table is now empty */
+ write_unlock(&event_lock);
+
+ /* rbtree is now empty */
return CN_NET_ACK_SUCCES;
}
-/**
- * add a entry to the hash table
- * we are checking if we register a same event twice
+/**
+ * alloc_event()
+ * alloc memory for a event_node, and return pointer to it
*/
-static enum ack_err add_event(struct list_head *hash, struct event ev) {
+static struct event_node *alloc_event(void) {
+ struct event_node *evn = NULL;
+ evn = kzalloc(sizeof(struct event_node), GFP_KERNEL);
+ return evn;
+}
- struct event_list *data = NULL;
- unsigned int h = 0;
- enum ack_err err = CN_NET_ACK_SUCCES;
-
- data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
- if (!data) {
- err = CN_NET_ACK_ENOMEM;
- return err;
+/**
+ * insert_event()
+ * insert a event in the rbtree
+ * we are checking if we are trying to register a same event twice
+ */
+static enum ack_err insert_event(struct event ev) {
+ struct rb_node **rb_link, *rb_parent;
+ struct event_node *new_evn;
+ enum ack_err ret = CN_NET_ACK_SUCCES;
+
+ rb_link = &event_tree.rb_node;
+ rb_parent = NULL;
+
+ write_lock(&event_lock);
+
+ while(*rb_link) {
+ struct event_node *tmp;
+
+ rb_parent = *rb_link;
+ tmp = rb_entry(rb_parent, struct event_node, ev_node);
+
+ if (ev.syscall_num < tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.syscall_num > tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_right;
+ else if (ev.protocol < tmp->ev.protocol)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.protocol > tmp->ev.protocol)
+ rb_link = &rb_parent->rb_right;
+ else {
+ /* event is already registered */
+ ret = CN_NET_ACK_EINCONFIG;
+ goto out;
+ }
}
- data->ev.syscall_num = ev.syscall_num;
- data->ev.protocol = ev.protocol;
- INIT_LIST_HEAD(&(data->list));
- /* check if the event is already registered */
- if (check_event(data)) {
- kfree(data);
- err = CN_NET_ACK_EINCONFIG;
- return err;
+ /* no match: event is added to the tree */
+ if ((new_evn = alloc_event()) != NULL) {
+ new_evn->ev.syscall_num = ev.syscall_num;
+ new_evn->ev.protocol = ev.protocol;
+ } else {
+ /* can't allocate memory, exiting */
+ ret = CN_NET_ACK_ENOMEM;
+ goto out;
}
- h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
- write_lock(&hash_lock);
- list_add_tail(&data->list, &hash[h]);
- write_unlock(&hash_lock);
+ rb_link_node(&new_evn->ev_node, rb_parent, rb_link);
+ rb_insert_color(&new_evn->ev_node, &event_tree);
- return err;
+out:
+ write_unlock(&event_lock);
+ return ret;
}
/**
- * delete a entry from the hash table
- * we are checking if we delete a unregistered event
+ * remove_event()
+ * delete a entry from the rbtree
+ * we are checking if we are trying to delete a unregistered event
*/
-static enum ack_err del_event(struct list_head *hash, struct event ev) {
-
- struct event_list *data;
- unsigned int h = 0;
- enum ack_err err = CN_NET_ACK_EINCONFIG;
- struct list_head *l = NULL;
- struct event_list *evl = NULL;
-
- data = kzalloc(sizeof(struct event_list), GFP_KERNEL);
- if (!data) {
- err = CN_NET_ACK_ENOMEM;
- return err;
- }
- data->ev.syscall_num = ev.syscall_num;
- data->ev.protocol = ev.protocol;
- INIT_LIST_HEAD(&(data->list));
-
- h = jhash(&(data->ev), sizeof(struct event), 0) % hash_size;
-
- write_lock(&hash_lock);
- l = &hash[h];
- list_for_each_entry(evl, l, list) {
- if (is_same_event(evl->ev, data->ev)) {
- list_del(&evl->list);
- kfree(evl);
- err = CN_NET_ACK_SUCCES;
+static enum ack_err remove_event(struct event ev) {
+ struct event_node *cur = NULL;
+ enum ack_err ret = CN_NET_ACK_EINCONFIG;
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ write_lock(&event_lock);
+
+ while (rb_node) {
+ cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev))
break;
- }
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
}
- write_unlock(&hash_lock);
- kfree(data);
- return err;
+ if (rb_node) {
+ rb_erase(&cur->ev_node, &event_tree);
+ kfree(cur);
+ ret = CN_NET_ACK_SUCCES;
+ }
+ write_unlock(&event_lock);
+
+ return ret;
}
/**
@@ -261,13 +285,13 @@ static enum ack_err do_config(struct msg
switch (cfg->config_cmd) {
case CN_NET_CONFIG_ADD:
- err = add_event(hash, cfg->ev);
+ err = insert_event(cfg->ev);
break;
case CN_NET_CONFIG_DEL:
- err = del_event(hash, cfg->ev);
+ err= remove_event(cfg->ev);
break;
case CN_NET_CONFIG_FLUSH:
- err = clean_all_event(hash);
+ err = remove_all_event();
break;
default:
err = CN_NET_ACK_EINTYPE;
@@ -353,8 +377,8 @@ void cn_net_ctl(void *data) {
case CN_NET_IGNORE: /* userspace is unregistering */
atomic_dec(&net_event_num_listeners);
break;
- case CN_NET_DUMP: /* dumping hash table -- debug purpose */
- dump_event(hash);
+ case CN_NET_DUMP: /* dumping the rbtree -- debug purpose */
+ dump_event();
break;
default:
err = CN_NET_ACK_EINTYPE;
@@ -485,7 +509,7 @@ out:
static int cn_net_socket_listen(struct socket *sock, int backlog) {
DEBUGP(KERN_INFO "cn_net_socket_listen\n");
- if (check_wanted_data(sock->sk, CN_NET_SOCKET_LISTEN) > 0)
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_LISTEN) == NULL)
cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_LISTEN);
return 0;
@@ -495,7 +519,7 @@ static int cn_net_socket_bind(struct soc
struct sockaddr *address, int addrlen) {
DEBUGP(KERN_INFO "cn_net_socket_bind\n");
- if (check_wanted_data(sock->sk, CN_NET_SOCKET_BIND) > 0)
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_BIND) == NULL)
cn_net_send_event(sock->sk, address, CN_NET_SOCKET_BIND);
return 0;
@@ -504,7 +528,7 @@ static int cn_net_socket_bind(struct soc
static int cn_net_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) {
DEBUGP(KERN_INFO "cn_net_socket_connect\n");
- if (check_wanted_data(sock->sk, CN_NET_SOCKET_CONNECT) > 0)
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_CONNECT) == NULL)
cn_net_send_event(sock->sk, address, CN_NET_SOCKET_CONNECT);
return 0;
@@ -513,7 +537,7 @@ static int cn_net_socket_connect(struct
static int cn_net_socket_shutdown(struct socket *sock, int how) {
DEBUGP(KERN_INFO "cn_net_socket_shutdown\n");
- if (check_wanted_data(sock->sk, CN_NET_SOCKET_SHUTDOWN) > 0)
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_SHUTDOWN) == NULL)
cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_SHUTDOWN);
return 0;
@@ -522,7 +546,7 @@ static int cn_net_socket_shutdown(struct
static int cn_net_sk_free_security(struct sock *sk) {
DEBUGP(KERN_INFO "cn_net_sk_free_security\n");
- if (check_wanted_data(sk, CN_NET_SK_FREE_SECURITY) > 0)
+ if (check_wanted_event(sk, CN_NET_SK_FREE_SECURITY) == NULL)
cn_net_send_event(sk, NULL, CN_NET_SK_FREE_SECURITY);
return 0;
@@ -537,17 +561,7 @@ static struct security_operations cn_net
};
static int __init init(void) {
- int err = 0, i = 0;
-
- hash = kzalloc(sizeof(struct list_head) * hash_size, GFP_KERNEL);
- if (!hash) {
- printk(KERN_WARNING "cn_net: Failure can't alloc memory for hash\n");
- err = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < hash_size; i++)
- INIT_LIST_HEAD(&(hash[i]));
+ int err = 0;
err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
if (err) {
@@ -574,9 +588,6 @@ out_security:
cn_del_callback(&cn_net_event_id);
out_callback:
- kfree(hash);
-
-out:
return err;
}
@@ -594,10 +605,7 @@ static void __exit fini(void) {
cn_del_callback(&cn_net_event_id);
/* clean memory */
- if (hash) {
- clean_all_event(hash);
- kfree(hash);
- }
+ remove_all_event();
printk(KERN_INFO "cn_net: network events module unloaded\n");
}
diff --git a/include/linux/cn_net.h b/include/linux/cn_net.h
index 6604053..0d86715 100644
--- a/include/linux/cn_net.h
+++ b/include/linux/cn_net.h
@@ -76,8 +76,8 @@ struct event {
__u8 protocol;
};
-struct event_list {
- struct list_head list;
+struct event_node {
+ struct rb_node ev_node;
struct event ev;
};
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-02-17 16:34 ` Evgeniy Polyakov
2007-03-15 0:48 ` Samir Bellabes
2007-03-15 0:50 ` Samir Bellabes
@ 2007-03-15 0:53 ` Samir Bellabes
2 siblings, 0 replies; 8+ messages in thread
From: Samir Bellabes @ 2007-03-15 0:53 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev
Evgeniy Polyakov <johnpol@2ka.mipt.ru> writes:
> On Fri, Feb 09, 2007 at 05:43:14AM +0100, Samir Bellabes (sam@synack.fr) wrote:
>> Hi,
>>
>> Here is a new feature which can help firewalls to be more application
>> aware, so more useful for people.
>>
>> Our previous discussion about cn_net and firewalls:
>> http://marc2.theaimsgroup.com/?t=115976957500002&r=1&w=2
>>
>> Please, I would really like to have feedback and comments on that tool,
>> in order to improve it.
>
> Technical side does have problems.
> 4. are you 100% sure there are misalignments and 32/64 bit userspace
> problems? you seems to copy some bits from proc connector, which
> suffered from that errors in the past.
No, i'm currently not sure of that.
I think you are refering to this :
http://git.kernel.org/?p=linux/kernel/git/acme/net-2.6.22.git;a=commitdiff;h=af3e095a1fb42bac32355d5d59ce93f8b4e59a3e
I'm going to care of this for cn_net
Evgeniy, thanks a lot for your review.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC] [PATCH] Network Events Connector
@ 2007-03-15 1:05 Samir Bellabes
2007-03-15 1:17 ` David Miller
2007-03-15 18:00 ` Evgeniy Polyakov
0 siblings, 2 replies; 8+ messages in thread
From: Samir Bellabes @ 2007-03-15 1:05 UTC (permalink / raw)
To: netdev
Hi,
here a updated patch for the network events connector.
review are welcome :)
Thanks.
[PATCH] Network Events Connector
This patch adds a connector which reports networking's events to
userspace.
It's sending events when a userspace application is using
syscalls so with LSM, we are catching this, at hooks : socket_listen,
socket_bind, socket_connect, socket_shutdown and sk_free_security.
Wanted events are dynamically manage from userspace, in a rbtree. A
daemon, cn_net_daemon, is listening for the connector in userspace.
daemon and doc are available here :
http://people.mandriva.com/~sbellabes/cn_net/
Signed-off-by: Samir Bellabes <sam@synack.fr>
------------------------------------------------------------------------------
drivers/connector/Kconfig | 8
drivers/connector/Makefile | 1
drivers/connector/cn_net.c | 618 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/cn_net.h | 122 ++++++++
include/linux/connector.h | 5
5 files changed, 752 insertions(+), 2 deletions(-)
------------------------------------------------------------------------------
diff --git a/drivers/connector/Kconfig b/drivers/connector/Kconfig
index e0bdc0d..0b04952 100644
--- a/drivers/connector/Kconfig
+++ b/drivers/connector/Kconfig
@@ -18,4 +18,12 @@ config PROC_EVENTS
Provide a connector that reports process events to userspace. Send
events such as fork, exec, id change (uid, gid, suid, etc), and exit.
+config NET_EVENTS
+ boolean "Report network events to userspace"
+ depends on CONNECTOR=y && SECURITY_NETWORK
+ default y
+ ---help---
+ Provide a connector that reports networking's events to userspace.
+ Send events such as DCCP/TCP listen/close and UDP bind/close.
+
endmenu
diff --git a/drivers/connector/Makefile b/drivers/connector/Makefile
index 1f255e4..436bb5d 100644
--- a/drivers/connector/Makefile
+++ b/drivers/connector/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CONNECTOR) += cn.o
obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
+obj-$(CONFIG_NET_EVENTS) += cn_net.o
cn-y += cn_queue.o connector.o
diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
new file mode 100644
index 0000000..a15f67b
--- /dev/null
+++ b/drivers/connector/cn_net.c
@@ -0,0 +1,618 @@
+/*
+ * drivers/connector/cn_net.c
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <net/inet_sock.h>
+#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <linux/in6.h>
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+
+#include <linux/cn_net.h>
+
+static atomic_t net_event_num_listeners = ATOMIC_INIT(0);
+static struct cb_id cn_net_event_id = { CN_IDX_NET, CN_VAL_NET };
+static char cn_net_event_name[] = "cn_net_event";
+static int secondary = 0;
+
+struct rb_root event_tree = RB_ROOT;
+static rwlock_t event_lock = RW_LOCK_UNLOCKED;
+
+#if defined(CONFIG_NET_EVENTS)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "cn_net"
+#endif
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/**
+ * is_same_event()
+ * check if two events are the same or not
+ */
+static unsigned int is_same_event(struct event one, struct event two) {
+ return ((one.syscall_num == two.syscall_num) &&
+ (one.protocol == two.protocol));
+}
+
+/**
+ * lookup_event()
+ * look for a match in the rbtree
+ * returns address of the wanted element if it is in the rbtree, or NULL
+ */
+static struct event_node *lookup_event(struct event ev) {
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ read_lock(&event_lock);
+
+ while (rb_node) {
+ struct event_node *cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev)) {
+ read_unlock(&event_lock);
+ return cur;
+ }
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
+ }
+
+ read_unlock(&event_lock);
+ return NULL;
+}
+
+/**
+ * check_wanted_data()
+ * we don't send unwanted informations to userspace, according to the
+ * dynamic configuration in the rbtree
+ */
+static int check_wanted_event(struct sock *sk, enum cn_net_socket syscall_num) {
+ int err = -EINVAL;
+ struct event ev;
+
+ if (!sk)
+ return err;
+
+ ev.syscall_num = syscall_num;
+ ev.protocol = sk->sk_protocol;
+
+ /* check if the event is already registered */
+ if (lookup_event(ev))
+ err = 0;
+
+ return err;
+}
+
+/**
+ * dump_event()
+ * dump the entire rbtree in log
+ */
+static void dump_event(void) {
+ struct rb_node *p = NULL ;
+ struct event_node *tmp = NULL;
+
+ read_lock(&event_lock);
+ p = rb_first(&event_tree);
+ while (p) {
+ tmp = rb_entry(p, struct event_node, ev_node);
+ printk(KERN_INFO "%d:%s\n",
+ tmp->ev.protocol, syscall_name[tmp->ev.syscall_num]);
+ p = rb_next(p);
+ };
+ read_unlock(&event_lock);
+}
+
+/**
+ * remove_all_event()
+ * delete all events registered in the rbtree
+ */
+static enum ack_err remove_all_event(void) {
+ struct rb_node *p = NULL;
+ struct event_node *cur = NULL;
+
+ write_lock(&event_lock);
+ while ((p = rb_first(&event_tree)) != NULL) {
+ cur = rb_entry(p, struct event_node, ev_node);
+ rb_erase(p, &event_tree);
+ kfree(cur);
+ }
+ write_unlock(&event_lock);
+
+ /* rbtree is now empty */
+ return CN_NET_ACK_SUCCES;
+}
+
+/**
+ * alloc_event()
+ * alloc memory for a event_node, and return pointer to it
+ */
+static struct event_node *alloc_event(void) {
+ struct event_node *evn = NULL;
+ evn = kzalloc(sizeof(struct event_node), GFP_KERNEL);
+ return evn;
+}
+
+/**
+ * insert_event()
+ * insert a event in the rbtree
+ * we are checking if we are trying to register a same event twice
+ */
+static enum ack_err insert_event(struct event ev) {
+ struct rb_node **rb_link, *rb_parent;
+ struct event_node *new_evn;
+ enum ack_err ret = CN_NET_ACK_SUCCES;
+
+ rb_link = &event_tree.rb_node;
+ rb_parent = NULL;
+
+ write_lock(&event_lock);
+
+ while(*rb_link) {
+ struct event_node *tmp;
+
+ rb_parent = *rb_link;
+ tmp = rb_entry(rb_parent, struct event_node, ev_node);
+
+ if (ev.syscall_num < tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.syscall_num > tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_right;
+ else if (ev.protocol < tmp->ev.protocol)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.protocol > tmp->ev.protocol)
+ rb_link = &rb_parent->rb_right;
+ else {
+ /* event is already registered */
+ ret = CN_NET_ACK_EINCONFIG;
+ goto out;
+ }
+ }
+
+ /* no match: event is added to the tree */
+ if ((new_evn = alloc_event()) != NULL) {
+ new_evn->ev.syscall_num = ev.syscall_num;
+ new_evn->ev.protocol = ev.protocol;
+ } else {
+ /* can't allocate memory, exiting */
+ ret = CN_NET_ACK_ENOMEM;
+ goto out;
+ }
+
+ rb_link_node(&new_evn->ev_node, rb_parent, rb_link);
+ rb_insert_color(&new_evn->ev_node, &event_tree);
+
+out:
+ write_unlock(&event_lock);
+ return ret;
+}
+
+/**
+ * remove_event()
+ * delete a entry from the rbtree
+ * we are checking if we are trying to delete a unregistered event
+ */
+static enum ack_err remove_event(struct event ev) {
+ struct event_node *cur = NULL;
+ enum ack_err ret = CN_NET_ACK_EINCONFIG;
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ write_lock(&event_lock);
+
+ while (rb_node) {
+ cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev))
+ break;
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
+ }
+
+ if (rb_node) {
+ rb_erase(&cur->ev_node, &event_tree);
+ kfree(cur);
+ ret = CN_NET_ACK_SUCCES;
+ }
+ write_unlock(&event_lock);
+
+ return ret;
+}
+
+/**
+ * do_register()
+ * check if userpace protocol version is same as kernel protocol version
+ */
+static enum ack_err do_register(__u32 version) {
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ DEBUGP(KERN_INFO "do_register: %d %d\n",
+ version, CN_NET_VERSION);
+
+ if (version == CN_NET_VERSION)
+ atomic_inc(&net_event_num_listeners);
+ else
+ err = CN_NET_ACK_EBADPROTO;
+ return err;
+}
+
+/**
+ * do_config()
+ * execute config asked by userspace
+ * return enum ack_err
+ */
+static enum ack_err do_config(struct msg_config *cfg) {
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ DEBUGP(KERN_INFO "do_config: %s %s %d\n",
+ config_name[cfg->config_cmd],
+ syscall_name[cfg->ev.syscall_num],
+ cfg->ev.protocol);
+
+ switch (cfg->config_cmd) {
+ case CN_NET_CONFIG_ADD:
+ err = insert_event(cfg->ev);
+ break;
+ case CN_NET_CONFIG_DEL:
+ err= remove_event(cfg->ev);
+ break;
+ case CN_NET_CONFIG_FLUSH:
+ err = remove_all_event();
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ };
+
+ return err;
+}
+
+/**
+ * cn_net_ack
+ * Send an acknowledgement message to userspace
+ *
+ * Use 0 for SUCCESS, EFOO otherwise. (see enum ack_err)
+ */
+static void cn_net_ack(enum ack_err err, unsigned int rcvd_seq, unsigned int rcvd_ack, enum msg_type msg_type)
+{
+ struct cn_msg *m;
+ struct net_event *net_ev;
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
+
+ if (atomic_read(&net_event_num_listeners) < 1 && err != CN_NET_ACK_EBADPROTO)
+ return;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ net_ev->msg_type = msg_type;
+ ktime_get_real_ts(&net_ev->timestamp);
+ net_ev->net_event_data.ack = err;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = rcvd_seq;
+ m->ack = rcvd_ack + 1;
+ m->len = sizeof(struct net_event);
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+}
+
+/**
+ * cn_net_ctl
+ * connector callback
+ * @data: message receive from userspace via the connector
+ */
+void cn_net_ctl(void *data) {
+ struct cn_msg *m = data;
+ struct net_event *net_ev = NULL;
+ enum msg_type msg_type;
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ if (m->len != sizeof(struct net_event)) {
+ printk(KERN_WARNING "cn_net_ctl : message with bad size, discard\n");
+ return;
+ }
+
+ net_ev = (struct net_event *) m->data;
+
+ if (net_ev->msg_type != CN_NET_LISTEN &&
+ atomic_read(&net_event_num_listeners) < 1) {
+ printk(KERN_WARNING "cn_net_ctl : register first\n");
+ return;
+ }
+ msg_type = CN_NET_ACK; /* default response message type */
+
+ switch (net_ev->msg_type) {
+ case CN_NET_NONE: /* want to play ping pong ? */
+ msg_type = net_ev->msg_type;
+ break;
+ case CN_NET_ACK: /* userspace is ack'ing - check that */
+ /* FIXME: we don't send an ACK to an ACK */
+ /* we just check which message is ack */
+ goto out;
+ break;
+ case CN_NET_DATA: /* CN_NET_DATA can't be used by userspace */
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ case CN_NET_CONFIG: /* configuring kernel's behaviour */
+ err = do_config(&(net_ev->net_event_data.config));
+ break;
+ case CN_NET_LISTEN: /* userspace is registering */
+ err = do_register(net_ev->net_event_data.version);
+ break;
+ case CN_NET_IGNORE: /* userspace is unregistering */
+ atomic_dec(&net_event_num_listeners);
+ break;
+ case CN_NET_DUMP: /* dumping the rbtree -- debug purpose */
+ dump_event();
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ };
+ cn_net_ack(err, m->seq, m->ack, msg_type);
+out:
+ return;
+}
+
+/**
+ * cn_net_send_event
+ * send data to userspace
+ * @sock: sock which sock->sk->sk_state change to the state identified by @event
+ */
+static void cn_net_send_event(struct sock *sk, struct sockaddr *address,
+ enum cn_net_socket syscall_num) {
+
+ struct net_event *net_ev;
+ struct cn_msg *m;
+ struct inet_sock *inet = inet_sk(sk);
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ struct task_struct *me = current;
+
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
+
+ if (atomic_read(&net_event_num_listeners) < 1)
+ goto out;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ switch (syscall_num) {
+ case CN_NET_SOCKET_LISTEN:
+ case CN_NET_SOCKET_SHUTDOWN:
+ case CN_NET_SK_FREE_SECURITY:
+ switch (sk->sk_family) {
+ case AF_INET:
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ break;
+ case AF_INET6:
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case CN_NET_SOCKET_BIND:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ net_ev->net_event_data.data.sport = ntohs(addr4->sin_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(addr6->sin6_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ case CN_NET_SOCKET_CONNECT:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr4->sin_port);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr6->sin6_port);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ default:
+ /* Bad syscall_num */
+ break;
+ };
+ net_ev->msg_type = CN_NET_DATA;
+ ktime_get_real_ts(&net_ev->timestamp);
+ net_ev->net_event_data.data.uid = me->uid;
+ get_task_comm(net_ev->net_event_data.data.taskname, me);
+ net_ev->net_event_data.data.ev.protocol = sk->sk_protocol;
+ net_ev->net_event_data.data.ev.syscall_num = syscall_num;
+ net_ev->net_event_data.data.family = sk->sk_family;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = get_random_int();
+ m->ack = 0;
+ m->len = sizeof(struct net_event);
+
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+out:
+ return;
+}
+
+static int cn_net_socket_listen(struct socket *sock, int backlog) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_listen\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_LISTEN) == NULL)
+ cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_LISTEN);
+
+ return 0;
+}
+
+static int cn_net_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_bind\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_BIND) == NULL)
+ cn_net_send_event(sock->sk, address, CN_NET_SOCKET_BIND);
+
+ return 0;
+}
+
+static int cn_net_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_connect\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_CONNECT) == NULL)
+ cn_net_send_event(sock->sk, address, CN_NET_SOCKET_CONNECT);
+
+ return 0;
+}
+
+static int cn_net_socket_shutdown(struct socket *sock, int how) {
+
+ DEBUGP(KERN_INFO "cn_net_socket_shutdown\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_SHUTDOWN) == NULL)
+ cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_SHUTDOWN);
+
+ return 0;
+}
+
+static int cn_net_sk_free_security(struct sock *sk) {
+
+ DEBUGP(KERN_INFO "cn_net_sk_free_security\n");
+ if (check_wanted_event(sk, CN_NET_SK_FREE_SECURITY) == NULL)
+ cn_net_send_event(sk, NULL, CN_NET_SK_FREE_SECURITY);
+
+ return 0;
+}
+
+static struct security_operations cn_net_security_ops = {
+ .socket_listen = cn_net_socket_listen,
+ .socket_bind = cn_net_socket_bind,
+ .socket_connect = cn_net_socket_connect,
+ .socket_shutdown = cn_net_socket_shutdown,
+ .sk_free_security = cn_net_sk_free_security,
+};
+
+static int __init init(void) {
+ int err = 0;
+
+ err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
+ if (err) {
+ printk(KERN_WARNING "cn_net: Failure add connector callback\n");
+ goto out_callback;
+ }
+
+ if (register_security(&cn_net_security_ops)) {
+ printk(KERN_INFO "cn_net: Failure registering with kernel\n");
+ if (mod_reg_security(MY_NAME, &cn_net_security_ops)) {
+ printk(KERN_WARNING
+ "cn_net: Failure registering with primary security"
+ " module\n");
+ err = -EINVAL;
+ goto out_security;
+ }
+ secondary = 1;
+ }
+
+ printk(KERN_INFO "cn_net: network events module loaded\n");
+ return 0;
+
+out_security:
+ cn_del_callback(&cn_net_event_id);
+
+out_callback:
+ return err;
+}
+
+static void __exit fini(void) {
+ if (secondary) {
+ if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with"
+ " primary security module\n");
+ } else {
+ if (unregister_security(&cn_net_security_ops))
+ printk(KERN_INFO "cn_net: Failure unregistering with "
+ "kernel\n");
+ }
+
+ cn_del_callback(&cn_net_event_id);
+
+ /* clean memory */
+ remove_all_event();
+
+ printk(KERN_INFO "cn_net: network events module unloaded\n");
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_DESCRIPTION("Network events module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
diff --git a/include/linux/cn_net.h b/include/linux/cn_net.h
new file mode 100644
index 0000000..0d86715
--- /dev/null
+++ b/include/linux/cn_net.h
@@ -0,0 +1,122 @@
+/*
+ * include/linux/cn_net.h
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef CN_NET_H
+#define CN_NET_H
+
+#include <linux/time.h>
+#include <linux/ipv6.h>
+
+#define CN_NET_VERSION 0x1
+
+#define CN_NET_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct net_event))
+
+char *syscall_name[] = { "LISTEN", "BIND", "CONNECT", "SHUTDOWN", "SK_FREE" };
+char *msg_type_name[] = { "CN_NET_NONE", "CN_NET_ACK", "CN_NET_DATA",
+ "CN_NET_CONFIG", "CN_NET_LISTEN", "CN_NET_IGNORE",
+ "CN_NET_DUMP" };
+char *config_name[] = {"CN_NET_CONFIG_ADD", "CN_NET_CONFIG_DEL", "CN_NET_CONFIG_FLUSH" };
+
+/**
+ * identify which syscall has been called.
+ */
+enum cn_net_socket {
+ CN_NET_SOCKET_LISTEN = 0,
+ CN_NET_SOCKET_BIND = 1,
+ CN_NET_SOCKET_CONNECT = 2,
+ CN_NET_SOCKET_SHUTDOWN = 3,
+ CN_NET_SK_FREE_SECURITY = 4,
+ CN_NET_SOCKET_MAX = 5,
+};
+
+/**
+ * Protocol message type
+ */
+enum msg_type {
+ CN_NET_NONE = 0,
+ CN_NET_ACK = 1,
+ CN_NET_DATA = 2,
+ CN_NET_CONFIG = 3,
+ CN_NET_LISTEN = 4,
+ CN_NET_IGNORE = 5,
+ CN_NET_DUMP = 6,
+};
+
+/**
+ * values for CN_NET_ACK messages
+ */
+enum ack_err {
+ CN_NET_ACK_SUCCES = 0,
+ CN_NET_ACK_ENOMEM = 1,
+ CN_NET_ACK_EINCONFIG = 2,
+ CN_NET_ACK_EINTYPE = 3,
+ CN_NET_ACK_EBADPROTO = 4,
+};
+
+/**
+ * values for CN_NET_CONFIG messages
+ */
+enum config_cmd {
+ CN_NET_CONFIG_ADD = 0,
+ CN_NET_CONFIG_DEL = 1,
+ CN_NET_CONFIG_FLUSH = 2,
+};
+
+struct event {
+ enum cn_net_socket syscall_num;
+ __u8 protocol;
+};
+
+struct event_node {
+ struct rb_node ev_node;
+ struct event ev;
+};
+
+struct msg_config {
+ enum config_cmd config_cmd;
+ struct event ev;
+};
+
+struct net_event {
+ enum msg_type msg_type;
+ struct timespec timestamp;
+ union {
+ /* protocol version number */
+ __u32 version;
+
+ /* generic ack for both userspace and kernel */
+ enum ack_err ack;
+
+ /* send data to userspace */
+ struct {
+ struct event ev;
+ uid_t uid;
+ unsigned char taskname[TASK_COMM_LEN];
+ unsigned int family;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } saddr;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } daddr;
+ unsigned int sport;
+ unsigned int dport;
+ } data;
+
+ /* send config to kernel */
+ struct msg_config config;
+ } net_event_data;
+};
+
+#endif /* CN_NET_H */
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 10eb56b..3042360 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -36,9 +36,10 @@ #define CN_IDX_CIFS 0x2
#define CN_VAL_CIFS 0x1
#define CN_W1_IDX 0x3 /* w1 communication */
#define CN_W1_VAL 0x1
+#define CN_IDX_NET 0x4
+#define CN_VAL_NET 0x1
-
-#define CN_NETLINK_USERS 4
+#define CN_NETLINK_USERS 5
/*
* Maximum connector's message size.
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-03-15 1:05 Samir Bellabes
@ 2007-03-15 1:17 ` David Miller
2007-03-15 18:00 ` Evgeniy Polyakov
1 sibling, 0 replies; 8+ messages in thread
From: David Miller @ 2007-03-15 1:17 UTC (permalink / raw)
To: sam; +Cc: netdev
From: Samir Bellabes <sam@synack.fr>
Date: Thu, 15 Mar 2007 02:05:53 +0100
> +#if 0
> +#define DEBUGP printk
> +#else
> +#define DEBUGP(format, args...)
> +#endif
Please no local debugging macros.
> +static unsigned int is_same_event(struct event one, struct event two) {
Please format functions properly, especially wrt. braces, they don't
belong on the line holding the argument list closing parenthesis:
return_type func_name(args)
{
}
I'm not going to read this any further, please master
Documentation/CodingStyle and resubmit if you want further
review from me.
Thanks.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC] [PATCH] Network Events Connector
2007-03-15 1:05 Samir Bellabes
2007-03-15 1:17 ` David Miller
@ 2007-03-15 18:00 ` Evgeniy Polyakov
1 sibling, 0 replies; 8+ messages in thread
From: Evgeniy Polyakov @ 2007-03-15 18:00 UTC (permalink / raw)
To: Samir Bellabes; +Cc: netdev
On Thu, Mar 15, 2007 at 02:05:53AM +0100, Samir Bellabes (sam@synack.fr) wrote:
> Hi,
Hi Samir.
My comments are below.
> here a updated patch for the network events connector.
> review are welcome :)
>
> Thanks.
>
> [PATCH] Network Events Connector
>
> This patch adds a connector which reports networking's events to
> userspace.
>
> It's sending events when a userspace application is using
> syscalls so with LSM, we are catching this, at hooks : socket_listen,
> socket_bind, socket_connect, socket_shutdown and sk_free_security.
>
> Wanted events are dynamically manage from userspace, in a rbtree. A
> daemon, cn_net_daemon, is listening for the connector in userspace.
>
> daemon and doc are available here :
> http://people.mandriva.com/~sbellabes/cn_net/
>
> Signed-off-by: Samir Bellabes <sam@synack.fr>
>
> ------------------------------------------------------------------------------
>
> drivers/connector/Kconfig | 8
> drivers/connector/Makefile | 1
> drivers/connector/cn_net.c | 618 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/cn_net.h | 122 ++++++++
> include/linux/connector.h | 5
> 5 files changed, 752 insertions(+), 2 deletions(-)
>
> ------------------------------------------------------------------------------
>
> diff --git a/drivers/connector/Kconfig b/drivers/connector/Kconfig
> index e0bdc0d..0b04952 100644
> --- a/drivers/connector/Kconfig
> +++ b/drivers/connector/Kconfig
> @@ -18,4 +18,12 @@ config PROC_EVENTS
> Provide a connector that reports process events to userspace. Send
> events such as fork, exec, id change (uid, gid, suid, etc), and exit.
>
> +config NET_EVENTS
> + boolean "Report network events to userspace"
> + depends on CONNECTOR=y && SECURITY_NETWORK
> + default y
> + ---help---
> + Provide a connector that reports networking's events to userspace.
> + Send events such as DCCP/TCP listen/close and UDP bind/close.
> +
> endmenu
> diff --git a/drivers/connector/Makefile b/drivers/connector/Makefile
> index 1f255e4..436bb5d 100644
> --- a/drivers/connector/Makefile
> +++ b/drivers/connector/Makefile
> @@ -1,4 +1,5 @@
> obj-$(CONFIG_CONNECTOR) += cn.o
> obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
> +obj-$(CONFIG_NET_EVENTS) += cn_net.o
>
> cn-y += cn_queue.o connector.o
> diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
> new file mode 100644
> index 0000000..a15f67b
> --- /dev/null
> +++ b/drivers/connector/cn_net.c
> @@ -0,0 +1,618 @@
> +/*
> + * drivers/connector/cn_net.c
> + *
> + * Network events connector
> + * Samir Bellabes <sam@synack.fr>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/skbuff.h>
> +#include <linux/security.h>
> +#include <linux/netlink.h>
> +#include <linux/connector.h>
> +#include <linux/net.h>
> +#include <net/sock.h>
> +#include <net/inet_sock.h>
> +#include <linux/in.h>
> +#include <linux/ipv6.h>
> +#include <linux/in6.h>
> +#include <linux/rbtree.h>
> +#include <linux/list.h>
> +#include <linux/spinlock.h>
> +#include <linux/random.h>
> +
> +#include <linux/cn_net.h>
> +
> +static atomic_t net_event_num_listeners = ATOMIC_INIT(0);
> +static struct cb_id cn_net_event_id = { CN_IDX_NET, CN_VAL_NET };
> +static char cn_net_event_name[] = "cn_net_event";
> +static int secondary = 0;
> +
> +struct rb_root event_tree = RB_ROOT;
That should be static.
> +static rwlock_t event_lock = RW_LOCK_UNLOCKED;
> +
> +#if defined(CONFIG_NET_EVENTS)
> +#define MY_NAME THIS_MODULE->name
> +#else
> +#define MY_NAME "cn_net"
> +#endif
> +
Why is it needed?
> +#if 0
> +#define DEBUGP printk
> +#else
> +#define DEBUGP(format, args...)
> +#endif
> +
> +/**
> + * is_same_event()
> + * check if two events are the same or not
> + */
> +static unsigned int is_same_event(struct event one, struct event two) {
> + return ((one.syscall_num == two.syscall_num) &&
> + (one.protocol == two.protocol));
> +}
Coding style is broken, place '{' on the next line.
> +/**
> + * lookup_event()
> + * look for a match in the rbtree
> + * returns address of the wanted element if it is in the rbtree, or NULL
> + */
> +static struct event_node *lookup_event(struct event ev) {
> + struct rb_node *rb_node = event_tree.rb_node;
> +
> + read_lock(&event_lock);
> +
> + while (rb_node) {
> + struct event_node *cur = rb_entry(rb_node, struct event_node, ev_node);
> +
> + if (is_same_event(cur->ev, ev)) {
> + read_unlock(&event_lock);
> + return cur;
> + }
> +
> + if (ev.syscall_num < cur->ev.syscall_num)
> + rb_node = rb_node->rb_left;
> + else if (ev.syscall_num > cur->ev.syscall_num)
> + rb_node = rb_node->rb_right;
> + else if (ev.protocol < cur->ev.protocol)
> + rb_node = rb_node->rb_left;
> + else if (ev.protocol > cur->ev.protocol)
> + rb_node = rb_node->rb_right;
> + }
> +
> + read_unlock(&event_lock);
> + return NULL;
> +}
> +
> +/**
> + * check_wanted_data()
> + * we don't send unwanted informations to userspace, according to the
> + * dynamic configuration in the rbtree
> + */
> +static int check_wanted_event(struct sock *sk, enum cn_net_socket syscall_num) {
> + int err = -EINVAL;
> + struct event ev;
> +
> + if (!sk)
> + return err;
> +
> + ev.syscall_num = syscall_num;
> + ev.protocol = sk->sk_protocol;
> +
> + /* check if the event is already registered */
> + if (lookup_event(ev))
> + err = 0;
> +
> + return err;
> +}
> +
> +/**
> + * dump_event()
> + * dump the entire rbtree in log
> + */
> +static void dump_event(void) {
> + struct rb_node *p = NULL ;
> + struct event_node *tmp = NULL;
> +
> + read_lock(&event_lock);
> + p = rb_first(&event_tree);
> + while (p) {
> + tmp = rb_entry(p, struct event_node, ev_node);
> + printk(KERN_INFO "%d:%s\n",
> + tmp->ev.protocol, syscall_name[tmp->ev.syscall_num]);
> + p = rb_next(p);
> + };
> + read_unlock(&event_lock);
> +}
I doubt it should exist - it is purely debug stuff.
> +/**
> + * remove_all_event()
> + * delete all events registered in the rbtree
> + */
> +static enum ack_err remove_all_event(void) {
> + struct rb_node *p = NULL;
> + struct event_node *cur = NULL;
> +
> + write_lock(&event_lock);
> + while ((p = rb_first(&event_tree)) != NULL) {
> + cur = rb_entry(p, struct event_node, ev_node);
> + rb_erase(p, &event_tree);
> + kfree(cur);
> + }
> + write_unlock(&event_lock);
> +
> + /* rbtree is now empty */
> + return CN_NET_ACK_SUCCES;
> +}
> +
> +/**
> + * alloc_event()
> + * alloc memory for a event_node, and return pointer to it
> + */
> +static struct event_node *alloc_event(void) {
> + struct event_node *evn = NULL;
> + evn = kzalloc(sizeof(struct event_node), GFP_KERNEL);
> + return evn;
> +}
> +
> +/**
> + * insert_event()
> + * insert a event in the rbtree
> + * we are checking if we are trying to register a same event twice
> + */
> +static enum ack_err insert_event(struct event ev) {
> + struct rb_node **rb_link, *rb_parent;
> + struct event_node *new_evn;
> + enum ack_err ret = CN_NET_ACK_SUCCES;
> +
> + rb_link = &event_tree.rb_node;
> + rb_parent = NULL;
> +
> + write_lock(&event_lock);
> +
> + while(*rb_link) {
> + struct event_node *tmp;
> +
> + rb_parent = *rb_link;
> + tmp = rb_entry(rb_parent, struct event_node, ev_node);
> +
> + if (ev.syscall_num < tmp->ev.syscall_num)
> + rb_link = &rb_parent->rb_left;
> + else if (ev.syscall_num > tmp->ev.syscall_num)
> + rb_link = &rb_parent->rb_right;
> + else if (ev.protocol < tmp->ev.protocol)
> + rb_link = &rb_parent->rb_left;
> + else if (ev.protocol > tmp->ev.protocol)
> + rb_link = &rb_parent->rb_right;
> + else {
> + /* event is already registered */
> + ret = CN_NET_ACK_EINCONFIG;
> + goto out;
> + }
> + }
> +
> + /* no match: event is added to the tree */
> + if ((new_evn = alloc_event()) != NULL) {
This is broken - you allocate under lock with gfp_kernel flags.
I would just allocate event in advance and insert it, if insertion
fails, event can be freed.
> + new_evn->ev.syscall_num = ev.syscall_num;
> + new_evn->ev.protocol = ev.protocol;
> + } else {
> + /* can't allocate memory, exiting */
> + ret = CN_NET_ACK_ENOMEM;
> + goto out;
> + }
> +
> + rb_link_node(&new_evn->ev_node, rb_parent, rb_link);
> + rb_insert_color(&new_evn->ev_node, &event_tree);
> +
> +out:
> + write_unlock(&event_lock);
> + return ret;
> +}
> +
> +/**
> + * remove_event()
> + * delete a entry from the rbtree
> + * we are checking if we are trying to delete a unregistered event
> + */
> +static enum ack_err remove_event(struct event ev) {
> + struct event_node *cur = NULL;
> + enum ack_err ret = CN_NET_ACK_EINCONFIG;
> + struct rb_node *rb_node = event_tree.rb_node;
> +
> + write_lock(&event_lock);
> +
> + while (rb_node) {
> + cur = rb_entry(rb_node, struct event_node, ev_node);
> +
> + if (is_same_event(cur->ev, ev))
> + break;
> +
> + if (ev.syscall_num < cur->ev.syscall_num)
> + rb_node = rb_node->rb_left;
> + else if (ev.syscall_num > cur->ev.syscall_num)
> + rb_node = rb_node->rb_right;
> + else if (ev.protocol < cur->ev.protocol)
> + rb_node = rb_node->rb_left;
> + else if (ev.protocol > cur->ev.protocol)
> + rb_node = rb_node->rb_right;
> + }
> +
> + if (rb_node) {
> + rb_erase(&cur->ev_node, &event_tree);
> + kfree(cur);
> + ret = CN_NET_ACK_SUCCES;
> + }
> + write_unlock(&event_lock);
> +
> + return ret;
> +}
> +
> +/**
> + * do_register()
> + * check if userpace protocol version is same as kernel protocol version
> + */
> +static enum ack_err do_register(__u32 version) {
> + enum ack_err err = CN_NET_ACK_SUCCES;
> +
> + DEBUGP(KERN_INFO "do_register: %d %d\n",
> + version, CN_NET_VERSION);
> +
> + if (version == CN_NET_VERSION)
> + atomic_inc(&net_event_num_listeners);
> + else
> + err = CN_NET_ACK_EBADPROTO;
> + return err;
> +}
> +
> +/**
> + * do_config()
> + * execute config asked by userspace
> + * return enum ack_err
> + */
> +static enum ack_err do_config(struct msg_config *cfg) {
> + enum ack_err err = CN_NET_ACK_SUCCES;
> +
> + DEBUGP(KERN_INFO "do_config: %s %s %d\n",
> + config_name[cfg->config_cmd],
> + syscall_name[cfg->ev.syscall_num],
> + cfg->ev.protocol);
> +
> + switch (cfg->config_cmd) {
> + case CN_NET_CONFIG_ADD:
> + err = insert_event(cfg->ev);
> + break;
> + case CN_NET_CONFIG_DEL:
> + err= remove_event(cfg->ev);
> + break;
> + case CN_NET_CONFIG_FLUSH:
> + err = remove_all_event();
I would call it 'remove_all_events()'.
> + break;
> + default:
> + err = CN_NET_ACK_EINTYPE;
> + break;
> + };
> +
> + return err;
> +}
> +
> +/**
> + * cn_net_ack
> + * Send an acknowledgement message to userspace
> + *
> + * Use 0 for SUCCESS, EFOO otherwise. (see enum ack_err)
> + */
> +static void cn_net_ack(enum ack_err err, unsigned int rcvd_seq, unsigned int rcvd_ack, enum msg_type msg_type)
> +{
> + struct cn_msg *m;
> + struct net_event *net_ev;
> + __u8 buffer[CN_NET_MSG_SIZE];
> +
> + DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
> +
> + if (atomic_read(&net_event_num_listeners) < 1 && err != CN_NET_ACK_EBADPROTO)
> + return;
Why don't you like CN_NET_ACK_EBADPROTO?
> + m = (struct cn_msg *) buffer;
> + net_ev = (struct net_event *) m->data;
> +
> + net_ev->msg_type = msg_type;
> + ktime_get_real_ts(&net_ev->timestamp);
Please be absolutely shure you do not have misalignment here
and/or different size issues with 32/64 dual arches like x86_64.
> + net_ev->net_event_data.ack = err;
> + memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
> + m->seq = rcvd_seq;
> + m->ack = rcvd_ack + 1;
> + m->len = sizeof(struct net_event);
> + cn_netlink_send(m, CN_IDX_NET, gfp_any());
> +}
> +
> +/**
> + * cn_net_ctl
> + * connector callback
> + * @data: message receive from userspace via the connector
> + */
> +void cn_net_ctl(void *data) {
> + struct cn_msg *m = data;
> + struct net_event *net_ev = NULL;
> + enum msg_type msg_type;
> + enum ack_err err = CN_NET_ACK_SUCCES;
> +
> + if (m->len != sizeof(struct net_event)) {
> + printk(KERN_WARNING "cn_net_ctl : message with bad size, discard\n");
Either put it under printk_ratelimit() or discard at all.
> + return;
> + }
> +
> + net_ev = (struct net_event *) m->data;
> +
> + if (net_ev->msg_type != CN_NET_LISTEN &&
> + atomic_read(&net_event_num_listeners) < 1) {
> + printk(KERN_WARNING "cn_net_ctl : register first\n");
The same.
> + return;
> + }
> + msg_type = CN_NET_ACK; /* default response message type */
> +
> + switch (net_ev->msg_type) {
> + case CN_NET_NONE: /* want to play ping pong ? */
> + msg_type = net_ev->msg_type;
> + break;
> + case CN_NET_ACK: /* userspace is ack'ing - check that */
> + /* FIXME: we don't send an ACK to an ACK */
> + /* we just check which message is ack */
> + goto out;
> + break;
> + case CN_NET_DATA: /* CN_NET_DATA can't be used by userspace */
> + err = CN_NET_ACK_EINTYPE;
> + break;
> + case CN_NET_CONFIG: /* configuring kernel's behaviour */
> + err = do_config(&(net_ev->net_event_data.config));
> + break;
> + case CN_NET_LISTEN: /* userspace is registering */
> + err = do_register(net_ev->net_event_data.version);
> + break;
> + case CN_NET_IGNORE: /* userspace is unregistering */
> + atomic_dec(&net_event_num_listeners);
> + break;
> + case CN_NET_DUMP: /* dumping the rbtree -- debug purpose */
> + dump_event();
> + break;
> + default:
> + err = CN_NET_ACK_EINTYPE;
> + break;
> + };
> + cn_net_ack(err, m->seq, m->ack, msg_type);
> +out:
> + return;
> +}
> +
> +/**
> + * cn_net_send_event
> + * send data to userspace
> + * @sock: sock which sock->sk->sk_state change to the state identified by @event
> + */
> +static void cn_net_send_event(struct sock *sk, struct sockaddr *address,
> + enum cn_net_socket syscall_num) {
> +
> + struct net_event *net_ev;
> + struct cn_msg *m;
> + struct inet_sock *inet = inet_sk(sk);
> + struct sockaddr_in *addr4 = NULL;
> + struct sockaddr_in6 *addr6 = NULL;
> + struct task_struct *me = current;
> +
> + __u8 buffer[CN_NET_MSG_SIZE];
> +
> + DEBUGP(KERN_INFO "cn_net_ack: listen=%d\n", atomic_read(&net_event_num_listeners));
> +
> + if (atomic_read(&net_event_num_listeners) < 1)
> + goto out;
> +
> + m = (struct cn_msg *) buffer;
> + net_ev = (struct net_event *) m->data;
> +
> + switch (syscall_num) {
> + case CN_NET_SOCKET_LISTEN:
> + case CN_NET_SOCKET_SHUTDOWN:
> + case CN_NET_SK_FREE_SECURITY:
> + switch (sk->sk_family) {
> + case AF_INET:
> + net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
> + net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
> + break;
> + case AF_INET6:
> + memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
> + &(inet->pinet6->saddr), sizeof(struct in6_addr));
> + memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
> + &(inet->pinet6->daddr), sizeof(struct in6_addr));
> + break;
> + default:
> + /* other protocol, sending nothing */
> + goto out;
> + break;
> + };
> + net_ev->net_event_data.data.sport = ntohs(inet->sport);
> + net_ev->net_event_data.data.dport = ntohs(inet->dport);
> + break;
> + case CN_NET_SOCKET_BIND:
> + switch (sk->sk_family) {
> + case AF_INET:
> + addr4 = (struct sockaddr_in *) address;
> + net_ev->net_event_data.data.saddr.ipv4 = addr4->sin_addr.s_addr;
> + net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
> + net_ev->net_event_data.data.sport = ntohs(addr4->sin_port);
> + net_ev->net_event_data.data.dport = ntohs(inet->dport);
> + break;
> + case AF_INET6:
> + addr6 = (struct sockaddr_in6 *) address;
> + memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
> + &(addr6->sin6_addr), sizeof(struct in6_addr));
> + memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
> + &(inet->pinet6->daddr), sizeof(struct in6_addr));
> + net_ev->net_event_data.data.sport = ntohs(addr6->sin6_port);
> + net_ev->net_event_data.data.dport = ntohs(inet->dport);
> + break;
> + default:
> + /* other protocol, sending nothing */
> + goto out;
> + break;
> + };
> + break;
> + case CN_NET_SOCKET_CONNECT:
> + switch (sk->sk_family) {
> + case AF_INET:
> + addr4 = (struct sockaddr_in *) address;
> + net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
> + net_ev->net_event_data.data.daddr.ipv4 = addr4->sin_addr.s_addr;
> + net_ev->net_event_data.data.sport = ntohs(inet->sport);
> + net_ev->net_event_data.data.dport = ntohs(addr4->sin_port);
> + break;
> + case AF_INET6:
> + addr6 = (struct sockaddr_in6 *) address;
> + memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
> + &(inet->pinet6->saddr), sizeof(struct in6_addr));
> + memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
> + &(addr6->sin6_addr), sizeof(struct in6_addr));
> + net_ev->net_event_data.data.sport = ntohs(inet->sport);
> + net_ev->net_event_data.data.dport = ntohs(addr6->sin6_port);
> + break;
> + default:
> + /* other protocol, sending nothing */
> + goto out;
> + break;
> + };
> + break;
> + default:
> + /* Bad syscall_num */
> + break;
> + };
> + net_ev->msg_type = CN_NET_DATA;
> + ktime_get_real_ts(&net_ev->timestamp);
> + net_ev->net_event_data.data.uid = me->uid;
> + get_task_comm(net_ev->net_event_data.data.taskname, me);
> + net_ev->net_event_data.data.ev.protocol = sk->sk_protocol;
> + net_ev->net_event_data.data.ev.syscall_num = syscall_num;
> + net_ev->net_event_data.data.family = sk->sk_family;
> + memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
> + m->seq = get_random_int();
You generally want to use linear sequence number, although it only
matters if protocol does support its check - you can use ny number of
course.
> + m->ack = 0;
> + m->len = sizeof(struct net_event);
> +
> + cn_netlink_send(m, CN_IDX_NET, gfp_any());
> +out:
> + return;
> +}
> +
> +static int cn_net_socket_listen(struct socket *sock, int backlog) {
> +
> + DEBUGP(KERN_INFO "cn_net_socket_listen\n");
> + if (check_wanted_event(sock->sk, CN_NET_SOCKET_LISTEN) == NULL)
> + cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_LISTEN);
> +
> + return 0;
> +}
> +
> +static int cn_net_socket_bind(struct socket *sock,
> + struct sockaddr *address, int addrlen) {
> +
> + DEBUGP(KERN_INFO "cn_net_socket_bind\n");
> + if (check_wanted_event(sock->sk, CN_NET_SOCKET_BIND) == NULL)
> + cn_net_send_event(sock->sk, address, CN_NET_SOCKET_BIND);
> +
> + return 0;
> +}
> +
> +static int cn_net_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) {
> +
> + DEBUGP(KERN_INFO "cn_net_socket_connect\n");
> + if (check_wanted_event(sock->sk, CN_NET_SOCKET_CONNECT) == NULL)
> + cn_net_send_event(sock->sk, address, CN_NET_SOCKET_CONNECT);
> +
> + return 0;
> +}
> +
> +static int cn_net_socket_shutdown(struct socket *sock, int how) {
> +
> + DEBUGP(KERN_INFO "cn_net_socket_shutdown\n");
> + if (check_wanted_event(sock->sk, CN_NET_SOCKET_SHUTDOWN) == NULL)
> + cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_SHUTDOWN);
> +
> + return 0;
> +}
> +
> +static int cn_net_sk_free_security(struct sock *sk) {
> +
> + DEBUGP(KERN_INFO "cn_net_sk_free_security\n");
> + if (check_wanted_event(sk, CN_NET_SK_FREE_SECURITY) == NULL)
> + cn_net_send_event(sk, NULL, CN_NET_SK_FREE_SECURITY);
> +
> + return 0;
> +}
> +
> +static struct security_operations cn_net_security_ops = {
> + .socket_listen = cn_net_socket_listen,
> + .socket_bind = cn_net_socket_bind,
> + .socket_connect = cn_net_socket_connect,
> + .socket_shutdown = cn_net_socket_shutdown,
> + .sk_free_security = cn_net_sk_free_security,
> +};
> +
> +static int __init init(void) {
> + int err = 0;
> +
> + err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
> + if (err) {
> + printk(KERN_WARNING "cn_net: Failure add connector callback\n");
> + goto out_callback;
> + }
> +
> + if (register_security(&cn_net_security_ops)) {
> + printk(KERN_INFO "cn_net: Failure registering with kernel\n");
> + if (mod_reg_security(MY_NAME, &cn_net_security_ops)) {
> + printk(KERN_WARNING
> + "cn_net: Failure registering with primary security"
> + " module\n");
> + err = -EINVAL;
> + goto out_security;
> + }
> + secondary = 1;
> + }
> +
> + printk(KERN_INFO "cn_net: network events module loaded\n");
> + return 0;
> +
> +out_security:
> + cn_del_callback(&cn_net_event_id);
> +
> +out_callback:
> + return err;
> +}
> +
> +static void __exit fini(void) {
> + if (secondary) {
> + if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
> + printk(KERN_INFO "cn_net: Failure unregistering with"
> + " primary security module\n");
> + } else {
> + if (unregister_security(&cn_net_security_ops))
> + printk(KERN_INFO "cn_net: Failure unregistering with "
> + "kernel\n");
> + }
> +
> + cn_del_callback(&cn_net_event_id);
> +
> + /* clean memory */
> + remove_all_event();
> +
> + printk(KERN_INFO "cn_net: network events module unloaded\n");
> +}
> +
> +module_init(init);
> +module_exit(fini);
> +
> +MODULE_DESCRIPTION("Network events module");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
> diff --git a/include/linux/cn_net.h b/include/linux/cn_net.h
> new file mode 100644
> index 0000000..0d86715
> --- /dev/null
> +++ b/include/linux/cn_net.h
> @@ -0,0 +1,122 @@
> +/*
> + * include/linux/cn_net.h
> + *
> + * Network events connector
> + * Samir Bellabes <sam@synack.fr>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#ifndef CN_NET_H
> +#define CN_NET_H
> +
> +#include <linux/time.h>
> +#include <linux/ipv6.h>
> +
> +#define CN_NET_VERSION 0x1
> +
> +#define CN_NET_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct net_event))
> +
> +char *syscall_name[] = { "LISTEN", "BIND", "CONNECT", "SHUTDOWN", "SK_FREE" };
> +char *msg_type_name[] = { "CN_NET_NONE", "CN_NET_ACK", "CN_NET_DATA",
> + "CN_NET_CONFIG", "CN_NET_LISTEN", "CN_NET_IGNORE",
> + "CN_NET_DUMP" };
> +char *config_name[] = {"CN_NET_CONFIG_ADD", "CN_NET_CONFIG_DEL", "CN_NET_CONFIG_FLUSH" };
Why they should live in header file?
I would sugest to put them into source and provide appropriate helper
functions to access strings.
> +/**
> + * identify which syscall has been called.
> + */
> +enum cn_net_socket {
> + CN_NET_SOCKET_LISTEN = 0,
> + CN_NET_SOCKET_BIND = 1,
> + CN_NET_SOCKET_CONNECT = 2,
> + CN_NET_SOCKET_SHUTDOWN = 3,
> + CN_NET_SK_FREE_SECURITY = 4,
> + CN_NET_SOCKET_MAX = 5,
> +};
> +
> +/**
> + * Protocol message type
> + */
> +enum msg_type {
> + CN_NET_NONE = 0,
> + CN_NET_ACK = 1,
> + CN_NET_DATA = 2,
> + CN_NET_CONFIG = 3,
> + CN_NET_LISTEN = 4,
> + CN_NET_IGNORE = 5,
> + CN_NET_DUMP = 6,
> +};
> +
> +/**
> + * values for CN_NET_ACK messages
> + */
> +enum ack_err {
> + CN_NET_ACK_SUCCES = 0,
> + CN_NET_ACK_ENOMEM = 1,
> + CN_NET_ACK_EINCONFIG = 2,
> + CN_NET_ACK_EINTYPE = 3,
> + CN_NET_ACK_EBADPROTO = 4,
> +};
> +
> +/**
> + * values for CN_NET_CONFIG messages
> + */
> +enum config_cmd {
> + CN_NET_CONFIG_ADD = 0,
> + CN_NET_CONFIG_DEL = 1,
> + CN_NET_CONFIG_FLUSH = 2,
> +};
> +
> +struct event {
> + enum cn_net_socket syscall_num;
> + __u8 protocol;
> +};
Imho you should not use enum as a type - I would put there two shorts to
align to 32 bits.
> +struct event_node {
> + struct rb_node ev_node;
> + struct event ev;
> +};
> +
> +struct msg_config {
> + enum config_cmd config_cmd;
> + struct event ev;
> +};
> +
> +struct net_event {
> + enum msg_type msg_type;
> + struct timespec timestamp;
NO WAY!
It has different size on 32 and 64 bit platforms, you are not allowed to
use it in code supposed to be seen from userspace.
> + union {
> + /* protocol version number */
> + __u32 version;
> +
> + /* generic ack for both userspace and kernel */
> + enum ack_err ack;
> +
> + /* send data to userspace */
> + struct {
> + struct event ev;
> + uid_t uid;
> + unsigned char taskname[TASK_COMM_LEN];
> + unsigned int family;
> + union {
> + struct in6_addr ipv6;
> + __u32 ipv4;
> + } saddr;
> + union {
> + struct in6_addr ipv6;
> + __u32 ipv4;
> + } daddr;
> + unsigned int sport;
> + unsigned int dport;
> + } data;
> +
> + /* send config to kernel */
> + struct msg_config config;
> + } net_event_data;
> +};
And generally all your names in this header suck :)
Add something uniq to them line cn_event_ or ne_ or somthing else, but
definitely not 'struct event'.
--
Evgeniy Polyakov
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-03-15 18:00 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-02-09 4:43 [RFC] [PATCH] Network Events Connector Samir Bellabes
2007-02-17 16:34 ` Evgeniy Polyakov
2007-03-15 0:48 ` Samir Bellabes
2007-03-15 0:50 ` Samir Bellabes
2007-03-15 0:53 ` Samir Bellabes
-- strict thread matches above, loose matches on Subject: below --
2007-03-15 1:05 Samir Bellabes
2007-03-15 1:17 ` David Miller
2007-03-15 18:00 ` Evgeniy Polyakov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).