blob: fdb20baa5ec75e0bee30388d991c96df6c735c70 [file] [log] [blame]
/*
*
* module-wide functions, mostly boilerplate
*
* Copyright (c) 2013-2014 Andrew Yourtchenko <ayourtch@gmail.com>
*
* 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
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/inet.h>
#include <linux/icmpv6.h>
#include <linux/inetdevice.h>
#include <linux/types.h>
#include <linux/netfilter_ipv4.h>
#include <linux/fs.h> // for basic filesystem
#include <linux/proc_fs.h> // for the proc filesystem
#include <linux/seq_file.h> // for sequence files
#include <net/ip.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/ip6_route.h>
#include <net/ipv6.h>
#include "nat46-core.h"
#include "nat46-netdev.h"
#define NAT46_PROC_NAME "nat46"
#define NAT46_CONTROL_PROC_NAME "control"
#ifndef NAT46_VERSION
#define NAT46_VERSION __DATE__ " " __TIME__
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrew Yourtchenko <ayourtch@gmail.com>");
MODULE_DESCRIPTION("NAT46 stateless translation");
int debug = 0;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "debugging messages level (default=1)");
bool add_dummy_header = 0;
module_param(add_dummy_header, bool, 0);
MODULE_PARM_DESC(add_dummy_header, "Add dummy fragment header");
static DEFINE_MUTEX(add_del_lock);
static struct proc_dir_entry *nat46_proc_entry;
static struct proc_dir_entry *nat46_proc_parent;
static int nat46_proc_show(struct seq_file *m, void *v)
{
nat64_show_all_configs(m);
return 0;
}
static int nat46_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, nat46_proc_show, NULL);
}
static char *get_devname(char **ptail)
{
const int maxlen = IFNAMSIZ-1;
char *devname = get_next_arg(ptail);
if(strlen(devname) > maxlen) {
printk(KERN_INFO "nat46: '%s' is "
"longer than %d chars, truncating\n", devname, maxlen);
devname[maxlen] = 0;
}
return devname;
}
static ssize_t nat46_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
char *buf = NULL;
char *tail = NULL;
char *devname = NULL;
char *arg_name = NULL;
buf = kmalloc(sizeof(char) * (count + 1), GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
kfree(buf);
return -EFAULT;
}
tail = buf;
buf[count] = '\0';
if( (count > 0) && (buf[count-1] == '\n') ) {
buf[count-1] = '\0';
}
while (NULL != (arg_name = get_next_arg(&tail))) {
if (0 == strcmp(arg_name, "add")) {
devname = get_devname(&tail);
printk(KERN_INFO "nat46: adding device (%s)\n", devname);
mutex_lock(&add_del_lock);
nat46_create(devname);
mutex_unlock(&add_del_lock);
} else if (0 == strcmp(arg_name, "del")) {
devname = get_devname(&tail);
printk(KERN_INFO "nat46: deleting device (%s)\n", devname);
mutex_lock(&add_del_lock);
nat46_destroy(devname);
mutex_unlock(&add_del_lock);
} else if (0 == strcmp(arg_name, "config")) {
devname = get_devname(&tail);
printk(KERN_INFO "nat46: configure device (%s) with '%s'\n", devname, tail);
mutex_lock(&add_del_lock);
nat46_configure(devname, tail);
mutex_unlock(&add_del_lock);
} else if (0 == strcmp(arg_name, "insert")) {
devname = get_devname(&tail);
printk(KERN_INFO "nat46: insert new rule into device (%s) with '%s'\n", devname, tail);
mutex_lock(&add_del_lock);
nat46_insert(devname, tail);
mutex_unlock(&add_del_lock);
}
}
kfree(buf);
return count;
}
static const struct file_operations nat46_proc_fops = {
.owner = THIS_MODULE,
.open = nat46_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = nat46_proc_write,
};
int create_nat46_proc_entry(void) {
nat46_proc_parent = proc_mkdir(NAT46_PROC_NAME, init_net.proc_net);
if (nat46_proc_parent) {
nat46_proc_entry = proc_create(NAT46_CONTROL_PROC_NAME, 0644, nat46_proc_parent, &nat46_proc_fops );
if(!nat46_proc_entry) {
printk(KERN_INFO "Error creating proc entry");
return -ENOMEM;
}
}
return 0;
}
static int __init nat46_init(void)
{
int ret = 0;
printk("nat46: module (version %s) loaded.\n", NAT46_VERSION);
ret = create_nat46_proc_entry();
if(ret) {
goto error;
}
return 0;
error:
return ret;
}
static void __exit nat46_exit(void)
{
nat46_destroy_all();
if (nat46_proc_parent) {
if (nat46_proc_entry) {
remove_proc_entry(NAT46_CONTROL_PROC_NAME, nat46_proc_parent);
}
remove_proc_entry(NAT46_PROC_NAME, init_net.proc_net);
}
printk("nat46: module unloaded.\n");
}
module_init(nat46_init);
module_exit(nat46_exit);