* [RFC v2 02/10] Revert "lsm: Remove the socket_post_accept() hook"
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
This reverts commit 8651d5c0b1f874c5b8307ae2b858bc40f9f02482.
snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.
Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
include/linux/security.h | 13 +++++++++++++
net/socket.c | 2 ++
security/capability.c | 5 +++++
security/security.c | 5 +++++
4 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index 74e564b..d8ad624 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -938,6 +938,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
* Return 0 if permission is granted.
+ * @socket_post_accept:
+ * This hook allows a security module to copy security
+ * information into the newly created socket's inode.
+ * @sock contains the listening socket structure.
+ * @newsock contains the newly created server socket for connection.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -1674,6 +1679,8 @@ struct security_operations {
struct sockaddr *address, int addrlen);
int (*socket_listen) (struct socket *sock, int backlog);
int (*socket_accept) (struct socket *sock, struct socket *newsock);
+ void (*socket_post_accept) (struct socket *sock,
+ struct socket *newsock);
int (*socket_sendmsg) (struct socket *sock,
struct msghdr *msg, int size);
int (*socket_recvmsg) (struct socket *sock,
@@ -2696,6 +2703,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
int security_socket_listen(struct socket *sock, int backlog);
int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags);
@@ -2778,6 +2786,11 @@ static inline int security_socket_accept(struct socket *sock,
return 0;
}
+static inline void security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
+{
+}
+
static inline int security_socket_sendmsg(struct socket *sock,
struct msghdr *msg, int size)
{
diff --git a/net/socket.c b/net/socket.c
index b4eb361..2e66a5a 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1538,6 +1538,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
fd_install(newfd, newfile);
err = newfd;
+ security_socket_post_accept(sock, newsock);
+
out_put:
fput_light(sock->file, fput_needed);
out:
diff --git a/security/capability.c b/security/capability.c
index a9810dc..61eae40 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -641,6 +641,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
return 0;
}
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
return 0;
@@ -1081,6 +1085,7 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, socket_connect);
set_to_cap_if_null(ops, socket_listen);
set_to_cap_if_null(ops, socket_accept);
+ set_to_cap_if_null(ops, socket_post_accept);
set_to_cap_if_null(ops, socket_sendmsg);
set_to_cap_if_null(ops, socket_recvmsg);
set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 288c3a8..673979f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1082,6 +1082,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
return security_ops->socket_accept(sock, newsock);
}
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ security_ops->socket_post_accept(sock, newsock);
+}
+
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
return security_ops->socket_sendmsg(sock, msg, size);
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 03/10] snet: introduce security/snet, Makefile and Kconfig changes
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
this patch creates folder security/snet and adds changes for Kconfig and Makefile
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/Kconfig | 6 ++++++
security/Makefile | 2 ++
security/snet/Kconfig | 11 +++++++++++
security/snet/Makefile | 14 ++++++++++++++
4 files changed, 33 insertions(+), 0 deletions(-)
create mode 100644 security/snet/Kconfig
create mode 100644 security/snet/Makefile
diff --git a/security/Kconfig b/security/Kconfig
index 226b955..eb37e9a 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -140,6 +140,7 @@ config LSM_MMAP_MIN_ADDR
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
+source security/snet/Kconfig
source security/integrity/ima/Kconfig
@@ -148,6 +149,7 @@ choice
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
+ default DEFAULT_SECURITY_SNET if SECURITY_SNET
default DEFAULT_SECURITY_DAC
help
@@ -163,6 +165,9 @@ choice
config DEFAULT_SECURITY_TOMOYO
bool "TOMOYO" if SECURITY_TOMOYO=y
+ config DEFAULT_SECURITY_SNET
+ bool "snet" if SECURITY_SNET=y
+
config DEFAULT_SECURITY_DAC
bool "Unix Discretionary Access Controls"
@@ -173,6 +178,7 @@ config DEFAULT_SECURITY
default "selinux" if DEFAULT_SECURITY_SELINUX
default "smack" if DEFAULT_SECURITY_SMACK
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
+ default "snet" if DEFAULT_SECURITY_SNET
default "" if DEFAULT_SECURITY_DAC
endmenu
diff --git a/security/Makefile b/security/Makefile
index da20a19..9374523 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
+subdir-$(CONFIG_SECURITY_SNET) += snet
# always enable default capabilities
obj-y += commoncap.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
+obj-$(CONFIG_SECURITY_SNET) += snet/built-in.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+ bool "snet - Security for NETwork syscalls"
+ depends on SECURITY_NETWORK
+ default n
+ ---help---
+ If this option is enabled, the kernel will include support for reporting
+ networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..e8f52f1
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) := snet.o
+
+snet-y := snet_event.o \
+ snet_netlink_helper.o \
+ snet_netlink.o \
+ snet_verdict.o \
+ snet_ticket_helper.o \
+ snet_ticket.o \
+ snet_hooks.o \
+ snet_core.o \
+ snet_utils.o
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 04/10] snet: introduce snet_core
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
this patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace
subsytems are:
- snet_hooks : LSM hooks
- snet_netlink : kernel-user communication (genetlink)
- snet_event : manages the list of protected syscalls
- snet_verdict : provides a waitqueue for syscalls and manage verdicts
from userspace
- snet_ticket : provides a granted-access ticket mecanism
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
include/linux/snet.h | 120 +++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_core.c | 76 ++++++++++++++++++++++++++++
2 files changed, 196 insertions(+), 0 deletions(-)
create mode 100644 include/linux/snet.h
create mode 100644 security/snet/snet_core.c
diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..739601d
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,120 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION 0x1
+#define SNET_NAME "snet"
+
+enum snet_syscall {
+ SNET_SOCKET_CREATE = 0,
+ SNET_SOCKET_BIND,
+ SNET_SOCKET_CONNECT,
+ SNET_SOCKET_LISTEN,
+ SNET_SOCKET_ACCEPT,
+ SNET_SOCKET_POST_ACCEPT,
+ SNET_SOCKET_SENDMSG,
+ SNET_SOCKET_RECVMSG,
+ SNET_SOCKET_SOCK_RCV_SKB,
+ SNET_SOCKET_CLOSE,
+ SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+ enum snet_syscall syscall;
+ u8 protocol;
+};
+
+enum snet_verdict {
+ SNET_VERDICT_GRANT = 0, /* grant the syscall */
+ SNET_VERDICT_DENY, /* deny the syscall */
+ SNET_VERDICT_PENDING, /* waiting for a decision */
+ SNET_VERDICT_NONE, /* no decision can be set */
+ SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+ SNET_TICKET_OFF = 0,
+ SNET_TICKET_FIX,
+ SNET_TICKET_EXTEND,
+};
+
+/* genetlink commands */
+enum {
+ SNET_C_UNSPEC,
+ SNET_C_VERSION,
+ SNET_C_REGISTER,
+ SNET_C_UNREGISTER,
+ SNET_C_INSERT,
+ SNET_C_REMOVE,
+ SNET_C_FLUSH,
+ SNET_C_LIST,
+ SNET_C_VERDICT,
+ SNET_C_CONFIG,
+ __SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+ SNET_A_UNSPEC,
+ SNET_A_VERSION, /* (NLA_U32) the snet protocol version */
+ SNET_A_VERDICT_ID,
+ SNET_A_FAMILY,
+ SNET_A_SYSCALL, /* (NLA_U8) a syscall identifier */
+ SNET_A_PROTOCOL, /* (NLA_U8) a protocol identifier */
+ SNET_A_UID,
+ SNET_A_PID,
+ SNET_A_TYPE,
+ SNET_A_IPV4SADDR,
+ SNET_A_IPV6SADDR,
+ SNET_A_IPV4DADDR,
+ SNET_A_IPV6DADDR,
+ SNET_A_SPORT,
+ SNET_A_DPORT,
+ SNET_A_BUFFER,
+ SNET_A_BUFFER_LEN,
+ SNET_A_VERDICT,
+ SNET_A_VERDICT_DELAY,
+ SNET_A_TICKET_DELAY,
+ SNET_A_TICKET_MODE,
+ __SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME "SNET"
+#define SNET_GENL_VERSION SNET_VERSION
+
+struct snet_sock_half {
+ struct {
+ union {
+ __be32 ip;
+ struct in6_addr ip6;
+ };
+ } u3;
+ struct {
+ __be16 port;
+ } u;
+};
+
+struct snet_info {
+ u32 verdict_id;
+
+ enum snet_syscall syscall;
+ u8 protocol;
+ u8 family;
+
+ int type;
+ struct snet_sock_half src;
+ struct snet_sock_half dst;
+ void *buffer;
+ int len;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..9f2eb2e
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,76 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT; /* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+ int ret;
+
+ pr_debug("initializing: event_hash_size=%u "
+ "verdict_hash_size=%u verdict_delay=%usecs "
+ "default_policy=%s\n",
+ snet_evh_size, snet_vdh_size, snet_verdict_delay,
+ snet_verdict_name(snet_verdict_policy));
+
+ ret = snet_event_init();
+ if (ret < 0)
+ goto event_failed;
+
+ ret = snet_verdict_init();
+ if (ret < 0)
+ goto verdict_failed;
+
+ ret = snet_ticket_init();
+ if (ret < 0)
+ goto ticket_failed;
+
+ /* snet_hooks_init() returns 0 or execute panic() */
+ snet_hooks_init();
+
+ pr_debug("started\n");
+ return 0;
+
+ticket_failed:
+ snet_verdict_exit();
+verdict_failed:
+ snet_event_exit();
+event_failed:
+ pr_debug("stopped\n");
+ return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 06/10] snet: introduce snet_hooks
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
This patch adds the snet LSM's subsystem
snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.
For each hook function, there is a generic mecanism:
0. check if the event [syscall, protocol] is registered
1. prepare informations for userspace
2. send informations to userspace (snet_netlink)
3. wait for verdict from userspace (snet_verdict)
4. apply verdict for the syscall
steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_hooks.c | 710 ++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_hooks.h | 10 +
2 files changed, 720 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_hooks.c
create mode 100644 security/snet/snet_hooks.h
diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..689babe
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,710 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ * family, type, protocol
+ * SOCKET_BIND:
+ * family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ * family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ * family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ * family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+
+static inline void snet_pr_tuple(struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ pr_debug("%pI4:%u->%pI4:%u\n",
+ &info->src.u3.ip, info->src.u.port,
+ &info->dst.u3.ip, info->dst.u.port);
+ break;
+ case AF_INET6:
+ pr_debug("%pI6:%u->%pI6:%u\n",
+ &info->src.u3.ip6, info->src.u.port,
+ &info->dst.u3.ip6, info->dst.u.port);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+ if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+ if (verdict != NULL)
+ *verdict = SNET_VERDICT_GRANT;
+ return -1;
+ }
+ return 0;
+}
+
+static int snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+ if (info->verdict_id == 0)
+ return -1;
+ /* sending networking informations to userspace */
+ if (snet_nl_send_event(info) == 0)
+ /* waiting for userspace reply or timeout */
+ *verdict = snet_verdict_wait(info->verdict_id);
+ /* removing verdict */
+ snet_verdict_remove(info->verdict_id);
+ return 0;
+}
+
+static void snet_do_send_event(struct snet_info *info)
+{
+ snet_nl_send_event(info);
+ return;
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+ /* if (kern) */
+ /* ; /\* do something smart *\/ */
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+ struct snet_info info;
+
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_CREATE;
+ info.protocol = protocol;
+ info.family = family;
+ info.type = type;
+
+ pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+ family, type, protocol, kern);
+
+ snet_do_verdict(&verdict, &info);
+
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+
+out:
+ return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+ struct sockaddr_in *a = (struct sockaddr_in *) address;
+ struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_BIND;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = a->sin_addr.s_addr;
+ info.dst.u3.ip = inet->inet_daddr;
+ info.src.u.port = ntohs(a->sin_port);
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ info.src.u.port = ntohs(a6->sin6_port);
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+ struct sockaddr_in *a = (struct sockaddr_in *) address;
+ struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+ /* prepare networking informations for userspace */
+ memset(&info, 0, sizeof(struct snet_info));
+ info.syscall = SNET_SOCKET_CONNECT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = a->sin_addr.s_addr;
+ info.dst.u.port = ntohs(a->sin_port);
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+ sizeof(info.dst.u3.ip6));
+ info.dst.u.port = ntohs(a6->sin6_port);
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ memset(&info, 0, sizeof(struct snet_info));
+ info.syscall = SNET_SOCKET_LISTEN;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_ACCEPT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ u8 protocol = 0;
+
+ if (snet_check_listeners(NULL) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(newsock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_POST_ACCEPT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.verdict_id = 0;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ goto out;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_send_event(&info);
+ }
+out:
+ return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_SENDMSG;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+ info.len = size;
+ info.buffer = msg->msg_iov->iov_base;
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_RECVMSG;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ if ((verdict = snet_ticket_check(&info)) != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+ info.protocol = protocol;
+ info.family = sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sk->sk_family) {
+ case PF_INET:
+ /* inserting verdict PENDING */
+ /* info.verdict_id = snet_verdict_insert(); */
+
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ /* inserting verdict PENDING */
+ /* info.verdict_id = snet_verdict_insert(); */
+
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ /* SNET_DOC_VERDICT(info); */
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+ u8 protocol = 0;
+
+ if (sock == NULL || sock->sk == NULL) {
+ goto out;
+ }
+
+ if (snet_check_listeners(NULL) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_CLOSE;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.verdict_id = 0;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ goto out;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_send_event(&info);
+ }
+out:
+ return;
+}
+
+static struct security_operations snet_security_ops = {
+ .name = "snet",
+
+ .socket_create = snet_socket_create,
+ .socket_bind = snet_socket_bind,
+ .socket_connect = snet_socket_connect,
+ .socket_listen = snet_socket_listen,
+ .socket_accept = snet_socket_accept,
+ .socket_post_accept = snet_socket_post_accept,
+ .socket_sendmsg = snet_socket_sendmsg,
+ .socket_recvmsg = snet_socket_recvmsg,
+ .socket_sock_rcv_skb = snet_socket_sock_rcv_skb,
+ .socket_close = snet_socket_close,
+
+ .cred_prepare = snet_prepare_creds,
+ .cred_free = snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+ if (!security_module_enable(&snet_security_ops))
+ return 0;
+
+ if (register_security(&snet_security_ops))
+ panic("snet: failed to register security_ops\n");
+
+ return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 07/10] snet: introduce snet_netlink
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
this patch adds the snet communication's subsystem.
snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_netlink.c | 431 +++++++++++++++++++++++++++++++++++
security/snet/snet_netlink.h | 17 ++
security/snet/snet_netlink_helper.c | 230 +++++++++++++++++++
security/snet/snet_netlink_helper.h | 7 +
4 files changed, 685 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_netlink.c
create mode 100644 security/snet/snet_netlink.h
create mode 100644 security/snet/snet_netlink_helper.c
create mode 100644 security/snet/snet_netlink_helper.h
diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..b0dd163
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,431 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+ struct sk_buff *skb_rsp;
+ void *msg_head;
+ int ret = 0, sbs = -1;
+ size_t size = 0;
+
+ sbs = snet_nl_size_by_syscall(info);
+ if (sbs < 0)
+ return -EINVAL;
+
+ size = sbs +
+ 2 * nla_total_size(sizeof(u8)) +
+ 1 * nla_total_size(sizeof(u16)) +
+ (info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+ skb_rsp = genlmsg_new(size, GFP_KERNEL);
+ if (skb_rsp == NULL)
+ return -ENOMEM;
+
+ msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+ atomic_inc_return(&snet_nl_seq),
+ &snet_genl_family, 0, SNET_C_VERDICT);
+ if (msg_head == NULL)
+ goto nla_put_failure;
+
+ pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+ "family=%u uid=%u pid=%u\n",
+ info->verdict_id, snet_syscall_name(info->syscall),
+ info->protocol, info->family, current_uid(), current->pid);
+
+ if (info->verdict_id)
+ NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+ NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+ NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+ NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+ NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+ NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+ ret = snet_nl_fill_by_syscall(skb_rsp, info);
+ if (ret != 0)
+ goto nla_put_failure;
+
+ ret = genlmsg_end(skb_rsp, msg_head);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+ kfree_skb(skb_rsp);
+ return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = SNET_GENL_NAME,
+ .version = SNET_GENL_VERSION,
+ .maxattr = SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+ [SNET_A_VERSION] = { .type = NLA_U32 },
+ [SNET_A_VERDICT_ID] = { .type = NLA_U32 },
+ [SNET_A_FAMILY] = { .type = NLA_U8 },
+ [SNET_A_SYSCALL] = { .type = NLA_U16 },
+ [SNET_A_PROTOCOL] = { .type = NLA_U8 },
+ [SNET_A_UID] = { .type = NLA_U32 },
+ [SNET_A_PID] = { .type = NLA_U32 },
+ [SNET_A_TYPE] = { .type = NLA_U32 },
+ [SNET_A_IPV4SADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in_addr) },
+ [SNET_A_IPV6SADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [SNET_A_IPV4DADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in_addr) },
+ [SNET_A_IPV6DADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [SNET_A_BUFFER] = { .type = NLA_BINARY },
+ [SNET_A_BUFFER_LEN] = { .type = NLA_U32 },
+ [SNET_A_SPORT] = { .type = NLA_U16 },
+ [SNET_A_DPORT] = { .type = NLA_U16 },
+ [SNET_A_VERDICT] = { .type = NLA_U8 },
+ [SNET_A_VERDICT_DELAY] = { .type = NLA_U32 },
+ [SNET_A_TICKET_DELAY] = { .type = NLA_U32 },
+ [SNET_A_TICKET_MODE] = { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ struct sk_buff *skb_rsp = NULL;
+ void *msg_head;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb_rsp == NULL)
+ return -ENOMEM;
+ msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+ 0, SNET_C_VERSION);
+ if (msg_head == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+ ret = genlmsg_end(skb_rsp, msg_head);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ ret = genlmsg_reply(skb_rsp, info);
+ if (ret != 0)
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb_rsp);
+ return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ u32 version = 0;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_VERSION]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+ if (version != SNET_VERSION) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (snet_nl_pid > 0) {
+ ret = -ECONNREFUSED;
+ goto out;
+ }
+ snet_nl_pid = info->snd_pid;
+ pr_debug("pid=%u\n", snet_nl_pid);
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (snet_nl_pid == 0) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ snet_nl_pid = 0;
+ pr_debug("pid=%u\n", snet_nl_pid);
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ enum snet_syscall syscall;
+ u8 protocol;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+ protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+ ret = snet_event_insert(syscall, protocol);
+ pr_debug("syscall=%s protocol=%u insert=%s\n",
+ snet_syscall_name(syscall), protocol,
+ (ret == 0) ? "success" : "failed");
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ enum snet_syscall syscall;
+ u8 protocol;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+ protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+ ret = snet_event_remove(syscall, protocol);
+ pr_debug("syscall=%s protocol=%u remove=%s\n",
+ snet_syscall_name(syscall), protocol,
+ (ret == 0) ? "success" : "failed");
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+ atomic_set(&snet_nl_seq, info->snd_seq);
+ snet_event_flush();
+ return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+ u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+ if (hdr == NULL)
+ return -1;
+
+ NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+ NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ unsigned int len = 0;
+
+ atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+ len = snet_event_fill_info(skb, cb);
+ return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ u32 verdict_id;
+ enum snet_verdict verdict;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (snet_nl_pid == 0) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+ verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+ ret = snet_verdict_set(verdict_id, verdict);
+out:
+ return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ int ret = -EINVAL;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (info->attrs[SNET_A_VERDICT_DELAY]) {
+ snet_verdict_delay = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+ pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+ ret = 0;
+ }
+ if (info->attrs[SNET_A_TICKET_DELAY]) {
+ snet_ticket_delay = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+ pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+ ret = 0;
+ }
+ if (info->attrs[SNET_A_TICKET_MODE]) {
+ snet_ticket_mode = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+ pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+ ret = 0;
+ }
+ return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname) \
+ { \
+ .cmd = _cmd, \
+ .flags = _flags, \
+ .policy = _policy, \
+ ._op = snet_nl_##_opname, \
+ }
+
+static struct genl_ops snet_genl_ops[] = {
+ SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, version),
+ SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, register),
+ SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, unregister),
+ SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, insert),
+ SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, remove),
+ SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, flush),
+ SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+ dumpit, list),
+ SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, verdict),
+ SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+ return genl_register_family_with_ops(&snet_genl_family,
+ snet_genl_ops,
+ ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+ genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+ u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d6b5343
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,230 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ switch (info->family) {
+ case PF_INET:
+ ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+ sizeof(struct in_addr), &(info->src.u3.ip));
+ if (ret != 0)
+ goto out;
+ break;
+ case PF_INET6:
+ ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+ sizeof(struct in6_addr), &(info->src.u3.ip6));
+ if (ret != 0)
+ goto out;
+ break;
+ default:
+ break;
+ }
+ ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+ return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ switch (info->family) {
+ case PF_INET:
+ ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+ sizeof(struct in_addr), &(info->dst.u3.ip));
+ if (ret != 0)
+ goto out;
+ break;
+ case PF_INET6:
+ ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+ sizeof(struct in6_addr), &(info->dst.u3.ip6));
+ if (ret != 0)
+ goto out;
+ break;
+ default:
+ break;
+ }
+ ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+ return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+ return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+ struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = nla_put(skb_rsp, SNET_A_BUFFER, info->len, info->buffer);
+ if (ret != 0)
+ goto out;
+ ret = nla_put_u32(skb_rsp, SNET_A_BUFFER_LEN, info->len);
+out:
+ return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+ struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+ [SNET_SOCKET_CREATE] = &snet_fill_create,
+ [SNET_SOCKET_BIND] = &snet_fill_bind,
+ [SNET_SOCKET_CONNECT] = &snet_fill_connect,
+ [SNET_SOCKET_LISTEN] = &snet_fill_listen,
+ [SNET_SOCKET_ACCEPT] = &snet_fill_accept,
+ [SNET_SOCKET_POST_ACCEPT] = &snet_fill_post_accept,
+ [SNET_SOCKET_SENDMSG] = &snet_fill_sendmsg,
+ [SNET_SOCKET_RECVMSG] = &snet_fill_recvmsg,
+ [SNET_SOCKET_SOCK_RCV_SKB] = &snet_fill_sock_rcv_skb,
+ [SNET_SOCKET_CLOSE] = &snet_fill_close,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return -EINVAL;
+ else
+ return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+ unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+ nla_total_size(sizeof(u16));
+ unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+ nla_total_size(sizeof(u16));
+
+ unsigned int sbs4[] = {
+ [SNET_SOCKET_CREATE] = nla_total_size(sizeof(u8)),
+ [SNET_SOCKET_BIND] = size_addr4_port,
+ [SNET_SOCKET_CONNECT] = 2 * size_addr4_port,
+ [SNET_SOCKET_LISTEN] = size_addr4_port,
+ [SNET_SOCKET_ACCEPT] = size_addr4_port,
+ [SNET_SOCKET_POST_ACCEPT] = 2 * size_addr4_port,
+ [SNET_SOCKET_SENDMSG] = 2 * size_addr4_port +
+ info->len +
+ nla_total_size(sizeof(u32)),
+ [SNET_SOCKET_RECVMSG] = 2 * size_addr4_port,
+ [SNET_SOCKET_SOCK_RCV_SKB] = 2 * size_addr4_port,
+ [SNET_SOCKET_CLOSE] = 2 * size_addr4_port,
+ };
+
+ unsigned int sbs6[] = {
+ [SNET_SOCKET_CREATE] = nla_total_size(sizeof(u8)),
+ [SNET_SOCKET_BIND] = size_addr6_port,
+ [SNET_SOCKET_CONNECT] = 2 * size_addr6_port,
+ [SNET_SOCKET_LISTEN] = size_addr6_port,
+ [SNET_SOCKET_ACCEPT] = size_addr6_port,
+ [SNET_SOCKET_POST_ACCEPT] = 2 * size_addr6_port,
+ [SNET_SOCKET_SENDMSG] = 2 * size_addr6_port +
+ info->len +
+ nla_total_size(sizeof(u32)),
+ [SNET_SOCKET_RECVMSG] = 2 * size_addr6_port,
+ [SNET_SOCKET_SOCK_RCV_SKB] = 2 * size_addr6_port,
+ [SNET_SOCKET_CLOSE] = 2 * size_addr6_port,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return -EINVAL;
+ else {
+ switch (info->family) {
+ case AF_INET:
+ return sbs4[info->syscall];
+ break;
+ case AF_INET6:
+ return sbs6[info->syscall];
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ }
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 08/10] snet: introduce snet_verdict
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
This patch adds the snet's subsystem responsive of managing verdicts
snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).
This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_verdict.c | 184 ++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_verdict.h | 23 +++++
2 files changed, 207 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_verdict.c
create mode 100644 security/snet/snet_verdict.h
diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..480a7f8
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,184 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+ struct list_head list;
+ u32 verdict_id;
+ enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+ unsigned int h = 0;
+ struct list_head *l = NULL;
+ struct snet_verdict_entry *s = NULL;
+
+ h = verdict_id % snet_vdh_size;
+ l = &snet_vdh[h];
+
+ list_for_each_entry(s, l, list) {
+ if (s->verdict_id == verdict_id) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ long ret = 0;
+
+ ret = wait_event_timeout(snet_wq,
+ (verdict = snet_verdict_get(verdict_id))
+ != SNET_VERDICT_PENDING,
+ snet_verdict_delay * HZ);
+ if (ret)
+ return snet_verdict_get(verdict_id);
+ else
+ return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+ enum snet_verdict v = SNET_VERDICT_NONE;
+ struct snet_verdict_entry *data = NULL;
+
+ read_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data != NULL)
+ v = data->verdict;
+
+ read_unlock_bh(&snet_vdh_lock);
+ return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+ struct snet_verdict_entry *data = NULL;
+ int ret = -EINVAL;
+
+ if (verdict >= SNET_NR_VERDICT_TYPES)
+ goto out;
+
+ write_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data != NULL) {
+ /* if verdict is already set because of
+ timeout, we won't modify it */
+ if (data->verdict == SNET_VERDICT_PENDING) {
+ data->verdict = verdict;
+ ret = 0;
+ }
+ }
+ write_unlock_bh(&snet_vdh_lock);
+ wake_up(&snet_wq);
+out:
+ return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+ struct snet_verdict_entry *data = NULL;
+
+ write_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data == NULL) {
+ write_unlock_bh(&snet_vdh_lock);
+ return -EINVAL;
+ }
+ pr_debug("(verdict_id=%u)\n", data->verdict_id);
+ list_del(&data->list);
+ write_unlock_bh(&snet_vdh_lock);
+ kfree(data);
+ return 0;
+}
+
+int snet_verdict_insert(void)
+{
+ struct snet_verdict_entry *data = NULL;
+ unsigned int h = 0;
+ u32 verdict_id = 0;
+
+ data = kzalloc(sizeof(struct snet_verdict_entry), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ do {
+ verdict_id = atomic_inc_return(&value);
+ } while (verdict_id == 0);
+
+ data->verdict_id = verdict_id;
+ data->verdict = SNET_VERDICT_PENDING;
+ INIT_LIST_HEAD(&(data->list));
+ h = data->verdict_id % snet_vdh_size;
+
+ write_lock_bh(&snet_vdh_lock);
+ list_add_tail(&data->list, &snet_vdh[h]);
+ pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+ write_unlock_bh(&snet_vdh_lock);
+
+ return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+ unsigned int i = 0;
+
+ write_lock_bh(&snet_vdh_lock);
+ for (i = 0; i < snet_vdh_size; i++) {
+ struct snet_verdict_entry *data, *tmp;
+ list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+ list_del(&data->list);
+ kfree(data);
+ }
+ }
+ write_unlock_bh(&snet_vdh_lock);
+ return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+ int err = 0, i = 0;
+
+ snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+ GFP_KERNEL);
+ if (!snet_vdh) {
+ printk(KERN_WARNING
+ "snet: can't alloc memory for verdict\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < snet_vdh_size; i++)
+ INIT_LIST_HEAD(&snet_vdh[i]);
+
+out:
+ return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+ if (snet_vdh) {
+ kfree(snet_vdh);
+ snet_vdh = NULL;
+ }
+
+ return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 09/10] snet: introduce snet_ticket
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
this patch adds the snet's subsystem managing granted-access tickets
snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.
generic informations:
timeout
syscall
protocol
verdict
protocol-dependant informations : (so some infos may not be used)
address family
socket type
source address
source port
distant address
distant port
ticket are attached to the "void *security" pointer of task_struct
there are 3 modes:
0. no ticket - SNET_TICKET_OFF
every syscalls has to be verified by userspace.
1. timeout fixed - SNET_TICKET_FIX
for each response from the userspace, we are creating a ticket,
attached to the task_struct, with the filled informations, and a
fixed timeout value (10 secs by default).
then before asking userspace, kernel mecanism is checking existing
tickets for the task_struct, if there is a granted-access ticket, we
are using the verdict value attached.
after the timeout value, the ticket is destroyed.
2. timeout with extendable value - SNET_TICKET_EXTEND
this is the same mecanism as 1, but every time a ticket is matched
and used, the timeout value is reset to the default value, so its
life is extended.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_ticket.c | 171 ++++++++++++++++++++++++++++++++++++
security/snet/snet_ticket.h | 37 ++++++++
security/snet/snet_ticket_helper.c | 127 ++++++++++++++++++++++++++
security/snet/snet_ticket_helper.h | 8 ++
4 files changed, 343 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_ticket.c
create mode 100644 security/snet/snet_ticket.h
create mode 100644 security/snet/snet_ticket_helper.c
create mode 100644 security/snet/snet_ticket_helper.h
diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..62ced7b
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,171 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+ struct snet_ticket *st = NULL;
+ unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+ struct list_head *l = NULL;
+ struct snet_task_security *tsec = NULL;
+
+ if (snet_ticket_mode == SNET_TICKET_OFF)
+ goto out;
+
+ tsec = (struct snet_task_security*) current_security();
+
+ h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+ l = &tsec->hash[h];
+
+ read_lock_bh(&tsec->lock);
+ list_for_each_entry(st, l, list) {
+ if (__ticket_check(st, info)) {
+ verdict = st->verdict;
+ pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+ st, st->tsec);
+ if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+ mod_timer(&st->timeout,
+ jiffies + snet_ticket_delay * HZ);
+ }
+ break;
+ }
+ }
+ read_unlock_bh(&tsec->lock);
+out:
+ return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+ struct snet_ticket *st = (struct snet_ticket*)arg;
+
+ pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+ write_lock_bh(&st->tsec->lock);
+ list_del(&st->list);
+ write_unlock_bh(&st->tsec->lock);
+ kfree(st);
+ return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+ struct snet_ticket *st = NULL;
+
+ st = kzalloc(sizeof(struct snet_ticket), GFP_KERNEL);
+ if (st == NULL)
+ goto out;
+
+ INIT_LIST_HEAD(&st->list);
+ init_timer(&st->timeout);
+ st->timeout.expires = snet_ticket_delay * HZ;
+out:
+ return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+ unsigned int h;
+ struct list_head *l;
+
+ h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+ l = &(st->tsec->hash[h]);
+
+ st->timeout.expires += jiffies;
+ add_timer(&st->timeout);
+
+ write_lock_bh(&(st->tsec->lock));
+ list_add_tail(&st->list, l);
+ write_unlock_bh(&(st->tsec->lock));
+ return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+ struct snet_ticket *st;
+ struct snet_task_security *tsec = NULL;
+
+ if (snet_ticket_mode == SNET_TICKET_OFF)
+ goto out;
+
+ tsec = (struct snet_task_security*) current_security();
+
+ st = snet_ticket_alloc();
+ if (st == NULL)
+ goto out;
+
+ st->tsec = tsec;
+ snet_ticket_fill(st, info, verdict);
+ setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+ snet_ticket_insert(st);
+out:
+ return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+ unsigned int index = 0;
+ struct snet_task_security *tsec = NULL;
+
+ tsec = kzalloc(sizeof(struct snet_task_security), gfp);
+ if (tsec == NULL)
+ return -ENOMEM;
+
+ pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+ rwlock_init(&tsec->lock);
+ for (index = 0; index < HSIZE; index++)
+ INIT_LIST_HEAD(&tsec->hash[index]);
+
+ new->security = tsec;
+ return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+ struct snet_task_security *tsec = cred->security;
+ unsigned int index;
+
+ pr_debug("ticket_free_cred: pid=%u tsec=%p\n", current->pid, tsec);
+
+ write_lock_bh(&tsec->lock);
+ /* destroy all tickets */
+ for (index = 0; index < HSIZE; index++) {
+ struct snet_ticket *st, *tmp;
+ list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+ if (del_timer_sync(&st->timeout)) {
+ pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+ index, st, st->tsec);
+ list_del(&st->list);
+ kfree(st);
+ }
+ }
+ }
+ cred->security = NULL;
+ write_unlock_bh(&tsec->lock);
+ kfree(tsec);
+ return;
+}
+
+int snet_ticket_init(void)
+{
+ unsigned int index = 0;
+ struct cred *cred = (struct cred *) current->real_cred;
+ struct snet_task_security *tsec = NULL;
+
+ tsec = kzalloc(sizeof(struct snet_task_security), GFP_KERNEL);
+ if (tsec == NULL)
+ return -ENOMEM;
+
+ rwlock_init(&tsec->lock);
+ for (index = 0; index < HSIZE; index++)
+ INIT_LIST_HEAD(&tsec->hash[index]);
+
+ cred->security = tsec;
+ return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+ struct list_head hash[16];
+ rwlock_t lock;
+};
+
+struct snet_ticket {
+ struct list_head list;
+ struct snet_task_security *tsec;
+ struct timer_list timeout;
+
+ enum snet_syscall syscall;
+ u8 protocol;
+ u8 family;
+ int type;
+ struct snet_sock_half src;
+ struct snet_sock_half dst;
+ enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif /* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+ return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ if ((st->src.u3.ip == info->src.u3.ip) &&
+ (st->src.u.port == info->src.u.port))
+ return 1;
+ break;
+ case AF_INET6:
+ if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+ sizeof(info->src.u3.ip6))) &&
+ (st->src.u.port == info->src.u.port))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ if ((st->dst.u3.ip == info->dst.u3.ip) &&
+ (st->dst.u.port == info->dst.u.port))
+ return 1;
+ break;
+ case AF_INET6:
+ if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+ sizeof(info->dst.u3.ip6))) &&
+ (st->dst.u.port == info->dst.u.port))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+ return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+ return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+ static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+ [SNET_SOCKET_CREATE] = &check_create,
+ [SNET_SOCKET_BIND] = &check_src,
+ [SNET_SOCKET_CONNECT] = &check_dst,
+ [SNET_SOCKET_LISTEN] = &check_src,
+ [SNET_SOCKET_ACCEPT] = &check_src,
+ [SNET_SOCKET_POST_ACCEPT] = &check_none,
+ [SNET_SOCKET_SENDMSG] = &check_src_and_dst,
+ [SNET_SOCKET_RECVMSG] = &check_src_and_dst,
+ [SNET_SOCKET_SOCK_RCV_SKB] = &check_src_and_dst,
+ [SNET_SOCKET_CLOSE] = &check_none,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return 0;
+ else {
+ if ((st->syscall == info->syscall) &&
+ (st->protocol == info->protocol) &&
+ (st->family == info->family) &&
+ ticket_df[info->syscall](st, info))
+ return 1;
+ else
+ return 0;
+ }
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+ enum snet_verdict verdict)
+{
+ st->syscall = info->syscall;
+ st->protocol = info->protocol;
+ st->family = info->family;
+ st->src.u.port = info->src.u.port;
+ st->dst.u.port = info->dst.u.port;
+ st->verdict = verdict;
+
+ switch (info->family) {
+ case AF_INET:
+ st->src.u3.ip = info->src.u3.ip;
+ st->dst.u3.ip = info->dst.u3.ip;
+ pr_debug("ticket=%p [syscall=%s protocol=%u "
+ "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+ st, snet_syscall_name(st->syscall), st->protocol,
+ st->family, &st->src.u3.ip, st->src.u.port,
+ &st->dst.u3.ip, st->dst.u.port,
+ snet_verdict_name(st->verdict), st->tsec, current->pid);
+ break;
+ case AF_INET6:
+ memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+ sizeof(info->src.u3.ip6));
+ memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+ sizeof(info->dst.u3.ip6));
+ pr_debug("ticket=%p [syscall=%s protocol=%u "
+ "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+ st, snet_syscall_name(st->syscall), st->protocol,
+ st->family, &st->src.u3.ip6, st->src.u.port,
+ &st->dst.u3.ip6, st->dst.u.port,
+ snet_verdict_name(st->verdict), st->tsec, current->pid);
+ break;
+ default:
+ break;
+ }
+ return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+ enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif /* _SNET_TICKET_HELPER_H */
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 10/10] snet: introduce snet_utils
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>
This patch provides helper functions for other subsystems
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_utils.c | 38 ++++++++++++++++++++++++++++++++++++++
security/snet/snet_utils.h | 10 ++++++++++
2 files changed, 48 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_utils.c
create mode 100644 security/snet/snet_utils.h
diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..e9178d7
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,38 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+ static const char *const verdict_name[] = {
+ [SNET_VERDICT_GRANT] = "Grant",
+ [SNET_VERDICT_DENY] = "Deny",
+ [SNET_VERDICT_PENDING] = "Pending",
+ [SNET_VERDICT_NONE] = "None",
+ };
+
+ if (cmd >= SNET_NR_VERDICT_TYPES)
+ return "INVALID";
+ else
+ return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+ static const char *const syscall_name[] = {
+ [SNET_SOCKET_CREATE] = "Create",
+ [SNET_SOCKET_BIND] = "Bind",
+ [SNET_SOCKET_CONNECT] = "Connect",
+ [SNET_SOCKET_LISTEN] = "Listen",
+ [SNET_SOCKET_ACCEPT] = "Accept",
+ [SNET_SOCKET_POST_ACCEPT] = "Post Accept",
+ [SNET_SOCKET_SENDMSG] = "Sendmsg",
+ [SNET_SOCKET_RECVMSG] = "Recvmsg",
+ [SNET_SOCKET_SOCK_RCV_SKB] = "Sock Rcv Skb",
+ [SNET_SOCKET_CLOSE] = "Close",
+ };
+
+ if (sys >= SNET_NR_SOCKET_TYPES)
+ return "INVALID";
+ else
+ return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..4dad18b
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+int snet_data_fill(struct sk_buff *skb_rsp, struct snet_info *info);
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif /* _SNET_UTILS_H */
--
1.6.3.3
^ permalink raw reply related
* [RFC v2 05/10] snet: introduce snet_event
From: Samir Bellabes @ 2010-03-02 20:23 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Neil Horman, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1267561394-13626-1-git-send-email-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/snet_event.c | 189 ++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_event.h | 21 +++++
2 files changed, 210 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_event.c
create mode 100644 security/snet/snet_event.h
diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..9e3f7d2
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,189 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static rwlock_t snet_evh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_event_entry {
+ struct list_head list;
+ struct snet_event se;
+};
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_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;
+
+ /* computing its hash value */
+ h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+ l = &snet_evh[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(&snet_evh_lock);
+
+ for (i = 0; i < snet_evh_size; i++) {
+ if (i < hashs_to_skip)
+ continue;
+ l = &snet_evh[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(&snet_evh_lock);
+
+ cb->args[0] = i;
+ cb->args[1] = n;
+ return skb->len;
+}
+
+/*
+ * 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(&snet_evh_lock);
+ if (__snet_event_lookup(syscall, protocol) != NULL)
+ ret = 1;
+ read_unlock_bh(&snet_evh_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;
+ int err = 0;
+
+ data = kzalloc(sizeof(struct snet_event_entry), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ write_lock_bh(&snet_evh_lock);
+ /* check if event is already registered */
+ if (__snet_event_lookup(syscall, protocol) != NULL) {
+ write_unlock_bh(&snet_evh_lock);
+ kfree(data);
+ err = -EINVAL;
+ goto out;
+ }
+
+ data->se.syscall = syscall;
+ data->se.protocol = protocol;
+ INIT_LIST_HEAD(&(data->list));
+ h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+ list_add_tail(&data->list, &snet_evh[h]);
+ write_unlock_bh(&snet_evh_lock);
+ pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+ h, snet_syscall_name(syscall), protocol);
+out:
+ return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+ struct snet_event_entry *data = NULL;
+
+ write_lock_bh(&snet_evh_lock);
+ data = __snet_event_lookup(syscall, protocol);
+ if (data == NULL) {
+ write_unlock_bh(&snet_evh_lock);
+ return -EINVAL;
+ }
+ pr_debug("(syscall=%s, protocol=%u)\n",
+ snet_syscall_name(syscall), protocol);
+ list_del(&data->list);
+ write_unlock_bh(&snet_evh_lock);
+ kfree(data);
+ return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+ unsigned int i = 0;
+
+ write_lock_bh(&snet_evh_lock);
+ for (i = 0; i < snet_evh_size; i++) {
+ struct snet_event_entry *data, *tmp;
+ list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+ list_del(&data->list);
+ kfree(data);
+ }
+ }
+ write_unlock_bh(&snet_evh_lock);
+ return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+ int err = 0, i = 0;
+
+ snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+ GFP_KERNEL);
+ if (!snet_evh) {
+ printk(KERN_WARNING
+ "snet: can't alloc memory for snet_evh\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < snet_evh_size; i++)
+ INIT_LIST_HEAD(&snet_evh[i]);
+
+out:
+ return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+ kfree(snet_evh);
+ snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_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 */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
--
1.6.3.3
^ permalink raw reply related
* Re: [RFC][PATCH] ns: Syscalls for better namespace sharing control.
From: Sukadev Bhattiprolu @ 2010-03-02 21:19 UTC (permalink / raw)
To: Pavel Emelyanov
Cc: Daniel Lezcano, Linux Netdev List, containers,
Netfilter Development Mailinglist, Eric W. Biederman, Ben Greear
In-Reply-To: <4B8D28CF.8060304@parallels.com>
Pavel Emelyanov [xemul@parallels.com] wrote:
| > I agree with all the points you and Pavel you talked about but I don't
| > feel comfortable to have the current process to switch the pid namespace
| > because of the process tree hierarchy (what will be the parent of the
| > process when you enter the pid namespace for example).
|
| The answer is - the one, that used to be. I see no problems with it.
| Do you?
Just to be clear, when a process unshares its pid namespace, it takes
on additional pid nr (== 1) in the new namespace but retains its original
pid nr(s) in the parent (ancestor) namespaces right ?
i.e the process becomes the container-init of the new namespace. When it
exits, all its children belonging to the new namespace are killed too,
but any children in the parent namespace (i.e children created before
unshare()) are not killed.
After the unshare() the process will not be able to signal any children
it created before the unshare() (bc their active pid namespaces are
different)
Sukadev
^ permalink raw reply
* Re: [RFC][PATCH] ns: Syscalls for better namespace sharing control.
From: Eric W. Biederman @ 2010-03-02 21:45 UTC (permalink / raw)
To: Jan Engelhardt
Cc: Pavel Emelyanov, Daniel Lezcano, hadi, Patrick McHardy,
Linux Netdev List, containers, Netfilter Development Mailinglist,
Ben Greear, Serge Hallyn, Matt Helsley
In-Reply-To: <alpine.LSU.2.01.1003021613570.17303@obet.zrqbmnf.qr>
Jan Engelhardt <jengelh@medozas.de> writes:
> On Tuesday 2010-03-02 16:03, Pavel Emelyanov wrote:
>
>>> I agree with all the points you and Pavel you talked about but I don't
>>> feel comfortable to have the current process to switch the pid namespace
>>> because of the process tree hierarchy (what will be the parent of the
>>> process when you enter the pid namespace for example).
>>
>>The answer is - the one, that used to be. I see no problems with it.
>>Do you?
>
> But perhaps it could be named "namespacefd" instead of nsfd, to reduce
> potential clashes (because glibc will usually just use the same name
> when making the syscall available as a C function).
Maybe. namespacefd seems like a real mouthful. I agree nsfd might be
a bit non-obvious for a rarish syscall.
Eric
^ permalink raw reply
* Re: 2.6.33 problems
From: Andrew Morton @ 2010-03-02 21:50 UTC (permalink / raw)
To: werner; +Cc: linux-kernel, Tejun Heo, netdev
In-Reply-To: <1267290551.13148@guyane.dyn-o-saur.com>
On Sat, 27 Feb 2010 14:09:11 -0300 (GFT)
werner@guyane.dyn-o-saur.com wrote:
> For better error searching / correction, I add below the whole syslog.
>
> This is refered to 2.6.33 published (without patchs)
>
> This are different errors.
>
> The most of them exists since 2.6.33-rc1 or appr. -rc5.
>
> I posted here already the whole syslog.bz2 but nobody toke care.
>
> the boot_vga error occurs only after I start the grafics mode. It's the mainboard's embedded nvidia grafics. I use the vesa framebuffer driver of X 1.8
>
> the int6_init error occurs also in text mode. I think it have something to do with internet.
>
> Also the printk errors occur only when start the computer in the grafics mode, but not when starting it in the text mode.
>
> Below is also the kernel config. Its the same like since -rc7 (but the errors are also the same)
>
>
Thanks.
>
> ...
>
> Feb 27 13:43:07 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> Feb 27 13:43:07 werner kernel: Pid: 5314, comm: modprobe Tainted: G C 2.6.33 #1
> Feb 27 13:43:07 werner kernel: Call Trace:
> Feb 27 13:43:07 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> Feb 27 13:43:07 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> Feb 27 13:43:07 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> Feb 27 13:43:07 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> Feb 27 13:43:07 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> Feb 27 13:43:07 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> Feb 27 13:43:07 werner kernel: [<fed105af>] ipv6_add_dev+0x191/0x30b [ipv6]
> Feb 27 13:43:07 werner kernel: [<fd269000>] ? inet6_init+0x0/0x2a2 [ipv6]
> Feb 27 13:43:07 werner kernel: [<fd2692ec>] addrconf_init+0x3b/0x11b [ipv6]
> Feb 27 13:43:07 werner kernel: [<fd269195>] inet6_init+0x195/0x2a2 [ipv6]
> Feb 27 13:43:07 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> Feb 27 13:43:07 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> Feb 27 13:43:07 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
> Feb 27 13:43:19 werner kdm_greet[5382]: Can't open default user face
> Feb 27 13:44:24 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> Feb 27 13:44:24 werner kernel: Pid: 5737, comm: modprobe Tainted: G C 2.6.33 #1
> Feb 27 13:44:24 werner kernel: Call Trace:
> Feb 27 13:44:24 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> Feb 27 13:44:24 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> Feb 27 13:44:24 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> Feb 27 13:44:24 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> Feb 27 13:44:24 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> Feb 27 13:44:24 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> Feb 27 13:44:24 werner kernel: [<fed105af>] ipv6_add_dev+0x191/0x30b [ipv6]
> Feb 27 13:44:24 werner kernel: [<f91bf000>] ? inet6_init+0x0/0x2a2 [ipv6]
> Feb 27 13:44:24 werner kernel: [<f91bf2ec>] addrconf_init+0x3b/0x11b [ipv6]
> Feb 27 13:44:24 werner kernel: [<f91bf195>] inet6_init+0x195/0x2a2 [ipv6]
> Feb 27 13:44:24 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> Feb 27 13:44:24 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> Feb 27 13:44:24 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
> Feb 27 13:44:27 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> Feb 27 13:44:27 werner kernel: Pid: 5772, comm: modprobe Tainted: G C 2.6.33 #1
> Feb 27 13:44:27 werner kernel: Call Trace:
> Feb 27 13:44:27 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> Feb 27 13:44:27 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> Feb 27 13:44:27 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> Feb 27 13:44:27 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> Feb 27 13:44:27 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> Feb 27 13:44:27 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> Feb 27 13:44:27 werner kernel: [<fee155af>] ipv6_add_dev+0x191/0x30b [ipv6]
> Feb 27 13:44:27 werner kernel: [<f9185000>] ? inet6_init+0x0/0x2a2 [ipv6]
> Feb 27 13:44:27 werner kernel: [<f91852ec>] addrconf_init+0x3b/0x11b [ipv6]
> Feb 27 13:44:27 werner kernel: [<f9185195>] inet6_init+0x195/0x2a2 [ipv6]
> Feb 27 13:44:27 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> Feb 27 13:44:27 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> Feb 27 13:44:27 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
Methinks Tejun's stuff broke, and then triggered a bug in a
hitherto-untested networking error recovery codepath.
> Feb 27 13:44:27 werner kernel: BUG: unable to handle kernel paging request at fed348d4
> Feb 27 13:44:27 werner kernel: IP: [<c1b68700>] unregister_pernet_operations+0x21/0x93
> Feb 27 13:44:27 werner kernel: *pde = 360fc067 *pte = 00000000
> Feb 27 13:44:27 werner kernel: Oops: 0002 [#1] PREEMPT SMP
> Feb 27 13:44:27 werner kernel: last sysfs file: /sys/devices/pci0000:00/0000:00:0d.0/boot_vga
> Feb 27 13:44:27 werner kernel: Modules linked in: ipv6(+) bnep rfcomm hidp l2cap bluetooth snd_usb_audio snd_usb_lib snd_rawmidi snd_seq_device rt2860sta(C) uvcvideo usbvideo lp snd_hda_codec_analog rtc_cmos rtc_core rtc_lib rtl8187 tpm_tis tpm tpm_bios mac80211 led_class snd_hda_intel snd_hda_codec k8temp hwmon 8139too cfg80211 snd_hwdep snd_pcm rfkill snd_timer snd soundcore snd_page_alloc forcedeth i2c_nforce2
> Feb 27 13:44:27 werner kernel:
> Feb 27 13:44:27 werner kernel: Pid: 5772, comm: modprobe Tainted: G C 2.6.33 #1 M2N-VM DH/System Product Name
> Feb 27 13:44:27 werner kernel: EIP: 0060:[<c1b68700>] EFLAGS: 00010246 CPU: 0
> Feb 27 13:44:27 werner kernel: EIP is at unregister_pernet_operations+0x21/0x93
> Feb 27 13:44:27 werner kernel: EAX: fed348d4 EBX: fee39850 ECX: f0d7bf58 EDX: fee398d4
> Feb 27 13:44:27 werner kernel: ESI: fee39850 EDI: f9185000 EBP: f0d7bf6c ESP: f0d7bf58
> Feb 27 13:44:27 werner kernel: DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
> Feb 27 13:44:27 werner kernel: Process modprobe (pid: 5772, ti=f0d7a000 task=f0ccb660 task.ti=f0d7a000)
> Feb 27 13:44:27 werner kernel: Stack:
> Feb 27 13:44:27 werner kernel: f0d7bf58 f0d7bf58 fee39850 00000000 f9185000 f0d7bf78 c1b687c6 fffffff4
> Feb 27 13:44:27 werner kernel: <0> f0d7bf84 f9185257 fee3b834 f0d7bf9c c1001143 00000000 fee3b834 00000000
> Feb 27 13:44:27 werner kernel: <0> 0805e188 f0d7bfac c1066570 b7516008 0805e138 f0d7a000 c1c6acec b7516008
> Feb 27 13:44:27 werner kernel: Call Trace:
> Feb 27 13:44:27 werner kernel: [<f9185000>] ? inet6_init+0x0/0x2a2 [ipv6]
> Feb 27 13:44:27 werner kernel: [<c1b687c6>] ? unregister_pernet_subsys+0x1c/0x29
> Feb 27 13:44:27 werner kernel: [<f9185257>] ? inet6_init+0x257/0x2a2 [ipv6]
> Feb 27 13:44:27 werner kernel: [<c1001143>] ? do_one_initcall+0x51/0x13f
> Feb 27 13:44:27 werner kernel: [<c1066570>] ? sys_init_module+0xac/0x1e0
> Feb 27 13:44:27 werner kernel: [<c1c6acec>] ? syscall_call+0x7/0xb
> Feb 27 13:44:27 werner kernel: Code: c2 75 e5 e8 78 0a 51 ff eb 97 55 89 e5 57 56 53 83 ec 08 e8 03 b2 49 ff 89 c6 8d 4d ec 89 4d ec 89 4d f0 8b 10 8b 40 04 89 42 04 <89> 10 c7 06 00 01 10 00 c7 46 04 00 02 20 00 a1 b8 b7 10 c2 8d
> Feb 27 13:44:27 werner kernel: EIP: [<c1b68700>] unregister_pernet_operations+0x21/0x93 SS:ESP 0068:f0d7bf58
> Feb 27 13:44:27 werner kernel: CR2: 00000000fed348d4
> Feb 27 13:44:27 werner kernel: ---[ end trace 120df121853896c9 ]---
> Feb 27 13:45:39 werner kernel: BUG: Bad page map in process kio_http pte:fee39850 pmd:ace05067
> Feb 27 13:45:39 werner kernel: addr:b6635000 vm_flags:08000075 anon_vma:(null) mapping:f20f434c index:5d
> Feb 27 13:45:39 werner kernel: vma->vm_ops->fault: filemap_fault+0x0/0x2eb
> Feb 27 13:45:39 werner kernel: vma->vm_file->f_op->mmap: generic_file_mmap+0x0/0x44
> Feb 27 13:45:39 werner kernel: Pid: 5764, comm: kio_http Tainted: G D C 2.6.33 #1
> Feb 27 13:45:39 werner kernel: Call Trace:
> Feb 27 13:45:39 werner kernel: [<c10a47c7>] print_bad_pte+0x17e/0x190
> Feb 27 13:45:39 werner kernel: [<c10a5782>] unmap_vmas+0x444/0x676
> Feb 27 13:45:39 werner kernel: [<c10a461d>] ? __do_fault+0x3bd/0x3e9
> Feb 27 13:45:39 werner kernel: [<c10988b9>] ? ____pagevec_lru_add+0x101/0x10f
> Feb 27 13:45:39 werner kernel: [<c10a9344>] exit_mmap+0xab/0x13a
> Feb 27 13:45:39 werner kernel: [<c1038cc1>] mmput+0x3a/0xb0
> Feb 27 13:45:39 werner kernel: [<c103c669>] exit_mm+0xec/0xf4
> Feb 27 13:45:39 werner kernel: [<c1c6a525>] ? _raw_spin_lock_irq+0xb/0x34
> Feb 27 13:45:39 werner kernel: [<c1c6a855>] ? _raw_spin_unlock_irq+0x8/0x27
> Feb 27 13:45:39 werner kernel: [<c103dbc1>] do_exit+0x1ad/0x605
> Feb 27 13:45:39 werner kernel: [<c105554c>] ? up_read+0x8/0x18
> Feb 27 13:45:39 werner kernel: [<c103e080>] do_group_exit+0x67/0x8a
> Feb 27 13:45:39 werner kernel: [<c103e0bb>] sys_exit_group+0x18/0x1c
> Feb 27 13:45:39 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
>
^ permalink raw reply
* Re: BNX2: Kernel crashes with 2.6.31 and 2.6.31.9
From: Michael Chan @ 2010-03-02 22:12 UTC (permalink / raw)
To: Bruno Prémont; +Cc: Benjamin Li, NetDEV, Linux-Kernel
In-Reply-To: <20100302092020.52cfcd0e@pluto.restena.lu>
On Tue, 2010-03-02 at 00:20 -0800, Bruno Prémont wrote:
> [ 3405.422963] ------------[ cut here ]------------
> [ 3405.428958] WARNING: at /usr/src/linux-2.6.33-rc8-git7/kernel/softirq.c:143 local_bh_enable_ip+0x72/0xa0()
In normal NAPI mode, we are in softirq context and we correctly use
spin_lock_bh() and spin_unlock_bh() here. In netpoll mode, IRQs are
disabled and so we get a warning from spin_unlock_bh().
> [ 3405.431858] Hardware name: ProLiant DL360 G5
> [ 3405.431858] Modules linked in: netbomb bnx2 ipmi_devintf loop dm_mod sg sr_mod cdrom ata_piix ahci ipmi_si ipmi_msghandler uhci_hcd qla2xxx libata hpwdt ehci_hcd [last unloaded: bnx2]
> [ 3405.431858] Pid: 25763, comm: cat Not tainted 2.6.33-rc8-git7-x86_64 #1
> [ 3405.431858] Call Trace:
> [ 3405.431858] [<ffffffff8103f002>] ? local_bh_enable_ip+0x72/0xa0
> [ 3405.431858] [<ffffffff81039368>] warn_slowpath_common+0x78/0xd0
> [ 3405.431858] [<ffffffff810393cf>] warn_slowpath_null+0xf/0x20
> [ 3405.431858] [<ffffffff8103f002>] local_bh_enable_ip+0x72/0xa0
> [ 3405.431858] [<ffffffff814002af>] _raw_spin_unlock_bh+0xf/0x20
> [ 3405.431858] [<ffffffffa0108ed4>] bnx2_reg_rd_ind+0x44/0x60 [bnx2]
> [ 3405.431858] [<ffffffffa0108eff>] bnx2_shmem_rd+0xf/0x20 [bnx2]
> [ 3405.431858] [<ffffffffa0113464>] bnx2_poll+0x194/0x228 [bnx2]
> [ 3405.431858] [<ffffffff8135c081>] netpoll_poll+0xe1/0x3c0
> [ 3405.431858] [<ffffffff8135c518>] netpoll_send_skb+0x118/0x210
> [ 3405.431858] [<ffffffff8135c80b>] netpoll_send_udp+0x1fb/0x210
> [ 3405.431858] [<ffffffffa00131c5>] write_msg+0x95/0xd0 [netbomb]
> [ 3405.431858] [<ffffffffa0013255>] netbomb_write+0x55/0xa4 [netbomb]
> [ 3405.431858] [<ffffffff810f6571>] proc_reg_write+0x71/0xb0
> [ 3405.431858] [<ffffffff810ab6db>] vfs_write+0xcb/0x180
> [ 3405.431858] [<ffffffff810ab880>] sys_write+0x50/0x90
> [ 3405.431858] [<ffffffff8102a1a4>] sysenter_dispatch+0x7/0x2b
> [ 3405.431858] ---[ end trace b4ac1510884bf2bc ]---
> [ 3411.050005] ------------[ cut here ]------------
> [ 3411.054851] WARNING: at /usr/src/linux-2.6.33-rc8-git7/net/sched/sch_generic.c:255 dev_watchdog+0x25e/0x270()
> [ 3411.059546] Hardware name: ProLiant DL360 G5
> [ 3411.061569] NETDEV WATCHDOG: eth0 (bnx2): transmit queue 0 timed out
> [ 3411.064582] Modules linked in: netbomb bnx2 ipmi_devintf loop dm_mod sg sr_mod cdrom ata_piix ahci ipmi_si ipmi_msghandler uhci_hcd qla2xxx libata hpwdt ehci_hcd [last unloaded: bnx2]
> [ 3411.064597] Pid: 0, comm: swapper Tainted: G W 2.6.33-rc8-git7-x86_64 #1
> [ 3411.064599] Call Trace:
Do we have timers running in this environment? The timer in the bnx2
driver, bnx2_timer(), needs to run to provide a heart beat to the
firmware. In netpoll mode without timer interrupts, if we are regularly
calling the NAPI poll function, it should also be able to provide the
heartbeat. Without the heartbeat, the firmware will reset the chip and
result in the NETDEV WATCHDOG.
> [ 3411.064601] <IRQ> [<ffffffff8135f84e>] ? dev_watchdog+0x25e/0x270
> [ 3411.064609] [<ffffffff81039368>] warn_slowpath_common+0x78/0xd0
> [ 3411.064612] [<ffffffff81039444>] warn_slowpath_fmt+0x64/0x70
> [ 3411.064616] [<ffffffff8103486d>] ? default_wake_function+0xd/0x10
> [ 3411.064620] [<ffffffff8119f339>] ? strlcpy+0x49/0x60
> [ 3411.064623] [<ffffffff81349b33>] ? netdev_drivername+0x43/0x50
> [ 3411.064626] [<ffffffff8135f84e>] dev_watchdog+0x25e/0x270
> [ 3411.064630] [<ffffffff8104c000>] ? delayed_work_timer_fn+0x0/0x40
> [ 3411.064633] [<ffffffff8104bf87>] ? __queue_work+0x77/0x90
> [ 3411.064636] [<ffffffff8103558b>] ? scheduler_tick+0x1bb/0x290
> [ 3411.064639] [<ffffffff8135f5f0>] ? dev_watchdog+0x0/0x270
> [ 3411.064642] [<ffffffff810440fc>] run_timer_softirq+0x13c/0x210
> [ 3411.064645] [<ffffffff8105b4b7>] ? clockevents_program_event+0x57/0xa0
> [ 3411.064649] [<ffffffff8103edb6>] __do_softirq+0xa6/0x130
> [ 3411.064652] [<ffffffff81003bcc>] call_softirq+0x1c/0x30
> [ 3411.064655] [<ffffffff81005be5>] do_softirq+0x55/0x90
> [ 3411.064658] [<ffffffff8103eb35>] irq_exit+0x75/0x90
> [ 3411.064661] [<ffffffff8101aeed>] smp_apic_timer_interrupt+0x6d/0xa0
> [ 3411.064664] [<ffffffff81003693>] apic_timer_interrupt+0x13/0x20
> [ 3411.064666] <EOI> [<ffffffff8100b186>] ? mwait_idle+0x66/0x80
> [ 3411.064670] [<ffffffff81001f90>] ? enter_idle+0x20/0x30
> [ 3411.064673] [<ffffffff81002003>] cpu_idle+0x63/0xb0
> [ 3411.064676] [<ffffffff813f2f14>] rest_init+0x74/0x80
> [ 3411.064680] [<ffffffff81880c15>] start_kernel+0x2f8/0x336
> [ 3411.064683] [<ffffffff8188026d>] x86_64_start_reservations+0x7d/0x84
> [ 3411.064686] [<ffffffff81880354>] x86_64_start_kernel+0xe0/0xf2
> [ 3411.064688] ---[ end trace b4ac1510884bf2bd ]---
^ permalink raw reply
* Re: [RFC][PATCH] ns: Syscalls for better namespace sharing control.
From: Eric W. Biederman @ 2010-03-02 22:13 UTC (permalink / raw)
To: Sukadev Bhattiprolu
Cc: Pavel Emelyanov, Daniel Lezcano, Linux Netdev List, containers,
Netfilter Development Mailinglist, Ben Greear
In-Reply-To: <20100302211942.GA17816@us.ibm.com>
Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> writes:
> Pavel Emelyanov [xemul@parallels.com] wrote:
> | > I agree with all the points you and Pavel you talked about but I don't
> | > feel comfortable to have the current process to switch the pid namespace
> | > because of the process tree hierarchy (what will be the parent of the
> | > process when you enter the pid namespace for example).
> |
> | The answer is - the one, that used to be. I see no problems with it.
> | Do you?
>
> Just to be clear, when a process unshares its pid namespace, it takes
> on additional pid nr (== 1) in the new namespace but retains its original
> pid nr(s) in the parent (ancestor) namespaces right ?
>
> i.e the process becomes the container-init of the new namespace. When it
> exits, all its children belonging to the new namespace are killed too,
> but any children in the parent namespace (i.e children created before
> unshare()) are not killed.
>
> After the unshare() the process will not be able to signal any children
> it created before the unshare() (bc their active pid namespaces are
> different)
The only case that I see as being simple and unsurprising worked a bit
differently:
We currently have:
ns_of_pid(task_pid(tsk))
tsk->nsproxy->pid_ns
I would reduce the usage of tsk->nsproxy->pid_ns as much as possible,
and use ns_of_pid(task_pid(tsk)) for all of the routine things that
need to know the pid namespace of a process. Possibly even to the point
or reversing the order of the upid array so using it is more efficient.
I would leave tsk->nsproxy->pid_ns for use by fork/clone when allocating
a childs pid number.
The unsharing process would have to become the child reaper. I think the first
child would become pid 1 in that pid namespace.
>From an implementation point of view who gets pid 1 when the child_reaper is
not visible inside the pid namespace doesn't make much difference but we would
want to carefully look at the details so we minimize userspace confusion.
I don't think a process tree rooted at pid 0 is a show stopper. It is
somewhat confusing but we already have a forked process tree today,
and user space certainly hasn't fallen over. In the case of a join if you want
to live in properly in the process tree you can daemonize and become a child
of init.
I think replacing a struct pid for another struct pid allocated in
descendant pid_namespace (but has all of the same struct upid values
as the first struct pid) is a disastrous idea. It destroys the
uniqueness of struct pid and we have a lot of places where we check
that for equality of pid pointers, and that now would be broken.
Otherthings like proc directories also used a cached struct pid and
would start thinking the process was gone when it was not.
Eric
^ permalink raw reply
* Re: 2.6.33 problems
From: Stephen Hemminger @ 2010-03-02 22:42 UTC (permalink / raw)
To: Andrew Morton; +Cc: werner, linux-kernel, Tejun Heo, netdev
In-Reply-To: <20100302135032.e3d11bbf.akpm@linux-foundation.org>
On Tue, 2 Mar 2010 13:50:32 -0800
Andrew Morton <akpm@linux-foundation.org> wrote:
> On Sat, 27 Feb 2010 14:09:11 -0300 (GFT)
> werner@guyane.dyn-o-saur.com wrote:
>
> > For better error searching / correction, I add below the whole syslog.
> >
> > This is refered to 2.6.33 published (without patchs)
> >
> > This are different errors.
> >
> > The most of them exists since 2.6.33-rc1 or appr. -rc5.
> >
> > I posted here already the whole syslog.bz2 but nobody toke care.
> >
> > the boot_vga error occurs only after I start the grafics mode. It's the mainboard's embedded nvidia grafics. I use the vesa framebuffer driver of X 1.8
> >
> > the int6_init error occurs also in text mode. I think it have something to do with internet.
> >
> > Also the printk errors occur only when start the computer in the grafics mode, but not when starting it in the text mode.
> >
> > Below is also the kernel config. Its the same like since -rc7 (but the errors are also the same)
> >
> >
>
> Thanks.
>
> >
> > ...
> >
> > Feb 27 13:43:07 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> > Feb 27 13:43:07 werner kernel: Pid: 5314, comm: modprobe Tainted: G C 2.6.33 #1
> > Feb 27 13:43:07 werner kernel: Call Trace:
> > Feb 27 13:43:07 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> > Feb 27 13:43:07 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> > Feb 27 13:43:07 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> > Feb 27 13:43:07 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> > Feb 27 13:43:07 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> > Feb 27 13:43:07 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> > Feb 27 13:43:07 werner kernel: [<fed105af>] ipv6_add_dev+0x191/0x30b [ipv6]
> > Feb 27 13:43:07 werner kernel: [<fd269000>] ? inet6_init+0x0/0x2a2 [ipv6]
> > Feb 27 13:43:07 werner kernel: [<fd2692ec>] addrconf_init+0x3b/0x11b [ipv6]
> > Feb 27 13:43:07 werner kernel: [<fd269195>] inet6_init+0x195/0x2a2 [ipv6]
> > Feb 27 13:43:07 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> > Feb 27 13:43:07 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> > Feb 27 13:43:07 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
> > Feb 27 13:43:19 werner kdm_greet[5382]: Can't open default user face
> > Feb 27 13:44:24 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> > Feb 27 13:44:24 werner kernel: Pid: 5737, comm: modprobe Tainted: G C 2.6.33 #1
> > Feb 27 13:44:24 werner kernel: Call Trace:
> > Feb 27 13:44:24 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> > Feb 27 13:44:24 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> > Feb 27 13:44:24 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> > Feb 27 13:44:24 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> > Feb 27 13:44:24 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> > Feb 27 13:44:24 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> > Feb 27 13:44:24 werner kernel: [<fed105af>] ipv6_add_dev+0x191/0x30b [ipv6]
> > Feb 27 13:44:24 werner kernel: [<f91bf000>] ? inet6_init+0x0/0x2a2 [ipv6]
> > Feb 27 13:44:24 werner kernel: [<f91bf2ec>] addrconf_init+0x3b/0x11b [ipv6]
> > Feb 27 13:44:24 werner kernel: [<f91bf195>] inet6_init+0x195/0x2a2 [ipv6]
> > Feb 27 13:44:24 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> > Feb 27 13:44:24 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> > Feb 27 13:44:24 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
> > Feb 27 13:44:27 werner kernel: PERCPU: allocation failed, size=2048 align=8, failed to allocate new chunk
> > Feb 27 13:44:27 werner kernel: Pid: 5772, comm: modprobe Tainted: G C 2.6.33 #1
> > Feb 27 13:44:27 werner kernel: Call Trace:
> > Feb 27 13:44:27 werner kernel: [<c1c682ca>] ? printk+0x14/0x16
> > Feb 27 13:44:27 werner kernel: [<c10bdfa3>] pcpu_alloc+0x6ba/0x73b
> > Feb 27 13:44:27 werner kernel: [<c10bb0be>] ? get_slab+0x8/0x50
> > Feb 27 13:44:27 werner kernel: [<c1b7283d>] ? neigh_parms_alloc+0x55/0xd1
> > Feb 27 13:44:27 werner kernel: [<c10be047>] __alloc_percpu+0xf/0x14
> > Feb 27 13:44:27 werner kernel: [<c1bb26cb>] snmp_mib_init+0x22/0x5a
> > Feb 27 13:44:27 werner kernel: [<fee155af>] ipv6_add_dev+0x191/0x30b [ipv6]
> > Feb 27 13:44:27 werner kernel: [<f9185000>] ? inet6_init+0x0/0x2a2 [ipv6]
> > Feb 27 13:44:27 werner kernel: [<f91852ec>] addrconf_init+0x3b/0x11b [ipv6]
> > Feb 27 13:44:27 werner kernel: [<f9185195>] inet6_init+0x195/0x2a2 [ipv6]
> > Feb 27 13:44:27 werner kernel: [<c1001143>] do_one_initcall+0x51/0x13f
> > Feb 27 13:44:27 werner kernel: [<c1066570>] sys_init_module+0xac/0x1e0
> > Feb 27 13:44:27 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
>
> Methinks Tejun's stuff broke, and then triggered a bug in a
> hitherto-untested networking error recovery codepath.
>
>
> > Feb 27 13:44:27 werner kernel: BUG: unable to handle kernel paging request at fed348d4
> > Feb 27 13:44:27 werner kernel: IP: [<c1b68700>] unregister_pernet_operations+0x21/0x93
> > Feb 27 13:44:27 werner kernel: *pde = 360fc067 *pte = 00000000
> > Feb 27 13:44:27 werner kernel: Oops: 0002 [#1] PREEMPT SMP
> > Feb 27 13:44:27 werner kernel: last sysfs file: /sys/devices/pci0000:00/0000:00:0d.0/boot_vga
> > Feb 27 13:44:27 werner kernel: Modules linked in: ipv6(+) bnep rfcomm hidp l2cap bluetooth snd_usb_audio snd_usb_lib snd_rawmidi snd_seq_device rt2860sta(C) uvcvideo usbvideo lp snd_hda_codec_analog rtc_cmos rtc_core rtc_lib rtl8187 tpm_tis tpm tpm_bios mac80211 led_class snd_hda_intel snd_hda_codec k8temp hwmon 8139too cfg80211 snd_hwdep snd_pcm rfkill snd_timer snd soundcore snd_page_alloc forcedeth i2c_nforce2
> > Feb 27 13:44:27 werner kernel:
> > Feb 27 13:44:27 werner kernel: Pid: 5772, comm: modprobe Tainted: G C 2.6.33 #1 M2N-VM DH/System Product Name
> > Feb 27 13:44:27 werner kernel: EIP: 0060:[<c1b68700>] EFLAGS: 00010246 CPU: 0
> > Feb 27 13:44:27 werner kernel: EIP is at unregister_pernet_operations+0x21/0x93
> > Feb 27 13:44:27 werner kernel: EAX: fed348d4 EBX: fee39850 ECX: f0d7bf58 EDX: fee398d4
> > Feb 27 13:44:27 werner kernel: ESI: fee39850 EDI: f9185000 EBP: f0d7bf6c ESP: f0d7bf58
> > Feb 27 13:44:27 werner kernel: DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
> > Feb 27 13:44:27 werner kernel: Process modprobe (pid: 5772, ti=f0d7a000 task=f0ccb660 task.ti=f0d7a000)
> > Feb 27 13:44:27 werner kernel: Stack:
> > Feb 27 13:44:27 werner kernel: f0d7bf58 f0d7bf58 fee39850 00000000 f9185000 f0d7bf78 c1b687c6 fffffff4
> > Feb 27 13:44:27 werner kernel: <0> f0d7bf84 f9185257 fee3b834 f0d7bf9c c1001143 00000000 fee3b834 00000000
> > Feb 27 13:44:27 werner kernel: <0> 0805e188 f0d7bfac c1066570 b7516008 0805e138 f0d7a000 c1c6acec b7516008
> > Feb 27 13:44:27 werner kernel: Call Trace:
> > Feb 27 13:44:27 werner kernel: [<f9185000>] ? inet6_init+0x0/0x2a2 [ipv6]
> > Feb 27 13:44:27 werner kernel: [<c1b687c6>] ? unregister_pernet_subsys+0x1c/0x29
> > Feb 27 13:44:27 werner kernel: [<f9185257>] ? inet6_init+0x257/0x2a2 [ipv6]
> > Feb 27 13:44:27 werner kernel: [<c1001143>] ? do_one_initcall+0x51/0x13f
> > Feb 27 13:44:27 werner kernel: [<c1066570>] ? sys_init_module+0xac/0x1e0
> > Feb 27 13:44:27 werner kernel: [<c1c6acec>] ? syscall_call+0x7/0xb
> > Feb 27 13:44:27 werner kernel: Code: c2 75 e5 e8 78 0a 51 ff eb 97 55 89 e5 57 56 53 83 ec 08 e8 03 b2 49 ff 89 c6 8d 4d ec 89 4d ec 89 4d f0 8b 10 8b 40 04 89 42 04 <89> 10 c7 06 00 01 10 00 c7 46 04 00 02 20 00 a1 b8 b7 10 c2 8d
> > Feb 27 13:44:27 werner kernel: EIP: [<c1b68700>] unregister_pernet_operations+0x21/0x93 SS:ESP 0068:f0d7bf58
> > Feb 27 13:44:27 werner kernel: CR2: 00000000fed348d4
> > Feb 27 13:44:27 werner kernel: ---[ end trace 120df121853896c9 ]---
> > Feb 27 13:45:39 werner kernel: BUG: Bad page map in process kio_http pte:fee39850 pmd:ace05067
> > Feb 27 13:45:39 werner kernel: addr:b6635000 vm_flags:08000075 anon_vma:(null) mapping:f20f434c index:5d
> > Feb 27 13:45:39 werner kernel: vma->vm_ops->fault: filemap_fault+0x0/0x2eb
> > Feb 27 13:45:39 werner kernel: vma->vm_file->f_op->mmap: generic_file_mmap+0x0/0x44
> > Feb 27 13:45:39 werner kernel: Pid: 5764, comm: kio_http Tainted: G D C 2.6.33 #1
> > Feb 27 13:45:39 werner kernel: Call Trace:
> > Feb 27 13:45:39 werner kernel: [<c10a47c7>] print_bad_pte+0x17e/0x190
> > Feb 27 13:45:39 werner kernel: [<c10a5782>] unmap_vmas+0x444/0x676
> > Feb 27 13:45:39 werner kernel: [<c10a461d>] ? __do_fault+0x3bd/0x3e9
> > Feb 27 13:45:39 werner kernel: [<c10988b9>] ? ____pagevec_lru_add+0x101/0x10f
> > Feb 27 13:45:39 werner kernel: [<c10a9344>] exit_mmap+0xab/0x13a
> > Feb 27 13:45:39 werner kernel: [<c1038cc1>] mmput+0x3a/0xb0
> > Feb 27 13:45:39 werner kernel: [<c103c669>] exit_mm+0xec/0xf4
> > Feb 27 13:45:39 werner kernel: [<c1c6a525>] ? _raw_spin_lock_irq+0xb/0x34
> > Feb 27 13:45:39 werner kernel: [<c1c6a855>] ? _raw_spin_unlock_irq+0x8/0x27
> > Feb 27 13:45:39 werner kernel: [<c103dbc1>] do_exit+0x1ad/0x605
> > Feb 27 13:45:39 werner kernel: [<c105554c>] ? up_read+0x8/0x18
> > Feb 27 13:45:39 werner kernel: [<c103e080>] do_group_exit+0x67/0x8a
> > Feb 27 13:45:39 werner kernel: [<c103e0bb>] sys_exit_group+0x18/0x1c
> > Feb 27 13:45:39 werner kernel: [<c1c6acec>] syscall_call+0x7/0xb
The SNMP MIB stuff does allocate a disproportionately large amount of percpu
data. Looks like 2 tables per MIB.
^ permalink raw reply
* [PATCH 37/62] net: move am79c961's probe function to .devinit.text
From: Greg Kroah-Hartman @ 2010-03-02 23:28 UTC (permalink / raw)
To: linux-kernel
Cc: Uwe Kleine-König, Roel Kluin, Russell King, netdev,
Andrew Morton, Greg Kroah-Hartman
In-Reply-To: <20100302230933.GA32287@kroah.com>
From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
A pointer to am79c961_probe is passed to the core via
platform_driver_register and so the function must not disappear when the
.init sections are discarded. Otherwise (if also having HOTPLUG=y)
unbinding and binding a device to the driver via sysfs will result in an
oops as does a device being registered late.
An alternative to this patch is using platform_driver_probe instead of
platform_driver_register plus removing the pointer to the probe function
from the struct platform_driver.
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Roel Kluin <12o3l@tiscali.nl>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Cc: <netdev@vger.kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/net/arm/am79c961a.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
index 164b37e..1c3c1f9 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/arm/am79c961a.c
@@ -680,7 +680,7 @@ static const struct net_device_ops am79c961_netdev_ops = {
#endif
};
-static int __init am79c961_probe(struct platform_device *pdev)
+static int __devinit am79c961_probe(struct platform_device *pdev)
{
struct resource *res;
struct net_device *dev;
--
1.7.0.1
^ permalink raw reply related
* Re: 2.6.33 problems
From: Eric Dumazet @ 2010-03-02 23:30 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Andrew Morton, werner, linux-kernel, Tejun Heo, netdev
In-Reply-To: <20100302144219.3cd28ae9@nehalam>
Le mardi 02 mars 2010 à 14:42 -0800, Stephen Hemminger a écrit :
> The SNMP MIB stuff does allocate a disproportionately large amount of percpu
> data. Looks like 2 tables per MIB.
But did this percpu usage grew with 2.6.33 ?
In my understanding, ipv6 always wanted a lot of percpu data for its
MIBS (including per device MIBS), the big increase occurred with commit
96793b482540f3a26e2188eaf75cb56b7829d3e3
from David L Stevens, when RFC 4293 was implemented.
On IPV6, each net device needed 4096/8192 additional bytes of memory per
possible cpu.
An attempt to reduce SNMP mibs by 50% was attempted one ago, but not
completed. I'll try to find some time to resurrect the patch.
[RFC] percpu: convert SNMP mibs to new infra
(use one single table, for both user/bh contexts on x86)
http://lkml.org/lkml/2009/4/1/401
^ permalink raw reply
* [Bridge] [PATCH] bridge: per-cpu packet statistics (v3)
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David Miller; +Cc: Eric Dumazet, netdev, bridge
In-Reply-To: <1267553173.2839.9.camel@edumazet-laptop>
The shared packet statistics are a potential source of slow down
on bridged traffic. Convert to per-cpu array, but only keep those
statistics which change per-packet.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
Move tx and rx to be next to each other per Eric's suggestion
net/bridge/br_device.c | 43 ++++++++++++++++++++++++++++++++++++++-----
net/bridge/br_if.c | 6 ++++++
net/bridge/br_input.c | 5 +++--
net/bridge/br_private.h | 8 ++++++++
4 files changed, 55 insertions(+), 7 deletions(-)
--- a/net/bridge/br_device.c 2010-03-02 10:48:44.527817663 -0800
+++ b/net/bridge/br_device.c 2010-03-02 10:48:48.287817348 -0800
@@ -26,11 +26,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *
const unsigned char *dest = skb->data;
struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst;
+ struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
- BR_INPUT_SKB_CB(skb)->brdev = dev;
+ brstats->tx_packets++;
+ brstats->tx_bytes += skb->len;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
+ BR_INPUT_SKB_CB(skb)->brdev = dev;
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
@@ -81,6 +82,31 @@ static int br_dev_stop(struct net_device
return 0;
}
+static struct net_device_stats *br_get_stats(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct br_cpu_netstats sum = { 0 };
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct br_cpu_netstats *bstats
+ = per_cpu_ptr(br->stats, cpu);
+
+ sum.tx_bytes += bstats->tx_bytes;
+ sum.tx_packets += bstats->tx_packets;
+ sum.rx_bytes += bstats->rx_bytes;
+ sum.rx_packets += bstats->rx_packets;
+ }
+
+ stats->tx_bytes = sum.tx_bytes;
+ stats->tx_packets = sum.tx_packets;
+ stats->rx_bytes = sum.rx_bytes;
+ stats->rx_packets = sum.rx_packets;
+
+ return stats;
+}
+
static int br_change_mtu(struct net_device *dev, int new_mtu)
{
struct net_bridge *br = netdev_priv(dev);
@@ -180,19 +206,28 @@ static const struct net_device_ops br_ne
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
.ndo_start_xmit = br_dev_xmit,
+ .ndo_get_stats = br_get_stats,
.ndo_set_mac_address = br_set_mac_address,
.ndo_set_multicast_list = br_dev_set_multicast_list,
.ndo_change_mtu = br_change_mtu,
.ndo_do_ioctl = br_dev_ioctl,
};
+static void br_dev_free(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ free_percpu(br->stats);
+ free_netdev(dev);
+}
+
void br_dev_setup(struct net_device *dev)
{
random_ether_addr(dev->dev_addr);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
- dev->destructor = free_netdev;
+ dev->destructor = br_dev_free;
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
dev->tx_queue_len = 0;
dev->priv_flags = IFF_EBRIDGE;
--- a/net/bridge/br_if.c 2010-03-02 10:48:44.507817575 -0800
+++ b/net/bridge/br_if.c 2010-03-02 10:48:48.287817348 -0800
@@ -185,6 +185,12 @@ static struct net_device *new_bridge_dev
br = netdev_priv(dev);
br->dev = dev;
+ br->stats = alloc_percpu(struct br_cpu_netstats);
+ if (!br->stats) {
+ free_netdev(dev);
+ return NULL;
+ }
+
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
spin_lock_init(&br->hash_lock);
--- a/net/bridge/br_input.c 2010-03-02 10:48:44.515818544 -0800
+++ b/net/bridge/br_input.c 2010-03-02 10:48:48.287817348 -0800
@@ -23,9 +23,11 @@ const u8 br_group_address[ETH_ALEN] = {
static int br_pass_frame_up(struct sk_buff *skb)
{
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
+ struct net_bridge *br = netdev_priv(brdev);
+ struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
- brdev->stats.rx_packets++;
- brdev->stats.rx_bytes += skb->len;
+ brstats->rx_packets++;
+ brstats->rx_bytes += skb->len;
indev = skb->dev;
skb->dev = brdev;
--- a/net/bridge/br_private.h 2010-03-02 10:48:44.503817627 -0800
+++ b/net/bridge/br_private.h 2010-03-02 10:49:10.632566819 -0800
@@ -135,6 +135,14 @@ struct net_bridge
spinlock_t lock;
struct list_head port_list;
struct net_device *dev;
+
+ struct br_cpu_netstats __percpu {
+ unsigned long rx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_packets;
+ unsigned long tx_bytes;
+ } *stats;
+
spinlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
unsigned long feature_mask;
^ permalink raw reply
* [PATCH 00/12] IPv6 addrconf changes
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
This set includes a mixed bag of changes all related to
IPv6 address configuration: bugfixes (1-3,8), changes to use
list interface (4-5,8), RCU (6), cosmetic cleanups (9-10,12) and
minor improvments (7,11).
--
^ permalink raw reply
* [PATCH 01/12] IPv6: addrconf dad timer unnecessary bh_disable
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: addrconf-dad-bh.patch --]
[-- Type: text/plain, Size: 1296 bytes --]
Timer code runs in bottom half, so there is no need for
using _bh form of locking. Also check if device is not ready
to avoid race with address that is no longer active.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/net/ipv6/addrconf.c 2010-02-26 20:00:39.967484383 -0800
+++ b/net/ipv6/addrconf.c 2010-02-27 08:29:44.544108734 -0800
@@ -2850,9 +2850,9 @@ static void addrconf_dad_timer(unsigned
struct inet6_dev *idev = ifp->idev;
struct in6_addr mcaddr;
- read_lock_bh(&idev->lock);
- if (idev->dead) {
- read_unlock_bh(&idev->lock);
+ read_lock(&idev->lock);
+ if (idev->dead || !(idev->if_flags & IF_READY)) {
+ read_unlock(&idev->lock);
goto out;
}
@@ -2864,7 +2864,7 @@ static void addrconf_dad_timer(unsigned
ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
spin_unlock(&ifp->lock);
- read_unlock_bh(&idev->lock);
+ read_unlock(&idev->lock);
addrconf_dad_completed(ifp);
@@ -2874,7 +2874,7 @@ static void addrconf_dad_timer(unsigned
ifp->probes--;
addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
spin_unlock(&ifp->lock);
- read_unlock_bh(&idev->lock);
+ read_unlock(&idev->lock);
/* send a neighbour solicitation for our addr */
addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
--
^ permalink raw reply
* [PATCH 02/12] IPv6: addrconf timer race
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: addrconf-rstimer.patch --]
[-- Type: text/plain, Size: 1949 bytes --]
The Router Solicitation timer races with device state changes
because it doesn't lock the device. Use local variable to avoid
one repeated dereference.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/net/ipv6/addrconf.c 2010-02-26 20:00:43.703982448 -0800
+++ b/net/ipv6/addrconf.c 2010-02-27 08:29:41.727984521 -0800
@@ -2739,28 +2739,29 @@ static int addrconf_ifdown(struct net_de
static void addrconf_rs_timer(unsigned long data)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+ struct inet6_dev *idev = ifp->idev;
- if (ifp->idev->cnf.forwarding)
+ read_lock(&idev->lock);
+ if (idev->dead || !(idev->if_flags & IF_READY))
goto out;
- if (ifp->idev->if_flags & IF_RA_RCVD) {
- /*
- * Announcement received after solicitation
- * was sent
- */
+ if (idev->cnf.forwarding)
+ goto out;
+
+ /* Announcement received after solicitation was sent */
+ if (idev->if_flags & IF_RA_RCVD)
goto out;
- }
spin_lock(&ifp->lock);
- if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) {
+ if (ifp->probes++ < idev->cnf.rtr_solicits) {
/* The wait after the last probe can be shorter */
addrconf_mod_timer(ifp, AC_RS,
- (ifp->probes == ifp->idev->cnf.rtr_solicits) ?
- ifp->idev->cnf.rtr_solicit_delay :
- ifp->idev->cnf.rtr_solicit_interval);
+ (ifp->probes == idev->cnf.rtr_solicits) ?
+ idev->cnf.rtr_solicit_delay :
+ idev->cnf.rtr_solicit_interval);
spin_unlock(&ifp->lock);
- ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+ ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
} else {
spin_unlock(&ifp->lock);
/*
@@ -2768,10 +2769,11 @@ static void addrconf_rs_timer(unsigned l
* assumption any longer.
*/
printk(KERN_DEBUG "%s: no IPv6 routers present\n",
- ifp->idev->dev->name);
+ idev->dev->name);
}
out:
+ read_unlock(&idev->lock);
in6_ifa_put(ifp);
}
--
^ permalink raw reply
* [PATCH 03/12] IPv6: addrconf notify when address is unavailable
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: addrconf-anycast-notify.patch --]
[-- Type: text/plain, Size: 3038 bytes --]
My recent change in net-next to retain permanent addresses caused regression.
Device refcount would not go to zero when device was unregistered because
left over anycast reference would hold ipv6 dev reference which would hold
device references...
The correct procedure is to call notify chain when address is no longer
available for use. When interface comes back DAD timer will notify
back that address is available.
Also, link local addresses should be purged when interface is brought
down. The address might be changed.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
net/ipv6/addrconf.c | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
--- a/net/ipv6/addrconf.c 2010-02-27 08:56:23.955450341 -0800
+++ b/net/ipv6/addrconf.c 2010-02-27 08:57:02.271199959 -0800
@@ -2649,11 +2649,11 @@ static int addrconf_ifdown(struct net_de
write_lock_bh(&addrconf_hash_lock);
while ((ifa = *bifa) != NULL) {
if (ifa->idev == idev &&
- (how || !(ifa->flags&IFA_F_PERMANENT))) {
+ (how || !(ifa->flags&IFA_F_PERMANENT) ||
+ ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
*bifa = ifa->lst_next;
ifa->lst_next = NULL;
- addrconf_del_timer(ifa);
- in6_ifa_put(ifa);
+ __in6_ifa_put(ifa);
continue;
}
bifa = &ifa->lst_next;
@@ -2691,28 +2691,40 @@ static int addrconf_ifdown(struct net_de
#endif
bifa = &idev->addr_list;
while ((ifa = *bifa) != NULL) {
- if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) {
- /* Retain permanent address on admin down */
+ addrconf_del_timer(ifa);
+
+ /* If just doing link down, and address is permanent
+ and not link-local, then retain it. */
+ if (how == 0 &&
+ (ifa->flags&IFA_F_PERMANENT) &&
+ !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
bifa = &ifa->if_next;
- /* Restart DAD if needed when link comes back up */
- if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
- idev->cnf.accept_dad <= 0 ||
- (ifa->flags & IFA_F_NODAD)))
- ifa->flags |= IFA_F_TENTATIVE;
+ /* If not doing DAD on this address, just keep it. */
+ if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
+ idev->cnf.accept_dad <= 0 ||
+ (ifa->flags & IFA_F_NODAD))
+ continue;
+
+ /* If it was tentative already, no need to notify */
+ if (ifa->flags & IFA_F_TENTATIVE)
+ continue;
+
+ /* Flag it for later restoration when link comes up */
+ ifa->flags |= IFA_F_TENTATIVE;
+ in6_ifa_hold(ifa);
} else {
*bifa = ifa->if_next;
ifa->if_next = NULL;
-
ifa->dead = 1;
- write_unlock_bh(&idev->lock);
+ }
+ write_unlock_bh(&idev->lock);
- __ipv6_ifa_notify(RTM_DELADDR, ifa);
- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
- in6_ifa_put(ifa);
+ __ipv6_ifa_notify(RTM_DELADDR, ifa);
+ atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
+ in6_ifa_put(ifa);
- write_lock_bh(&idev->lock);
- }
+ write_lock_bh(&idev->lock);
}
write_unlock_bh(&idev->lock);
--
^ permalink raw reply
* [PATCH 04/12] ipv6: convert temporary address list to list macros
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: ipv6-tmp-addrlist.patch --]
[-- Type: text/plain, Size: 3069 bytes --]
Use list macros instead of open coded linked list.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
include/net/if_inet6.h | 4 ++--
net/ipv6/addrconf.c | 30 ++++++++++++------------------
2 files changed, 14 insertions(+), 20 deletions(-)
--- a/include/net/if_inet6.h 2010-02-26 17:50:50.719484571 -0800
+++ b/include/net/if_inet6.h 2010-02-27 08:29:38.872232789 -0800
@@ -58,7 +58,7 @@ struct inet6_ifaddr {
struct inet6_ifaddr *if_next; /* next addr in inet6_dev */
#ifdef CONFIG_IPV6_PRIVACY
- struct inet6_ifaddr *tmp_next; /* next addr in tempaddr_lst */
+ struct list_head tmp_list;
struct inet6_ifaddr *ifpub;
int regen_count;
#endif
@@ -175,7 +175,7 @@ struct inet6_dev {
#ifdef CONFIG_IPV6_PRIVACY
u8 rndid[8];
struct timer_list regen_timer;
- struct inet6_ifaddr *tempaddr_list;
+ struct list_head tempaddr_list;
#endif
struct neigh_parms *nd_parms;
--- a/net/ipv6/addrconf.c 2010-02-26 20:00:45.207484349 -0800
+++ b/net/ipv6/addrconf.c 2010-02-27 08:29:38.856233475 -0800
@@ -401,6 +401,7 @@ static struct inet6_dev * ipv6_add_dev(s
#endif
#ifdef CONFIG_IPV6_PRIVACY
+ INIT_LIST_HEAD(&ndev->tempaddr_list);
setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
if ((dev->flags&IFF_LOOPBACK) ||
dev->type == ARPHRD_TUNNEL ||
@@ -679,8 +680,7 @@ ipv6_add_addr(struct inet6_dev *idev, co
#ifdef CONFIG_IPV6_PRIVACY
if (ifa->flags&IFA_F_TEMPORARY) {
- ifa->tmp_next = idev->tempaddr_list;
- idev->tempaddr_list = ifa;
+ list_add(&ifa->tmp_list, &idev->tempaddr_list);
in6_ifa_hold(ifa);
}
#endif
@@ -732,19 +732,12 @@ static void ipv6_del_addr(struct inet6_i
write_lock_bh(&idev->lock);
#ifdef CONFIG_IPV6_PRIVACY
if (ifp->flags&IFA_F_TEMPORARY) {
- for (ifap = &idev->tempaddr_list; (ifa=*ifap) != NULL;
- ifap = &ifa->tmp_next) {
- if (ifa == ifp) {
- *ifap = ifa->tmp_next;
- if (ifp->ifpub) {
- in6_ifa_put(ifp->ifpub);
- ifp->ifpub = NULL;
- }
- __in6_ifa_put(ifp);
- ifa->tmp_next = NULL;
- break;
- }
+ list_del(&ifp->tmp_list);
+ if (ifp->ifpub) {
+ in6_ifa_put(ifp->ifpub);
+ ifp->ifpub = NULL;
}
+ __in6_ifa_put(ifp);
}
#endif
@@ -1968,7 +1961,7 @@ ok:
#ifdef CONFIG_IPV6_PRIVACY
read_lock_bh(&in6_dev->lock);
/* update all temporary addresses in the list */
- for (ift=in6_dev->tempaddr_list; ift; ift=ift->tmp_next) {
+ list_for_each_entry(ift, &in6_dev->tempaddr_list, tmp_list) {
/*
* When adjusting the lifetimes of an existing
* temporary address, only lower the lifetimes.
@@ -2673,9 +2666,10 @@ static int addrconf_ifdown(struct net_de
in6_dev_put(idev);
/* clear tempaddr list */
- while ((ifa = idev->tempaddr_list) != NULL) {
- idev->tempaddr_list = ifa->tmp_next;
- ifa->tmp_next = NULL;
+ while (!list_empty(&idev->tempaddr_list)) {
+ ifa = list_first_entry(&idev->tempaddr_list,
+ struct inet6_ifaddr, tmp_list);
+ list_del(&ifa->tmp_list);
ifa->dead = 1;
write_unlock_bh(&idev->lock);
spin_lock_bh(&ifa->lock);
--
^ permalink raw reply
* [PATCH 05/12] ipv6: convert addrconf list to hlist
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: ipv6-addrconf-hlist.patch --]
[-- Type: text/plain, Size: 9363 bytes --]
Using hash list macros, simplifies code and helps later RCU.
This patch includes some initialization that is not strictly necessary,
since an empty hlist node/list is all zero; and list is in BSS
and node is allocated with kzalloc.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
include/net/if_inet6.h | 2
net/ipv6/addrconf.c | 128 ++++++++++++++++++++-----------------------------
2 files changed, 54 insertions(+), 76 deletions(-)
--- a/net/ipv6/addrconf.c 2010-03-02 14:20:10.473202008 -0800
+++ b/net/ipv6/addrconf.c 2010-03-02 14:25:23.173252540 -0800
@@ -126,7 +126,7 @@ static int ipv6_count_addresses(struct i
/*
* Configured unicast address hash table
*/
-static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
+static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
static DEFINE_RWLOCK(addrconf_hash_lock);
static void addrconf_verify(unsigned long);
@@ -528,7 +528,7 @@ static int addrconf_fixup_forwarding(str
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
{
WARN_ON(ifp->if_next != NULL);
- WARN_ON(ifp->lst_next != NULL);
+ WARN_ON(!hlist_unhashed(&ifp->addr_lst));
#ifdef NET_REFCNT_DEBUG
printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
@@ -643,6 +643,7 @@ ipv6_add_addr(struct inet6_dev *idev, co
spin_lock_init(&ifa->lock);
init_timer(&ifa->timer);
+ INIT_HLIST_NODE(&ifa->addr_lst);
ifa->timer.data = (unsigned long) ifa;
ifa->scope = scope;
ifa->prefix_len = pfxlen;
@@ -669,8 +670,7 @@ ipv6_add_addr(struct inet6_dev *idev, co
/* Add to big hash table */
hash = ipv6_addr_hash(addr);
- ifa->lst_next = inet6_addr_lst[hash];
- inet6_addr_lst[hash] = ifa;
+ hlist_add_head(&ifa->addr_lst, &inet6_addr_lst[hash]);
in6_ifa_hold(ifa);
write_unlock(&addrconf_hash_lock);
@@ -718,15 +718,8 @@ static void ipv6_del_addr(struct inet6_i
ifp->dead = 1;
write_lock_bh(&addrconf_hash_lock);
- for (ifap = &inet6_addr_lst[hash]; (ifa=*ifap) != NULL;
- ifap = &ifa->lst_next) {
- if (ifa == ifp) {
- *ifap = ifa->lst_next;
- __in6_ifa_put(ifp);
- ifa->lst_next = NULL;
- break;
- }
- }
+ hlist_del_init(&ifp->addr_lst);
+ __in6_ifa_put(ifp);
write_unlock_bh(&addrconf_hash_lock);
write_lock_bh(&idev->lock);
@@ -1277,11 +1270,12 @@ static int ipv6_count_addresses(struct i
int ipv6_chk_addr(struct net *net, struct in6_addr *addr,
struct net_device *dev, int strict)
{
- struct inet6_ifaddr * ifp;
+ struct inet6_ifaddr *ifp = NULL;
+ struct hlist_node *node;
u8 hash = ipv6_addr_hash(addr);
read_lock_bh(&addrconf_hash_lock);
- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+ hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -1300,10 +1294,11 @@ static
int ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
struct net_device *dev)
{
- struct inet6_ifaddr * ifp;
+ struct inet6_ifaddr *ifp;
+ struct hlist_node *node;
u8 hash = ipv6_addr_hash(addr);
- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+ hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr)) {
@@ -1342,11 +1337,12 @@ EXPORT_SYMBOL(ipv6_chk_prefix);
struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr,
struct net_device *dev, int strict)
{
- struct inet6_ifaddr * ifp;
+ struct inet6_ifaddr *ifp = NULL;
+ struct hlist_node *node;
u8 hash = ipv6_addr_hash(addr);
read_lock_bh(&addrconf_hash_lock);
- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+ hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr)) {
@@ -2610,7 +2606,6 @@ static int addrconf_ifdown(struct net_de
struct inet6_dev *idev;
struct inet6_ifaddr *ifa, **bifa;
struct net *net = dev_net(dev);
- int i;
ASSERT_RTNL();
@@ -2635,25 +2630,6 @@ static int addrconf_ifdown(struct net_de
}
- /* Step 2: clear hash table */
- for (i=0; i<IN6_ADDR_HSIZE; i++) {
- bifa = &inet6_addr_lst[i];
-
- write_lock_bh(&addrconf_hash_lock);
- while ((ifa = *bifa) != NULL) {
- if (ifa->idev == idev &&
- (how || !(ifa->flags&IFA_F_PERMANENT) ||
- ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
- *bifa = ifa->lst_next;
- ifa->lst_next = NULL;
- __in6_ifa_put(ifa);
- continue;
- }
- bifa = &ifa->lst_next;
- }
- write_unlock_bh(&addrconf_hash_lock);
- }
-
write_lock_bh(&idev->lock);
/* Step 3: clear flags for stateless addrconf */
@@ -2714,6 +2690,12 @@ static int addrconf_ifdown(struct net_de
}
write_unlock_bh(&idev->lock);
+ /* clear hash table */
+ write_lock_bh(&addrconf_hash_lock);
+ hlist_del_init(&ifa->addr_lst);
+ __in6_ifa_put(ifa);
+ write_unlock_bh(&addrconf_hash_lock);
+
__ipv6_ifa_notify(RTM_DELADDR, ifa);
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
in6_ifa_put(ifa);
@@ -2953,36 +2935,37 @@ static struct inet6_ifaddr *if6_get_firs
struct net *net = seq_file_net(seq);
for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
- ifa = inet6_addr_lst[state->bucket];
-
- while (ifa && !net_eq(dev_net(ifa->idev->dev), net))
- ifa = ifa->lst_next;
- if (ifa)
- break;
+ struct hlist_node *n;
+ hlist_for_each_entry(ifa, n,
+ &inet6_addr_lst[state->bucket], addr_lst) {
+ if (net_eq(dev_net(ifa->idev->dev), net))
+ return ifa;
+ }
}
- return ifa;
+ return NULL;
}
-static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct inet6_ifaddr *ifa)
+static struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
+ struct inet6_ifaddr *ifa)
{
struct if6_iter_state *state = seq->private;
struct net *net = seq_file_net(seq);
+ struct hlist_node *n = &ifa->addr_lst;
- ifa = ifa->lst_next;
-try_again:
- if (ifa) {
- if (!net_eq(dev_net(ifa->idev->dev), net)) {
- ifa = ifa->lst_next;
- goto try_again;
- }
+ hlist_for_each_entry_continue(ifa, n, addr_lst) {
+ if (net_eq(dev_net(ifa->idev->dev), net))
+ return ifa;
}
- if (!ifa && ++state->bucket < IN6_ADDR_HSIZE) {
- ifa = inet6_addr_lst[state->bucket];
- goto try_again;
+ while (++state->bucket < IN6_ADDR_HSIZE) {
+ hlist_for_each_entry(ifa, n,
+ &inet6_addr_lst[state->bucket], addr_lst) {
+ if (net_eq(dev_net(ifa->idev->dev), net))
+ return ifa;
+ }
}
- return ifa;
+ return NULL;
}
static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos)
@@ -3084,10 +3067,12 @@ void if6_proc_exit(void)
int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr)
{
int ret = 0;
- struct inet6_ifaddr * ifp;
+ struct inet6_ifaddr *ifp = NULL;
+ struct hlist_node *n;
u8 hash = ipv6_addr_hash(addr);
+
read_lock_bh(&addrconf_hash_lock);
- for (ifp = inet6_addr_lst[hash]; ifp; ifp = ifp->lst_next) {
+ hlist_for_each_entry(ifp, n, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -3108,6 +3093,7 @@ int ipv6_chk_home_addr(struct net *net,
static void addrconf_verify(unsigned long foo)
{
struct inet6_ifaddr *ifp;
+ struct hlist_node *node;
unsigned long now, next;
int i;
@@ -3121,7 +3107,7 @@ static void addrconf_verify(unsigned lon
restart:
read_lock(&addrconf_hash_lock);
- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+ hlist_for_each_entry(ifp, node, &inet6_addr_lst[i], addr_lst) {
unsigned long age;
#ifdef CONFIG_IPV6_PRIVACY
unsigned long regen_advance;
@@ -4540,7 +4526,7 @@ EXPORT_SYMBOL(unregister_inet6addr_notif
int __init addrconf_init(void)
{
- int err;
+ int i, err;
if ((err = ipv6_addr_label_init()) < 0) {
printk(KERN_CRIT "IPv6 Addrconf: cannot initialize default policy table: %d.\n",
@@ -4575,6 +4561,9 @@ int __init addrconf_init(void)
if (err)
goto errlo;
+ for (i = 0; i < IN6_ADDR_HSIZE; i++)
+ INIT_HLIST_HEAD(&inet6_addr_lst[i]);
+
register_netdevice_notifier(&ipv6_dev_notf);
addrconf_verify(0);
@@ -4603,7 +4592,6 @@ errlo:
void addrconf_cleanup(void)
{
- struct inet6_ifaddr *ifa;
struct net_device *dev;
int i;
@@ -4624,18 +4612,8 @@ void addrconf_cleanup(void)
* Check hash table.
*/
write_lock_bh(&addrconf_hash_lock);
- for (i=0; i < IN6_ADDR_HSIZE; i++) {
- for (ifa=inet6_addr_lst[i]; ifa; ) {
- struct inet6_ifaddr *bifa;
-
- bifa = ifa;
- ifa = ifa->lst_next;
- printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa);
- /* Do not free it; something is wrong.
- Now we can investigate it with debugger.
- */
- }
- }
+ for (i = 0; i < IN6_ADDR_HSIZE; i++)
+ WARN_ON(!hlist_empty(&inet6_addr_lst[i]));
write_unlock_bh(&addrconf_hash_lock);
del_timer(&addr_chk_timer);
--- a/include/net/if_inet6.h 2010-03-02 14:20:10.473202008 -0800
+++ b/include/net/if_inet6.h 2010-03-02 14:24:40.305201840 -0800
@@ -54,7 +54,7 @@ struct inet6_ifaddr {
struct inet6_dev *idev;
struct rt6_info *rt;
- struct inet6_ifaddr *lst_next; /* next addr in addr_lst */
+ struct hlist_node addr_lst;
struct inet6_ifaddr *if_next; /* next addr in inet6_dev */
#ifdef CONFIG_IPV6_PRIVACY
--
^ permalink raw reply
* [PATCH 06/12] IPv6: convert addrconf hash list to RCU
From: Stephen Hemminger @ 2010-03-02 23:32 UTC (permalink / raw)
To: David S. Miller, Hideaki YOSHIFUJI; +Cc: netdev
In-Reply-To: <20100302233243.259794027@vyatta.com>
[-- Attachment #1: ipv6-addrconf-rcu.patch --]
[-- Type: text/plain, Size: 9668 bytes --]
Convert from reader/writer lock to RCU and spinlock for addrconf
hash list.
Adds an additional helper macro for hlist_for_each_entry_continue_rcu
to handle the continue case.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
---
include/linux/rculist.h | 13 +++++++
include/net/if_inet6.h | 1
net/ipv6/addrconf.c | 80 +++++++++++++++++++++++++-----------------------
3 files changed, 57 insertions(+), 37 deletions(-)
--- a/net/ipv6/addrconf.c 2010-03-02 14:29:25.444951749 -0800
+++ b/net/ipv6/addrconf.c 2010-03-02 14:33:59.033450885 -0800
@@ -127,7 +127,7 @@ static int ipv6_count_addresses(struct i
* Configured unicast address hash table
*/
static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
-static DEFINE_RWLOCK(addrconf_hash_lock);
+static DEFINE_SPINLOCK(addrconf_hash_lock);
static void addrconf_verify(unsigned long);
@@ -523,8 +523,13 @@ static int addrconf_fixup_forwarding(str
}
#endif
-/* Nobody refers to this ifaddr, destroy it */
+static void inet6_ifa_finish_destroy_rcu(struct rcu_head *head)
+{
+ struct inet6_ifaddr *ifp = container_of(head, struct inet6_ifaddr, rcu);
+ kfree(ifp);
+}
+/* Nobody refers to this ifaddr, destroy it */
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
{
WARN_ON(ifp->if_next != NULL);
@@ -545,7 +550,7 @@ void inet6_ifa_finish_destroy(struct ine
}
dst_release(&ifp->rt->u.dst);
- kfree(ifp);
+ call_rcu(&ifp->rcu, inet6_ifa_finish_destroy_rcu);
}
static void
@@ -616,7 +621,7 @@ ipv6_add_addr(struct inet6_dev *idev, co
goto out2;
}
- write_lock(&addrconf_hash_lock);
+ spin_lock(&addrconf_hash_lock);
/* Ignore adding duplicate addresses on an interface */
if (ipv6_chk_same_addr(dev_net(idev->dev), addr, idev->dev)) {
@@ -670,9 +675,9 @@ ipv6_add_addr(struct inet6_dev *idev, co
/* Add to big hash table */
hash = ipv6_addr_hash(addr);
- hlist_add_head(&ifa->addr_lst, &inet6_addr_lst[hash]);
+ hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]);
in6_ifa_hold(ifa);
- write_unlock(&addrconf_hash_lock);
+ spin_unlock(&addrconf_hash_lock);
write_lock(&idev->lock);
/* Add to inet6_dev unicast addr list. */
@@ -699,7 +704,7 @@ out2:
return ifa;
out:
- write_unlock(&addrconf_hash_lock);
+ spin_unlock(&addrconf_hash_lock);
goto out2;
}
@@ -717,10 +722,10 @@ static void ipv6_del_addr(struct inet6_i
ifp->dead = 1;
- write_lock_bh(&addrconf_hash_lock);
- hlist_del_init(&ifp->addr_lst);
+ spin_lock_bh(&addrconf_hash_lock);
+ hlist_del_init_rcu(&ifp->addr_lst);
__in6_ifa_put(ifp);
- write_unlock_bh(&addrconf_hash_lock);
+ spin_unlock_bh(&addrconf_hash_lock);
write_lock_bh(&idev->lock);
#ifdef CONFIG_IPV6_PRIVACY
@@ -1274,8 +1279,8 @@ int ipv6_chk_addr(struct net *net, struc
struct hlist_node *node;
u8 hash = ipv6_addr_hash(addr);
- read_lock_bh(&addrconf_hash_lock);
- hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
+ rcu_read_lock_bh();
+ hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -1285,7 +1290,8 @@ int ipv6_chk_addr(struct net *net, struc
break;
}
}
- read_unlock_bh(&addrconf_hash_lock);
+ rcu_read_unlock_bh();
+
return ifp != NULL;
}
EXPORT_SYMBOL(ipv6_chk_addr);
@@ -1341,8 +1347,8 @@ struct inet6_ifaddr *ipv6_get_ifaddr(str
struct hlist_node *node;
u8 hash = ipv6_addr_hash(addr);
- read_lock_bh(&addrconf_hash_lock);
- hlist_for_each_entry(ifp, node, &inet6_addr_lst[hash], addr_lst) {
+ rcu_read_lock_bh();
+ hlist_for_each_entry_rcu(ifp, node, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr)) {
@@ -1353,7 +1359,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(str
}
}
}
- read_unlock_bh(&addrconf_hash_lock);
+ rcu_read_unlock_bh();
return ifp;
}
@@ -2691,10 +2697,10 @@ static int addrconf_ifdown(struct net_de
write_unlock_bh(&idev->lock);
/* clear hash table */
- write_lock_bh(&addrconf_hash_lock);
- hlist_del_init(&ifa->addr_lst);
+ spin_lock_bh(&addrconf_hash_lock);
+ hlist_del_init_rcu(&ifa->addr_lst);
__in6_ifa_put(ifa);
- write_unlock_bh(&addrconf_hash_lock);
+ spin_unlock_bh(&addrconf_hash_lock);
__ipv6_ifa_notify(RTM_DELADDR, ifa);
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
@@ -2936,11 +2942,10 @@ static struct inet6_ifaddr *if6_get_firs
for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
struct hlist_node *n;
- hlist_for_each_entry(ifa, n,
- &inet6_addr_lst[state->bucket], addr_lst) {
+ hlist_for_each_entry_rcu(ifa, n, &inet6_addr_lst[state->bucket],
+ addr_lst)
if (net_eq(dev_net(ifa->idev->dev), net))
return ifa;
- }
}
return NULL;
}
@@ -2952,10 +2957,9 @@ static struct inet6_ifaddr *if6_get_next
struct net *net = seq_file_net(seq);
struct hlist_node *n = &ifa->addr_lst;
- hlist_for_each_entry_continue(ifa, n, addr_lst) {
+ hlist_for_each_entry_continue_rcu(ifa, n, addr_lst)
if (net_eq(dev_net(ifa->idev->dev), net))
return ifa;
- }
while (++state->bucket < IN6_ADDR_HSIZE) {
hlist_for_each_entry(ifa, n,
@@ -2979,9 +2983,9 @@ static struct inet6_ifaddr *if6_get_idx(
}
static void *if6_seq_start(struct seq_file *seq, loff_t *pos)
- __acquires(addrconf_hash_lock)
+ __acquires(rcu)
{
- read_lock_bh(&addrconf_hash_lock);
+ rcu_read_lock_bh();
return if6_get_idx(seq, *pos);
}
@@ -2995,9 +2999,9 @@ static void *if6_seq_next(struct seq_fil
}
static void if6_seq_stop(struct seq_file *seq, void *v)
- __releases(addrconf_hash_lock)
+ __releases(rcu)
{
- read_unlock_bh(&addrconf_hash_lock);
+ rcu_read_unlock_bh();
}
static int if6_seq_show(struct seq_file *seq, void *v)
@@ -3071,8 +3075,8 @@ int ipv6_chk_home_addr(struct net *net,
struct hlist_node *n;
u8 hash = ipv6_addr_hash(addr);
- read_lock_bh(&addrconf_hash_lock);
- hlist_for_each_entry(ifp, n, &inet6_addr_lst[hash], addr_lst) {
+ rcu_read_lock_bh();
+ hlist_for_each_entry_rcu(ifp, n, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net))
continue;
if (ipv6_addr_equal(&ifp->addr, addr) &&
@@ -3081,7 +3085,7 @@ int ipv6_chk_home_addr(struct net *net,
break;
}
}
- read_unlock_bh(&addrconf_hash_lock);
+ rcu_read_unlock_bh();
return ret;
}
#endif
@@ -3097,7 +3101,8 @@ static void addrconf_verify(unsigned lon
unsigned long now, next;
int i;
- spin_lock_bh(&addrconf_verify_lock);
+ rcu_read_lock_bh();
+ spin_lock(&addrconf_verify_lock);
now = jiffies;
next = now + ADDR_CHECK_FREQUENCY;
@@ -3106,8 +3111,8 @@ static void addrconf_verify(unsigned lon
for (i=0; i < IN6_ADDR_HSIZE; i++) {
restart:
- read_lock(&addrconf_hash_lock);
- hlist_for_each_entry(ifp, node, &inet6_addr_lst[i], addr_lst) {
+ hlist_for_each_entry_rcu(ifp, node,
+ &inet6_addr_lst[i], addr_lst) {
unsigned long age;
#ifdef CONFIG_IPV6_PRIVACY
unsigned long regen_advance;
@@ -3129,7 +3134,6 @@ restart:
age >= ifp->valid_lft) {
spin_unlock(&ifp->lock);
in6_ifa_hold(ifp);
- read_unlock(&addrconf_hash_lock);
ipv6_del_addr(ifp);
goto restart;
} else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
@@ -3151,7 +3155,6 @@ restart:
if (deprecate) {
in6_ifa_hold(ifp);
- read_unlock(&addrconf_hash_lock);
ipv6_ifa_notify(0, ifp);
in6_ifa_put(ifp);
@@ -3169,7 +3172,7 @@ restart:
in6_ifa_hold(ifp);
in6_ifa_hold(ifpub);
spin_unlock(&ifp->lock);
- read_unlock(&addrconf_hash_lock);
+
spin_lock(&ifpub->lock);
ifpub->regen_count = 0;
spin_unlock(&ifpub->lock);
@@ -3189,12 +3192,12 @@ restart:
spin_unlock(&ifp->lock);
}
}
- read_unlock(&addrconf_hash_lock);
}
addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next;
add_timer(&addr_chk_timer);
- spin_unlock_bh(&addrconf_verify_lock);
+ spin_unlock(&addrconf_verify_lock);
+ rcu_read_unlock_bh();
}
static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
@@ -4611,10 +4614,10 @@ void addrconf_cleanup(void)
/*
* Check hash table.
*/
- write_lock_bh(&addrconf_hash_lock);
+ spin_lock_bh(&addrconf_hash_lock);
for (i = 0; i < IN6_ADDR_HSIZE; i++)
WARN_ON(!hlist_empty(&inet6_addr_lst[i]));
- write_unlock_bh(&addrconf_hash_lock);
+ spin_unlock_bh(&addrconf_hash_lock);
del_timer(&addr_chk_timer);
rtnl_unlock();
--- a/include/linux/rculist.h 2010-03-01 08:22:23.456662311 -0800
+++ b/include/linux/rculist.h 2010-03-02 14:29:56.641076077 -0800
@@ -428,5 +428,18 @@ static inline void hlist_add_after_rcu(s
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
pos = rcu_dereference_raw(pos->next))
+/**
+ * hlist_for_each_entry_continue_rcu - iterate over a hlist continuing after current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue_rcu(tpos, pos, member) \
+ for (pos = rcu_dereference((pos)->next); \
+ pos && ({ prefetch(pos->next); 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
+ pos = rcu_dereference(pos->next))
+
+
#endif /* __KERNEL__ */
#endif
--- a/include/net/if_inet6.h 2010-03-02 14:29:25.465077512 -0800
+++ b/include/net/if_inet6.h 2010-03-02 14:31:13.137385294 -0800
@@ -64,6 +64,7 @@ struct inet6_ifaddr {
#endif
int dead;
+ struct rcu_head rcu;
};
struct ip6_sf_socklist {
--
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox