blob: 7b872a1d09b42c2050d14433ea4eb4b067ab565f [file] [log] [blame]
/*
* Copyright (c) 2014-2015, 2018 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/netlink.h>
#include <linux/sockev.h>
#include <net/sock.h>
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, "UNKNOWN", 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;
struct sock *sk;
sock = (struct socket *)data;
if (!socknlmsgsk || !sock)
goto sk_null;
sk = sock->sk;
if (!sk)
goto sk_null;
sock_hold(sk);
if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)
goto done;
if (event != SOCKEV_BIND && event != SOCKEV_LISTEN)
goto done;
skb = nlmsg_new(sizeof(struct sknlsockevmsg), GFP_KERNEL);
if (!skb)
goto done;
nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct sknlsockevmsg), 0);
if (!nlh) {
kfree_skb(skb);
goto done;
}
NETLINK_CB(skb).dst_group = SKNLGRP_SOCKEV;
smsg = nlmsg_data(nlh);
memset(smsg, 0, sizeof(struct sknlsockevmsg));
smsg->pid = current->pid;
_sockev_event(event, smsg->event, sizeof(smsg->event));
smsg->skfamily = sk->sk_family;
smsg->skstate = sk->sk_state;
smsg->skprotocol = sk->sk_protocol;
smsg->sktype = sk->sk_type;
smsg->skflags = sk->sk_flags;
nlmsg_notify(socknlmsgsk, skb, 0, SKNLGRP_SOCKEV, 0, GFP_KERNEL);
done:
sock_put(sk);
sk_null:
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");