icwmp/kcwmp.c
2019-12-06 09:57:02 +01:00

189 lines
4.9 KiB
C

/*
* Copyright (C) 2019 iopsys Software Solutions AB. 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 as published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <stdarg.h>
#include <linux/slab.h>
#include "inc/cwmp_kernel.h"
typedef struct kernel_cwmp_input {
char **argv;
int argc;
int handler;
struct mutex mutex;
wait_queue_head_t thresholdq;
} KERNEL_CWMP_INPUT;
static struct sock *nl_sk = NULL;
static struct kernel_cwmp_input kernel_cwmp_input;
MODULE_LICENSE("IOPSYS");
static void kernel_api_cwmp_value_change_listener(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg;
char *recv;
int i,res;
nlh = (struct nlmsghdr*)skb->data;
recv = (char*)nlmsg_data(nlh);
if (strcmp(recv,NETLINK_CWMP_ID)!=0)
{
return;
}
pid = nlh->nlmsg_pid; /*pid of sending process */
while (kernel_cwmp_input.argc==0)
{
if ( wait_event_interruptible( kernel_cwmp_input.thresholdq, kernel_cwmp_input.handler)) {
return ;
}
}
kernel_cwmp_input.handler = 0;
mutex_lock (&(kernel_cwmp_input.mutex));
for (i=0;i<=kernel_cwmp_input.argc;i++)
{
if (i<kernel_cwmp_input.argc)
{
msg = kernel_cwmp_input.argv[i];
}
else
{
msg = NETLINK_END_DATA;
}
msg_size = strlen(msg);
skb_out = nlmsg_new(msg_size,0);
if(!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh=nlmsg_put(skb_out,0,0,NLMSG_DONE,msg_size,0);
NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
strncpy(nlmsg_data(nlh),msg,msg_size);
res=nlmsg_unicast(nl_sk,skb_out,pid);
if (i<kernel_cwmp_input.argc)
{
kfree(kernel_cwmp_input.argv[i]);
}
}
if (kernel_cwmp_input.argv!=NULL)
{
kfree(kernel_cwmp_input.argv);
kernel_cwmp_input.argv = NULL;
}
kernel_cwmp_input.argc = 0;
mutex_unlock (&(kernel_cwmp_input.mutex));
}
static int /*__init*/ kernel_api_cwmp_value_change_init(void)
{
printk(KERN_INFO "Entering kernel cwmp module: %s\n",__FUNCTION__);
memset (&kernel_cwmp_input, 0, sizeof(struct kernel_cwmp_input));
init_waitqueue_head(&kernel_cwmp_input.thresholdq);
mutex_init(&kernel_cwmp_input.mutex);
nl_sk=netlink_kernel_create(&init_net, NETLINK_USER, 0, kernel_api_cwmp_value_change_listener, NULL, THIS_MODULE);
if(!nl_sk)
{
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
return 0;
}
static void /*__exit*/ kernel_api_cwmp_value_change_exit(void)
{
printk(KERN_INFO "Exiting kernel cwmp module: %s\n",__FUNCTION__);
netlink_kernel_release(nl_sk);
}
int kernel_api_cwmp_value_change_call (int count, ...)
{
int i;
va_list args;
char *s;
if (kernel_cwmp_input.argc>0)
{
kernel_cwmp_input.handler = 1;
wake_up_interruptible(&(kernel_cwmp_input.thresholdq));
return 1;
}
mutex_lock (&(kernel_cwmp_input.mutex));
kernel_cwmp_input.argv = kmalloc(count*sizeof(char *),GFP_KERNEL);
if (kernel_cwmp_input.argv==NULL)
{
goto kernel_api_cwmp_error;
}
va_start(args, count);
for (i=0;i<count;i++)
{
s = (char *) va_arg(args, char *);
if (s==NULL)
{
s = NETLINK_NULL;
}
kernel_cwmp_input.argv[i] = kmalloc(strlen(s),GFP_KERNEL);
if (kernel_cwmp_input.argv[i]==NULL)
{
goto kernel_api_cwmp_error;
}
strcpy(kernel_cwmp_input.argv[i],s);
kernel_cwmp_input.argc++;
}
va_end(args);
mutex_unlock (&(kernel_cwmp_input.mutex));
kernel_cwmp_input.handler = 1;
wake_up_interruptible(&(kernel_cwmp_input.thresholdq));
return 1;
kernel_api_cwmp_error:
if (kernel_cwmp_input.argv!=NULL)
{
for (i=0;i<kernel_cwmp_input.argc;i++)
{
if (kernel_cwmp_input.argv[i]!=NULL)
{
kfree(kernel_cwmp_input.argv[i]);
}
}
kfree(kernel_cwmp_input.argv);
kernel_cwmp_input.argv = NULL;
}
kernel_cwmp_input.argc = 0;
mutex_unlock (&(kernel_cwmp_input.mutex));
return 1;
}
EXPORT_SYMBOL(kernel_api_cwmp_value_change_call);
module_init(kernel_api_cwmp_value_change_init); module_exit(kernel_api_cwmp_value_change_exit);