net: socket: Added notifier chains for socket administrative functions
Allows other areas in the kernel to register notifier callbacks which
get invoked whenever something performs an administrative action on a
socket. This patch adds hooks in socket(), bind(), listen(), accept(),
shutdown().
CRs-Fixed: 626021
Change-Id: I4ae99cb2206d7c4eddba69757335c18d10143045
Signed-off-by: Harout Hedeshian <harouth@codeaurora.org>
diff --git a/include/net/sock.h b/include/net/sock.h
index 83e98f6..9f78060 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2155,4 +2155,15 @@
extern __u32 sysctl_wmem_default;
extern __u32 sysctl_rmem_default;
+/* SOCKEV Notifier Events */
+#define SOCKEV_SOCKET 0x00
+#define SOCKEV_BIND 0x01
+#define SOCKEV_LISTEN 0x02
+#define SOCKEV_ACCEPT 0x03
+#define SOCKEV_CONNECT 0x04
+#define SOCKEV_SHUTDOWN 0x05
+
+int sockev_register_notify(struct notifier_block *nb);
+int sockev_unregister_notify(struct notifier_block *nb);
+
#endif /* _SOCK_H */
diff --git a/net/socket.c b/net/socket.c
index 851edcd..3548af3 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -105,6 +105,8 @@
#include <linux/sockios.h>
#include <linux/atalk.h>
+static BLOCKING_NOTIFIER_HEAD(sockev_notifier_list);
+
static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
@@ -165,6 +167,14 @@
static DEFINE_PER_CPU(int, sockets_in_use);
/*
+ * Socket Event framework helpers
+ */
+static void sockev_notify(unsigned long event, struct socket *sk)
+{
+ blocking_notifier_call_chain(&sockev_notifier_list, event, sk);
+}
+
+/**
* Support routines.
* Move socket addresses back and forth across the kernel/user
* divide and look after the messy bits.
@@ -1343,6 +1353,9 @@
if (retval < 0)
goto out;
+ if (retval == 0)
+ sockev_notify(SOCKEV_SOCKET, sock);
+
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;
@@ -1460,6 +1473,8 @@
&address, addrlen);
}
fput_light(sock->file, fput_needed);
+ if (!err)
+ sockev_notify(SOCKEV_BIND, sock);
}
return err;
}
@@ -1487,6 +1502,8 @@
err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed);
+ if (!err)
+ sockev_notify(SOCKEV_LISTEN, sock);
}
return err;
}
@@ -1566,7 +1583,8 @@
fd_install(newfd, newfile);
err = newfd;
-
+ if (!err)
+ sockev_notify(SOCKEV_ACCEPT, sock);
out_put:
fput_light(sock->file, fput_needed);
out:
@@ -1616,6 +1634,8 @@
err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
sock->file->f_flags);
+ if (!err)
+ sockev_notify(SOCKEV_CONNECT, sock);
out_put:
fput_light(sock->file, fput_needed);
out:
@@ -1876,6 +1896,7 @@
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
+ sockev_notify(SOCKEV_SHUTDOWN, sock);
err = security_socket_shutdown(sock, how);
if (!err)
err = sock->ops->shutdown(sock, how);
@@ -3378,3 +3399,15 @@
return sock->ops->shutdown(sock, how);
}
EXPORT_SYMBOL(kernel_sock_shutdown);
+
+int sockev_register_notify(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&sockev_notifier_list, nb);
+}
+EXPORT_SYMBOL(sockev_register_notify);
+
+int sockev_unregister_notify(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&sockev_notifier_list, nb);
+}
+EXPORT_SYMBOL(sockev_unregister_notify);