diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 70aaa4238417..89ca965ec1cc 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -399,6 +399,7 @@ header-y += signalfd.h header-y += snmp.h header-y += sock_diag.h header-y += socket.h +header-y += sockev.h header-y += sockios.h header-y += som.h header-y += sonet.h diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index e6c6afe9ea23..de33c601e17c 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -26,7 +26,7 @@ #define NETLINK_ECRYPTFS 19 #define NETLINK_RDMA 20 #define NETLINK_CRYPTO 21 /* Crypto layer */ - +#define NETLINK_SOCKEV 22 /* Socket Administrative Events */ #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG #define MAX_LINKS 32 diff --git a/include/uapi/linux/sockev.h b/include/uapi/linux/sockev.h new file mode 100644 index 000000000000..fe6f35a2adf6 --- /dev/null +++ b/include/uapi/linux/sockev.h @@ -0,0 +1,31 @@ +#ifndef _SOCKEV_H_ +#define _SOCKEV_H_ + +#include +#include +#include + +enum sknetlink_groups { + SKNLGRP_UNICAST, + SKNLGRP_SOCKEV, + __SKNLGRP_MAX +}; + +#define SOCKEV_STR_MAX 32 + +/******************************************************************** +* Socket operation messages +****/ + +struct sknlsockevmsg { + __u8 event[SOCKEV_STR_MAX]; + __u32 pid; /* (struct task_struct*)->pid */ + __u16 skfamily; /* (struct socket*)->sk->sk_family */ + __u8 skstate; /* (struct socket*)->sk->sk_state */ + __u8 skprotocol; /* (struct socket*)->sk->sk_protocol */ + __u16 sktype; /* (struct socket*)->sk->sk_type */ + __u64 skflags; /* (struct socket*)->sk->sk_flags */ +}; + +#endif /* _SOCKEV_H_ */ + diff --git a/net/Kconfig b/net/Kconfig index 7403cf17e77c..262ac3287ce9 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -274,6 +274,15 @@ config BPF_JIT packet sniffing (libpcap/tcpdump). Note : Admin should enable this feature changing /proc/sys/net/core/bpf_jit_enable +config SOCKEV_NLMCAST + bool "Enable SOCKEV Netlink Multicast" + default n + ---help--- + Default client for SOCKEV notifier events. Sends multicast netlink + messages whenever the socket event notifier is invoked. Enable if + user space entities need to be notified of socket events without + having to poll /proc + menu "Network testing" config NET_PKTGEN diff --git a/net/core/Makefile b/net/core/Makefile index b33b996f5dd6..c84923cf8b11 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_TRACEPOINTS) += net-traces.o obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o obj-$(CONFIG_NETPRIO_CGROUP) += netprio_cgroup.o +obj-$(CONFIG_SOCKEV_NLMCAST) += sockev_nlmcast.o \ No newline at end of file diff --git a/net/core/sockev_nlmcast.c b/net/core/sockev_nlmcast.c new file mode 100644 index 000000000000..0b934d578d0b --- /dev/null +++ b/net/core/sockev_nlmcast.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Default SOCKEV client implementation + * + */ + +#include +#include +#include +#include +#include +#include + +static int registration_status; +static struct sock *socknlmsgsk; + +static void sockev_skmsg_recv(struct sk_buff *skb) +{ + pr_debug("%s(): Got unsolicited request\n", __func__); +} + +static struct netlink_kernel_cfg nlcfg = { + .input = sockev_skmsg_recv +}; + +static void _sockev_event(unsigned long event, __u8 *evstr, int buflen) +{ + switch (event) { + case SOCKEV_SOCKET: + strlcpy(evstr, "SOCKEV_SOCKET", buflen); + break; + case SOCKEV_BIND: + strlcpy(evstr, "SOCKEV_BIND", buflen); + break; + case SOCKEV_LISTEN: + strlcpy(evstr, "SOCKEV_LISTEN", buflen); + break; + case SOCKEV_ACCEPT: + strlcpy(evstr, "SOCKEV_ACCEPT", buflen); + break; + case SOCKEV_CONNECT: + strlcpy(evstr, "SOCKEV_CONNECT", buflen); + break; + case SOCKEV_SHUTDOWN: + strlcpy(evstr, "SOCKEV_SHUTDOWN", buflen); + break; + default: + strlcpy(evstr, "UNKOWN", buflen); + } +} + +static int sockev_client_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct sknlsockevmsg *smsg; + struct socket *sock; + + sock = (struct socket *)data; + if (socknlmsgsk == 0) + goto done; + if ((socknlmsgsk == NULL) || (sock == NULL) || (sock->sk == NULL)) + goto done; + + skb = nlmsg_new(sizeof(struct sknlsockevmsg), GFP_KERNEL); + if (skb == NULL) + goto done; + + nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct sknlsockevmsg), 0); + if (nlh == NULL) { + kfree_skb(skb); + goto done; + } + + NETLINK_CB(skb).dst_group = SKNLGRP_SOCKEV; + + smsg = nlmsg_data(nlh); + smsg->pid = current->pid; + _sockev_event(event, smsg->event, sizeof(smsg->event)); + smsg->skfamily = sock->sk->sk_family; + smsg->skstate = sock->sk->sk_state; + smsg->skprotocol = sock->sk->sk_protocol; + smsg->sktype = sock->sk->sk_type; + smsg->skflags = sock->sk->sk_flags; + + nlmsg_notify(socknlmsgsk, skb, 0, SKNLGRP_SOCKEV, 0, GFP_KERNEL); +done: + return 0; +} + +static struct notifier_block sockev_notifier_client = { + .notifier_call = sockev_client_cb, + .next = 0, + .priority = 0 +}; + +/* ***************** Startup/Shutdown *************************************** */ + +static int __init sockev_client_init(void) +{ + int rc; + registration_status = 1; + rc = sockev_register_notify(&sockev_notifier_client); + if (rc != 0) { + registration_status = 0; + pr_err("%s(): Failed to register cb (%d)\n", __func__, rc); + } + socknlmsgsk = netlink_kernel_create(&init_net, NETLINK_SOCKEV, &nlcfg); + if (!socknlmsgsk) { + pr_err("%s(): Failed to initialize netlink socket\n", __func__); + if (registration_status) + sockev_unregister_notify(&sockev_notifier_client); + registration_status = 0; + } + + return rc; +} +static void __exit sockev_client_exit(void) +{ + if (registration_status) + sockev_unregister_notify(&sockev_notifier_client); +} +module_init(sockev_client_init) +module_exit(sockev_client_exit) +MODULE_LICENSE("GPL v2"); +