<repo_name>jakubgs/netdev<file_sep>/kernel/fo.h
#ifndef _FO_H
#define _FO_H

#include <linux/fs.h> /* iovec, kiocb, file, pool_table_struct */

extern struct file_operations netdev_fops;

#endif /* _FO_H */
<file_sep>/kernel/fo_access.c
#include <asm/errno.h>

#include "fo_access.h"
#include "fo_comm.h"
#include "dbg.h"

struct fo_access * fo_acc_start(struct netdev_data *nddata, int pid) {
    int err = 0;
    struct fo_access *acc = NULL;

    if (!nddata) {
        printk(KERN_ERR "fo_acc_start: nddata is NULL\n");
        return NULL;
    }

    acc = kzalloc(sizeof(*acc), GFP_KERNEL);
    if (!acc) {
        printk(KERN_ERR "fo_acc_alloc: failed to allocate fo_access\n");
        return NULL;
    }

    if (nddata->dummy) { /* server doesn't need a queue */
        if ((err = kfifo_alloc(&acc->fo_queue,
                                NETDEV_FO_QUEUE_SIZE,
                                GFP_KERNEL))) {
            printk(KERN_ERR "fo_acc_alloc: failed to allocate fo_queue\n");
            goto err;
        }
    }

    init_rwsem(&acc->sem);
    acc->access_id = pid;
    acc->nddata = nddata;

    if (down_write_trylock(&nddata->sem)) {
        hash_add(nddata->foacc_htable, &acc->hnode, pid);
        ndmgm_get(nddata);

        up_write(&acc->nddata->sem);
    } else {
        printk(KERN_ERR "fo_acc_start: failed to add acc to hashtable\n");
        goto err;
    }

    return acc;
err:
    if (nddata->dummy) {
        kfifo_free(&acc->fo_queue);
    }
    kfree(acc);
    return NULL;
}

void fo_acc_destroy(
    struct fo_access *acc)
{
    down_write(&acc->nddata->sem);
    hash_del(&acc->hnode);
    ndmgm_put(acc->nddata);
    up_write(&acc->nddata->sem);

    if (acc->nddata->dummy) {
        kfifo_free(&acc->fo_queue);
    }
    kfree(acc);
}

int fo_acc_free_queue(
    struct fo_access *acc)
{
    int size = 0;
    struct fo_req *req = NULL;

    down_write(&acc->sem);
    /* destroy all requests in the queue */
    while (!kfifo_is_empty(&acc->fo_queue)) {
        size = kfifo_out(&acc->fo_queue, &req, sizeof(req));
        if ( size != sizeof(req) || IS_ERR(req)) {
            printk(KERN_ERR "fo_acc_free_queue: failed to fetch from queue, size = %d\n", size);
            return 1; /* failure */
        }

        req->rvalue = -ENODATA;
        complete(&req->comp); /* complete all pending file operations */
    }
    up_write(&acc->sem);
    return 0; /* success */
}

/* find the filo operation request with the correct sequence number */
struct fo_req * fo_acc_foreq_find(
    struct fo_access *acc,
    int seq)
{
    /* will hold the first element as a stopper */
    struct fo_req *req = NULL;
    struct fo_req *tmp = NULL;
    int size = 0;

    if (down_write_trylock(&acc->sem)) {
        while (!kfifo_is_empty(&acc->fo_queue)) {
            size = kfifo_out(&acc->fo_queue, &tmp, sizeof(tmp));
            if (size < sizeof(tmp) || IS_ERR(tmp)) {
                printk(KERN_ERR "fo_acc_foreq_find: failed to get queue element\n");
                tmp = NULL;
                break;
            }
            if (req == NULL) {
                req = tmp; /* first element */
            }
            if (tmp->seq == seq) {
                break;
            }
            /* put the wrong element back into the queue */
            debug("wrong element, putting it back at the end");
            kfifo_in(&acc->fo_queue, &tmp, sizeof(tmp));
            if (req == tmp) {
                tmp = NULL;
                break;
            }
        }
        up_write(&acc->sem);
    }
    return tmp;
}

int fo_acc_foreq_add(
    struct fo_access *acc,
    struct fo_req *req)
{
    if (down_write_trylock(&acc->sem)) {
        kfifo_in(&acc->fo_queue, &req, sizeof(req));
        up_write(&acc->sem);
        return 0; /* success */
    }
    return 1; /* failure */
}


<file_sep>/daemon/proxy.c
#include <stdlib.h>        /* malloc                           */
#include <stdio.h>         /* printf                           */
#include <unistd.h>        /* close, getpid                    */
#include <sys/prctl.h>      /* prctl */
#include <signal.h>         /* signal */
#include <errno.h>

#include "proxy.h"
#include "protocol.h"
#include "signal.h"
#include "helper.h"
#include "netlink.h"
#include "conn.h"
#include "debug.h"

void proxy_setup_signals()
{
    /* send SIGHUP when parent process dies */
    prctl(PR_SET_PDEATHSIG, SIGHUP);
    /* and handle SIGHUP to safely unregister the device */
    signal(SIGHUP, proxy_sig_hup);
    /* ignore INT since it comes before HUP */
    signal(SIGINT, SIG_IGN);
}

int proxy_setup_unixsoc(struct proxy_dev *pdev)
{
    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pdev->us_fd) == -1) {
        perror("proxy_setup_unixsoc");
        return -1; /* failure */
    }
    return 0; /* success */
}

struct proxy_dev * proxy_alloc_dev(int connfd)
{
    struct proxy_dev *pdev = NULL;
    socklen_t addrlen = 0;

    pdev = malloc(sizeof(struct proxy_dev));

    if (!pdev) {
        perror("proxy_alloc_dev(malloc)");
        return NULL;
    }

    pdev->rm_addr = malloc(sizeof(*pdev->rm_addr));
    addrlen = sizeof(*pdev->rm_addr);

    if (getpeername(connfd, (struct sockaddr *)pdev->rm_addr, &addrlen) == -1) {
        perror("proxy_alloc_dev(getperrname)");
        return NULL;
    }

    pdev->rm_fd = connfd;
    pdev->pid   = getpid();
    pdev->client = false; /* this is a server instance */

    return pdev;
}

void proxy_destroy(struct proxy_dev *pdev) {
    free(pdev->nl_src_addr);
    free(pdev->nl_dst_addr);
    free(pdev->rm_addr);
    free(pdev->us_addr);
    free(pdev->rm_ipaddr);
    free(pdev->remote_dev_name);
    free(pdev->dummy_dev_name);
    free(pdev);
}

void proxy_client(struct proxy_dev *pdev)
{
    pdev->pid = getpid(); /* make sure you have the right pid */
    proxy_setup_signals();
    printf("proxy_client: starting connection to server, pid = %d\n", pdev->pid);

    if (proxy_setup_unixsoc(pdev) == -1) {
        printf("proxy_client: failed to setup control socket\n");
        goto err;
    }

    /* setup a netlink connection with the kernel driver, rest is
     * pointless if we can't connect with the kernel driver */
    if (netlink_setup(pdev) == -1) {
        printf("proxy_client: failed to setup netlink socket!\n");
        goto err;
    }

    /* connect to the server */
    if (conn_server(pdev) == -1) {
        printf("proxy_client: failed to connect to the server!\n");
        goto err;
    }

    /* send a device registration request to the server */
    if (conn_send_dev_reg(pdev) == -1) {
        printf("proxy_client: failed to register device on the server!\n");
        goto err;
    }

    /* start loop that will forward all file operations */
    if (proxy_loop(pdev) == -1) {
        printf("proxy_client: loop broken\n");
    }

    if (netlink_unregister_dev(pdev) == -1) {
        printf("proxy_client: failed to unregister device!\n");
    }

err:
    proxy_destroy(pdev);
    return;
}

void proxy_server(int connfd)
{
    struct proxy_dev *pdev = NULL;

    proxy_setup_signals();

    if ((pdev = proxy_alloc_dev(connfd)) == NULL) {
        printf("proxy_server: failed to allocate proxy_dev\n");
        return;
    }
    printf("proxy_server: starting serving client, pid = %d\n", pdev->pid);

    if (proxy_setup_unixsoc(pdev) == -1) {
        printf("proxy_client: failed to setup control socket\n");
        return;
    }

    /* setup a netlink connection with the kernel driver, rest is pointless
     * if we can't connect to the kernel driver */
    if (netlink_setup(pdev) == -1) {
        printf("proxy_server: failed to setup netlink socket!\n");
        return;
    }

    /* connect to the client */
    if (conn_client(pdev) == -1) {
        printf("proxy_server: failed to connect to the server!\n");
        return;
    }

    /* receive a device registration request */
    if (conn_recv_dev_reg(pdev) == -1) {
        printf("proxy_server: failed to register device on the server!\n");
        return;
    }

    /* start loop that will forward all file operations */
    if (proxy_loop(pdev) == -1) {
        printf("proxy_server: loop broken\n");
    }

    if (netlink_unregister_dev(pdev) == -1) {
        printf("proxy_server: failed to unregister device!\n");
    }

    proxy_destroy(pdev);
    /* close the socket completely */
    close(connfd);
    return;
}

int proxy_loop(struct proxy_dev *pdev)
{
    int maxfd, nready;
    fd_set rset;

    debug("starting serving connections");

    /* Read message from kernel */
    while (1) {
        FD_ZERO(&rset); /* clear all file descriptors */
        FD_SET(pdev->us_fd[0], &rset); /* add unix socket */
        FD_SET(pdev->nl_fd, &rset); /* add netlink socket */
        FD_SET(pdev->rm_fd, &rset); /* add remote socket */
        maxfd = max(pdev->nl_fd, pdev->rm_fd);
        maxfd = max(maxfd, pdev->us_fd[0]);
        maxfd++; /* this is a count of how far the sockets go */

        if ((nready = select(maxfd, /* max number of file descriptors */
                            &rset,  /* read file descriptors */
                            NULL,   /* no write fd */
                            NULL,   /* no exception fd */
                            NULL)   /* no timeout */
                            ) == -1 ) {
            perror("proxy_loop(select)");
            debug("errno = %d", errno);
            return -1; /* failure */
        }

        if (FD_ISSET(pdev->us_fd[0], &rset)) {
            /* message from control unix socket */
            if (proxy_control(pdev) == -1) {
                return -1;
            }
            break;
        }

        if (FD_ISSET(pdev->nl_fd, &rset)) {
            if (proxy_handle_netlink(pdev) == -1) {
                return -1;
            }
        }

        if (FD_ISSET(pdev->rm_fd, &rset)) {
            if (proxy_handle_remote(pdev) == -1) {
                return -1;
            }
        }
    }

    return 0; /* success */
}

int proxy_control(struct proxy_dev *pdev)
{
    printf("proxy_control: not yet implemented\n");
    return -1;
}

int proxy_handle_remote(struct proxy_dev *pdev)
{
    struct netdev_header *ndhead = NULL;
    int rvalue , error;
    socklen_t len;

    rvalue = getsockopt(pdev->rm_fd, SOL_SOCKET, SO_ERROR, &error, &len);
    if (rvalue) {
        printf("proxy_handle_remote: connection lost\n");
        return -1;
    }

    ndhead = conn_recv(pdev);
    if (!ndhead) {
        printf("proxy_handle_remote: failed to receive message\n");
        return -1;
    }


    if (ndhead->msgtype > MSGT_FO_START &&
        ndhead->msgtype < MSGT_FO_END) {
        /*printf("proxy_handle_remote:  FILE OPERATION: %d\n",
                ndhead->msgtype);
        */

        netlink_send_nlh(pdev, (struct nlmsghdr *)ndhead->payload);
    } else {
        printf("proxy_handle_remote: unknown message type: %d\n",
                ndhead->msgtype);
        free(ndhead);
        return -1; /* failure */
    }

    free(ndhead->payload);
    free(ndhead);
    return 0;
}

/* at the moment the only thing the kernel will send are file
 * operations so I will ignore other possibilities */
int proxy_handle_netlink(struct proxy_dev *pdev)
{
    struct nlmsghdr *nlh = NULL;
    struct netdev_header *ndhead = NULL;
    int rvalue = -1;

    nlh = netlink_recv(pdev);

    if (!nlh) {
        printf("proxy_handle_netlink: failed to receive message\n");
        goto err;
    }

    if (nlh->nlmsg_type > MSGT_FO_START &&
        nlh->nlmsg_type < MSGT_FO_END) {
        /*printf("proxy_handle_netlink: FILE OPERATION: %d\n",
                nlh->nlmsg_type);
        */

        ndhead = malloc(sizeof(*ndhead));
        if (!ndhead) {
            perror("proxy_handle_netlink(malloc)");
            goto err;
        }
        ndhead->msgtype = nlh->nlmsg_type;
        ndhead->size = nlh->nlmsg_len;
        ndhead->payload = nlh;

        conn_send(pdev, ndhead);
        free(ndhead);
    } else {
        printf("proxy_handle_netlink: unknown message type: %d\n",
                nlh->nlmsg_type);
        goto err;
    }

    /* if error or this message says it wants a response */

    rvalue = 0; /* success */
err:
    free(nlh);
    return rvalue;
}

void proxy_sig_hup(int signo)
{

    printf("sig_hup: received signal in process: %d\n", getpid());

    /*
    if (proxy_unregister_device()) {
        printf("sig_hup: failed to send unregister message\n");
    }
    */

    return;
}
<file_sep>/kernel/fo_access.h
#ifndef _FO_ACCESS_H
#define _FO_ACCESS_H

#include <linux/kfifo.h>

/* fo_access represents one process with one open file descriptor */
struct fo_access {
    int access_id; /* identification number of process */
    struct netdev_data *nddata; /* netdev device opened by this process */
    struct file *filp; /* file descriptor */
    struct kfifo fo_queue; /* queue for file operations */
    struct rw_semaphore sem; /* necessary since threads can use this */
    struct hlist_node hnode; /* to use with hastable */
};

struct fo_access * fo_acc_start(struct netdev_data *nddata, int pid);
void fo_acc_destroy(struct fo_access *acc);
int fo_acc_free_queue(struct fo_access *acc);
struct fo_req * fo_acc_foreq_find(struct fo_access *acc, int seq);
int fo_acc_foreq_add(struct fo_access *acc, struct fo_req *req);

#endif /* _FO_ACCESS_H */
<file_sep>/daemon/netprotocol.h
#ifndef _NETPROTOCL_H
#define _NETPROTOCL_H

struct netdev_header {
    int msgtype;
    size_t size;
    void *payload;
};

#endif
<file_sep>/kernel/fo_comm.h
#ifndef _FO_COMM_H
#define _FO_COMM_H

#include <linux/skbuff.h>
#include <linux/netlink.h>  /* for netlink sockets */
#include <linux/fs.h> /* fl_owner_t */
#include <linux/completion.h>

#include "netdevmgm.h"
#include "fo_access.h"

/* necessary since netdevmgm.h and fo_comm.h reference each other */
struct netdev_data;
/* file structure for a queue of operations of dummy device, those will have
 * to be allocated fast and from a large pool since there will be a lot */
struct fo_req {
    long        seq;        /* for synchronizing netlink messages     */
    short       msgtype;    /* type of file operation                 */
    int         access_id;  /* identification of opened file */
    void       *args;       /* place for s_fo_OPERATION structures    */
    void       *data;       /* place for fo payload */
    size_t      size;       /* size of the s_fo_OPERATION structure   */
    size_t      data_size;
    int         rvalue;     /* informs about success of failure of fo */
    struct completion comp; /* completion to release once reply arrives */
};

int fo_send(short msgtype, struct fo_access *acc, void *args, size_t size, void *data, size_t data_size);
int fo_recv(void *data);
int fo_complete(struct fo_access *acc, struct nlmsghdr *nlh, struct sk_buff *skb);
int fo_execute(struct fo_access *acc, struct nlmsghdr *nlh, struct sk_buff *skb);
void * fo_serialize(struct fo_req *req, size_t *bufflen);
struct fo_req * fo_deserialize_toreq(struct fo_req *req, void *data);
struct fo_req * fo_deserialize(struct fo_access *acc, void *data);

#endif /* _FO_COMM_H */
<file_sep>/kernel/fo_send.c
#include "fo_send.h"
#include "fo_comm.h"
#include "fo_struct.h"
#include "protocol.h"
#include "dbg.h"

/* functions for sending and receiving file operations */
loff_t ndfo_send_llseek(struct file *filp, loff_t offset, int whence)
{
    loff_t rvalue = 0;
    struct s_fo_llseek args = {
        .offset = offset,
        .whence = whence,
        .rvalue = -EIO
    };

    rvalue = fo_send(MSGT_FO_LLSEEK,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
ssize_t ndfo_send_read(struct file *filp, char __user *data, size_t size, loff_t *offset)
{
    ssize_t rvalue = 0;
    struct s_fo_read args = {
        .size = size,
        .offset = *offset,
        .rvalue = -EIO
    };
    if (size >= NETDEV_MESSAGE_LIMIT) {
        printk(KERN_ERR "ndfo_send_read: buffor too big for message\n");
        return -EINVAL;
    }
    args.data = kmalloc(size, GFP_KERNEL);
    if (!args.data) {
        printk(KERN_ERR "ndfo_send_read: failed to allocate args.data\n");
        return -ENOMEM;
    }

    rvalue = fo_send(MSGT_FO_READ,
                    filp->private_data,
                    &args, sizeof(args),
                    args.data, 0);

    if (rvalue < 0) {
        return rvalue;
    }

    *offset = args.offset;
    rvalue = copy_to_user(data, args.data, args.rvalue);
    if (rvalue > 0) {
        printk(KERN_ERR "ndfo_send_read: failed to copy to user\n");
        return -1;
    }
    kfree(args.data);
    return args.rvalue;
}
ssize_t ndfo_send_write(struct file *filp, const char __user *data, size_t size, loff_t *offset)
{
    size_t rvalue = 0;
    struct s_fo_write args = {
        .size = size,
        .offset = *offset,
        .rvalue = -EIO
    };
    if (size >= NETDEV_MESSAGE_LIMIT) {
        printk(KERN_ERR "ndfo_send_write: buffor too big for message\n");
        return -EINVAL;
    }
    args.data = kmalloc(size, GFP_KERNEL);
    if (!args.data) {
        printk(KERN_ERR "ndfo_send_write: failed to allocate args.data\n");
        return -ENOMEM;
    }

    *offset = args.offset;
    rvalue = copy_from_user(args.data, data, size);
    if (rvalue > 0) {
        printk(KERN_ERR "ndfo_send_write: failed to copy from user\n");
        return -EIO;
    }

    rvalue = fo_send(MSGT_FO_WRITE,
                    filp->private_data,
                    &args, sizeof(args),
                    args.data, size);

    if (rvalue < 0) {
        return rvalue;
    }
    kfree(args.data);
    return args.rvalue;
}
size_t ndfo_send_aio_read(struct kiocb *a, const struct iovec *b, unsigned long c, loff_t offset)
{
    return -EIO;
}
ssize_t ndfo_send_aio_write(struct kiocb *a, const struct iovec *b, unsigned long c, loff_t d)
{
    return -EIO;
}
unsigned int ndfo_send_poll(struct file *filp, struct poll_table_struct *wait)
{
    return -EIO;
}
long ndfo_send_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    long rvalue = 0;
    struct s_fo_unlocked_ioctl args = {
        .cmd = cmd,
        .arg = arg,
        .rvalue = -EIO
    };

    rvalue = fo_send(MSGT_FO_UNLOCKED_IOCTL,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
long ndfo_send_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    long rvalue = 0;
    struct s_fo_compat_ioctl args = {
        .cmd = cmd,
        .arg = arg,
        .rvalue = -EIO
    };

    rvalue = fo_send(MSGT_FO_COMPAT_IOCTL,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
int ndfo_send_mmap(struct file *filp, struct vm_area_struct *b)
{
    return -EIO;
}
int ndfo_send_open(struct inode *inode, struct file *filp)
{
    int rvalue = 0;
    struct netdev_data *nddata = NULL;
    struct fo_access *acc = NULL;
    struct s_fo_open args = {
        .rvalue = -EIO
    };

    nddata = ndmgm_find(ndmgm_find_pid(inode->i_cdev->dev));

    /* get the device connected with this file */
    acc = fo_acc_start(nddata, current->pid);
    if (!acc) {
        printk(KERN_ERR "ndfo_send_open: failed to allocate acc\n");
        return -ENOMEM;
    }

    /* set private data for easy access to netdev_data struct */
    filp->private_data = (void*)acc;

    rvalue = fo_send(MSGT_FO_OPEN,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
int ndfo_send_flush(struct file *filp, fl_owner_t id)
{
    int rvalue = 0;
    struct s_fo_flush args = {
        .rvalue = -EIO
    };

    rvalue = fo_send(MSGT_FO_FLUSH,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
int ndfo_send_release(struct inode *a, struct file *filp)
{
    int rvalue = 0;
    struct s_fo_release args = {
        .rvalue = -EIO
    };

    rvalue = fo_send(MSGT_FO_RELEASE,
                    filp->private_data,
                    &args, sizeof(args),
                    NULL, 0);

    if (rvalue < 0) {
        return rvalue;
    }
    return args.rvalue;
}
int ndfo_send_fsync(struct file *filp, loff_t b, loff_t offset, int d)
{
    return -EIO;
}
int ndfo_send_aio_fsync(struct kiocb *a, int b)
{
    return -EIO;
}
int ndfo_send_fasync(int a, struct file *filp, int c)
{
    return -EIO;
}
int ndfo_send_lock(struct file *filp, int b, struct file_lock *c)
{
    return -EIO;
}
ssize_t ndfo_send_sendpage(struct file *filp, struct page *b, int c, size_t d, loff_t *offset, int f)
{
    return -EIO;
}
unsigned long ndfo_send_get_unmapped_area(struct file *filp, unsigned long b, unsigned long c,unsigned long d, unsigned long e)
{
    return -EIO;
}
int ndfo_send_check_flags(int a)
{
    return -EIO;
}
int ndfo_send_flock(struct file *filp, int b, struct file_lock *c)
{
    return -EIO;
}
ssize_t ndfo_send_splice_write(struct pipe_inode_info *a, struct file *filp, loff_t *offset, size_t d, unsigned int e)
{
    return -EIO;
}
ssize_t ndfo_send_splice_read(struct file *filp, loff_t *offset, struct pipe_inode_info *c, size_t d, unsigned int e)
{
    return -EIO;
}
int ndfo_send_setlease(struct file *filp, long b, struct file_lock **c)
{
    return -EIO;
}
long ndfo_send_fallocate(struct file *filp, int b, loff_t offset, loff_t len)
{
    return -EIO;
}
int ndfo_send_show_fdinfo(struct seq_file *a, struct file *filp)
{
    return -EIO;
}
<file_sep>/kernel/netdevmgm.c
#include <linux/slab.h>     /* kmalloc, kzalloc, kfree and so on */
#include <linux/device.h>

#include "netdevmgm.h"
#include "fo.h"
#include "dbg.h"

static DEFINE_HASHTABLE(netdev_htable, NETDEV_HTABLE_DEV_SIZE);
static struct rw_semaphore  netdev_htable_sem;
static struct rw_semaphore  netdev_minor_sem;
static int netdev_max_minor;
static int *netdev_minors_used; /* array with pids to all the devices */

struct netdev_data * ndmgm_alloc_data(
    int nlpid,
    char *name,
    int minor)
{
    struct netdev_data *nddata;
    char pool_name[30];
    sprintf(pool_name, "%s:%s%d", NETDEV_REQ_POOL_NAME, name, minor);

    nddata = kzalloc(sizeof(*nddata), GFP_KERNEL);
    if (!nddata) {
        printk(KERN_ERR "ndmgm_alloc_data: failed to allocate nddata\n");
        return NULL;
    }

    nddata->devname = kzalloc(strlen(name)+1, GFP_KERNEL);
    if (!nddata->devname) {
        printk(KERN_ERR "ndmgm_alloc_data: failed to allocate nddata\n");
        goto free_devname;
    }

    nddata->cdev = kzalloc(sizeof(*nddata->cdev), GFP_KERNEL);
    if (!nddata->cdev) {
        printk(KERN_ERR "ndmgm_alloc_data: failed to allocate nddata->cdev\n");
        goto free_nddata;
    }

    nddata->queue_pool = kmem_cache_create(pool_name,
                                            sizeof(struct fo_req),
    /* no alignment, flags or constructor */ 0, 0, NULL);

    if (!nddata->queue_pool) {
        printk(KERN_ERR "ndmgm_alloc_data: failed to allocate queue_pool\n");
        goto free_cdev;
    }

    sprintf(nddata->devname, "/dev/%s", name);
    hash_init(nddata->foacc_htable);
    init_rwsem(&nddata->sem);
    spin_lock_init(&nddata->nllock);
    atomic_set(&nddata->curseq, 0);
    nddata->nlpid = nlpid;
    nddata->active = true;

    return nddata;
free_cdev:
    kfree(nddata->cdev);
free_devname:
    kfree(nddata->devname);
free_nddata:
    kfree(nddata);
    return NULL;
}

int ndmgm_create_dummy(
    int nlpid,
    char *name)
{
    int err = 0;
    int minor = -1;
    struct netdev_data *nddata = NULL;

    minor = ndmgm_get_minor(nlpid);
    if (minor == -1) {
        printk(KERN_ERR "ndmgm_create_dummy: could not get minor\n");
        return 1;
    }
    debug("creating dummy device: /dev/%s", name);

    if ( (nddata = ndmgm_alloc_data(nlpid, name, minor)) == NULL ) {
        printk(KERN_ERR "ndmgm_create_dummy: failed to create netdev_data\n");
        return 1;
    }

    cdev_init(nddata->cdev, &netdev_fops);
    nddata->cdev->owner = THIS_MODULE;
    nddata->cdev->dev = MKDEV(MAJOR(netdev_devno), minor);
    nddata->dummy = true; /* should be true for a dummy device */

    /* tell the kernel the cdev structure is ready,
     * if it is not do not call cdev_add */
    err = cdev_add(nddata->cdev, nddata->cdev->dev, 1);

    /* Unlikely but might fail */
    if (unlikely(err)) {
        printk(KERN_ERR "Error %d adding netdev\n", err);
        goto free_nddata;
    }

    nddata->device = device_create(netdev_class,
                            NULL,   /* no aprent device           */
                            nddata->cdev->dev, /* major and minor */
                            nddata, /* device data for callback   */
                            "%s", /* defines name of the device */
                            name);

    if (IS_ERR(nddata->device)) {
       err = PTR_ERR(nddata->device);
       printk(KERN_WARNING "[target] Error %d while trying to name %s\n",
                            err, name);
       goto undo_cdev;
    }

    /* add the device to hashtable with all devices since it's ready */
    ndmgm_get(nddata); /* increase count for hashtable */
    hash_add(netdev_htable, &nddata->hnode, (int)nlpid);

    return 0; /* success */
undo_cdev:
    cdev_del(nddata->cdev);
free_nddata:
    ndmgm_free_data(nddata);
    return err;
}

int ndmgm_create_server(
    int nlpid,
    char *name)
{
    struct netdev_data *nddata = NULL;
    char pool_name[30];
    sprintf(pool_name, "%s:%s", NETDEV_REQ_POOL_NAME, name);

    debug("creating server for: %s", name);

    nddata = kzalloc(sizeof(*nddata), GFP_KERNEL);
    if (!nddata) {
        printk(KERN_ERR "ndmgm_create_server: failed to allocate nddata\n");
        return 1;
    }

    nddata->devname = kzalloc(strlen(name)+1, GFP_KERNEL);
    if (!nddata->devname) {
        printk(KERN_ERR "ndmgm_create_server: failed to allocate nddata\n");
        goto free_nddata;
    }

    nddata->queue_pool = kmem_cache_create(pool_name,
                                            sizeof(struct fo_req),
    /* no alignment, flags or constructor */ 0, 0, NULL);

    if (!nddata->queue_pool) {
        printk(KERN_ERR "ndmgm_alloc_data: failed to allocate queue_pool\n");
        goto free_devname;
    }


    memcpy(nddata->devname, name, strlen(name)+1);
    init_rwsem(&nddata->sem);
    spin_lock_init(&nddata->nllock);
    atomic_set(&nddata->curseq, 0);
    nddata->nlpid = nlpid;
    nddata->active = true;
    nddata->dummy = false; /* should be false for a server device */

    /* add the device to hashtable with all devices since it's ready */
    ndmgm_get(nddata); /* increase count for hashtable */
    hash_add(netdev_htable, &nddata->hnode, (int)nlpid);

    /* TODO find cdev and device of the dev we are serving */

    return 0; /* success */
free_devname:
    kfree(nddata->devname);
free_nddata:
    kfree(nddata);
    return 1;
}

void ndmgm_free_data(
    struct netdev_data *nddata)
{
    kmem_cache_destroy(nddata->queue_pool);
    kfree(nddata->cdev);
    kfree(nddata->devname);
    kfree(nddata);
}

/* returns netdev_data based on pid, you have to make sure to
 * increment the reference counter */
struct netdev_data* ndmgm_find(int nlpid)
{
    struct netdev_data *nddata = NULL;
    if (nlpid == -1) {
        printk("ndmgm_find: invalid pid\n");
        return NULL;
    }

    if (down_read_trylock(&netdev_htable_sem)) {
        hash_for_each_possible(netdev_htable, nddata, hnode, (int)nlpid) {
            if ( nddata->nlpid == nlpid ) {
                up_read(&netdev_htable_sem);
                return nddata;
            }
        }
        up_read(&netdev_htable_sem);
    }

    return NULL;
}

struct fo_access * ndmgm_find_acc(
    struct netdev_data *nddata,
    int access_id)
{
    struct fo_access *acc = NULL;

    if (down_read_trylock(&nddata->sem)) {
        hash_for_each_possible(nddata->foacc_htable,
                                acc,
                                hnode,
                                access_id) {
            if ( acc->access_id == access_id ) {
                up_read(&nddata->sem);
                return acc;
            }
        }
        up_read(&nddata->sem);
    }

    return NULL;
}

void ndmgm_put_minor(
    int minor)
{
    int i = 0;
    if (down_write_trylock(&netdev_minor_sem)) {
        netdev_minors_used[minor] = -1;
        if (minor == netdev_max_minor) {
            for (i = minor; i > 0; i--) {
                if (netdev_minors_used[i] != -1) {
                    netdev_max_minor = i;
                }
            }
        }
        up_write(&netdev_minor_sem);
    }
}

int ndmgm_get_minor(
    int pid)
{
    int minor = -1;
    int i = 0;
    if (down_write_trylock(&netdev_minor_sem)) {
        if (netdev_max_minor == NETDEV_MAX_DEVICES) {
            printk("ndmgm_get_minor: device limit reached: %d\n",
                    NETDEV_MAX_DEVICES);
            up_read(&netdev_minor_sem);
            return -1;
        }
        for (i = 0; i <= netdev_max_minor+1; i++) {
            if (netdev_minors_used[i] == -1) {
                netdev_minors_used[i] = pid;
                minor = i;
                break;
            }
        }
        if (minor >= 0) {
            netdev_max_minor = max(netdev_max_minor, minor);
        }
        up_write(&netdev_minor_sem);
    }
    return minor;
}

int ndmgm_find_pid(
    dev_t dev)
{
    int pid = -1;
    if (down_read_trylock(&netdev_minor_sem)) {
        pid = netdev_minors_used[MINOR(dev)];
        up_read(&netdev_minor_sem);
    }
    return pid;
}

/* this function safely increases the current sequence number */
int ndmgm_incseq(
    struct netdev_data *nddata)
{
    int rvalue;
    atomic_inc(&nddata->curseq);
    rvalue = atomic_read(&nddata->curseq);
    return rvalue;
}

int ndmgm_find_destroy(
    int nlpid)
{
    struct netdev_data *nddata = NULL;

    nddata = ndmgm_find(nlpid);
    if (!nddata) {
        printk(KERN_ERR "ndmgm_find_destroy: no such device\n");
        return 1; /* failure */
    }

    if (down_read_trylock(&netdev_htable_sem)) {
        hash_del(&nddata->hnode);
        ndmgm_put(nddata);

        up_read(&netdev_htable_sem);
    }

    return ndmgm_destroy(nddata);
}

int ndmgm_destroy(
    struct netdev_data *nddata)
{
    int rvalue;
    if (!nddata) {
        printk(KERN_ERR "ndmgm_destroy: nddata is NULL\n");
        return 1; /* failure */
    }
    debug("destroying device %s", nddata->devname);

    if (nddata->dummy == true) {
        rvalue = ndmgm_destroy_dummy(nddata);
    } else if (nddata->dummy == false) {
        rvalue = ndmgm_destroy_server(nddata);
    }

    return rvalue;
}

int ndmgm_destroy_allacc(
    struct netdev_data *nddata)
{
    struct fo_access *acc = NULL;
    struct hlist_node *tmp;
    int i = 0;

    if (down_write_trylock(&netdev_htable_sem)) {
        hash_for_each_safe(nddata->foacc_htable, i, tmp, acc, hnode) {
            hash_del(&nddata->hnode); /* delete the element from table */

            /* make sure all pending operations are completed */
            if (fo_acc_free_queue(acc)) {
                printk(KERN_ERR "ndmgm_destroy_allacc: failed to free queue\n");
                return 1; /* failure */
            }

            fo_acc_destroy(acc);
        }
        up_write(&netdev_htable_sem);
        return 0; /* success */
    }
    return 1; /* failure */
}

int ndmgm_destroy_dummy(
    struct netdev_data *nddata)
{
    if (down_write_trylock(&nddata->sem)) {
        nddata->active = false;

        if (ndmgm_destroy_allacc(nddata)) {
            printk(KERN_ERR
                    "ndmgm_destroy_dummy: failed to stop all acc\n");
            up_write(&nddata->sem);
            return 1; /* failure */
        }

        /* should never happen but better test for it */
        if (ndmgm_refs(nddata) > 1) {
            printk(KERN_ERR
                    "ndmgm_destroy_dummy: more than one ref left: %d\n",
                    ndmgm_refs(nddata));
            up_write(&nddata->sem);
            return 1; /* failure */
        }

        device_destroy(netdev_class, nddata->cdev->dev);
        cdev_del(nddata->cdev);

        up_write(&nddata->sem); /* has to be unlocked before kfree */

        ndmgm_put_minor(MINOR(nddata->cdev->dev));
        ndmgm_free_data(nddata); /* finally free netdev_data */
        return 0; /* success */
    }
    debug("failed to destroy netdev_data");
    return 1; /* failure */
}

int ndmgm_destroy_server(
    struct netdev_data *nddata)
{
    if (down_write_trylock(&nddata->sem)) {
        nddata->active = false;

        if (ndmgm_refs(nddata) > 1) {
            printk(KERN_ERR
                    "ndmgm_destroy_server: more than one ref left: %d\n",
                    ndmgm_refs(nddata));
            up_write(&nddata->sem); /* has to be unlocked before kfree */
            return 1; /* failure */
        }

        up_write(&nddata->sem); /* has to be unlocked before kfree */

        kmem_cache_destroy(nddata->queue_pool);
        kfree(nddata->devname);
        kfree(nddata);

        return 0; /* success */
    }
    debug("failed to destroy netdev_data");
    return 1; /* failure */
}

int ndmgm_end(void)
{
    int i = 0;
    struct netdev_data *nddata = NULL;
    struct hlist_node *tmp;
    debug("cleaning devices");

    if (down_write_trylock(&netdev_htable_sem)) {
        hash_for_each_safe(netdev_htable, i, tmp, nddata, hnode) {
            debug("deleting dev pid = %d", nddata->nlpid);
            hash_del(&nddata->hnode); /* delete the element from table */
            ndmgm_put(nddata);

            if (ndmgm_destroy(nddata)) {
                printk(KERN_ERR "netdev_end: failed to destroy nddata\n");
            }
        }
        kfree(netdev_minors_used);
        up_write(&netdev_htable_sem);
        return 0; /* success */
    }
    debug("failed to unlock hashtable");
    return 1; /* failure */
}

void ndmgm_prepare(void)
{
    int i;
    /* create and array for all drivices which will be indexed with
     * minor numbers of those devices */
    netdev_minors_used = kcalloc(NETDEV_MAX_DEVICES,
                                sizeof(*netdev_minors_used),
                                GFP_KERNEL);
    for (i = 0; i < NETDEV_MAX_DEVICES; i++) {
        netdev_minors_used[i] = -1; /* -1 means free minor number */
    }
    netdev_max_minor = 0;

    /* create the hashtable which will store data about created devices
     * and for easy access through pid */
    hash_init(netdev_htable);
}

void ndmgm_get(
    struct netdev_data *nddata)
{
    //debug("dev: %15s, from: %pS", nddata->devname, __builtin_return_address(0));
    atomic_inc(&nddata->users);
}

void ndmgm_put(
    struct netdev_data *nddata)
{
    //debug("dev: %15s, from: %pS", nddata->devname, __builtin_return_address(0));
    atomic_dec(&nddata->users);
}

int ndmgm_refs(
    struct netdev_data *nddata)
{
    return atomic_read(&nddata->users);
}
<file_sep>/kernel/netdev.h
#ifndef _NETDEV_H
#define _NETDEV_H

/* this will be provided by the name variable in netdev_data struct */
#define NETDEV_NAME             "netdev"

/* let the kernel decide the major number */
#define NETDEV_MAJOR            0

/* defines the number which is the power of two which gives the max number
 * of devices this driver will manage and store in the hashtable */
#define NETDEV_HTABLE_DEV_SIZE  5
#define NETDEV_HTABLE_ACC_SIZE  5
#define NETDEV_MAX_DEVICES      32 /* 2 to the power of 5(HTABLE_DEV_SIZE)*/
#define NETDEV_MAX_OPEN_FILES   32 /* 2 to the power of 5(HTABLE_ACC_SIZE) */
#define NETDEV_FO_QUEUE_SIZE    64 /* has to be a power of 2 */
#define NETDEV_REQ_POOL_NAME    "netdev_req_pool" /* for debugging */

#endif /* _NETDEV_H */
<file_sep>/kernel/fo_comm.c
#include <linux/slab.h>
#include <net/netlink.h>
#include <asm/uaccess.h>

#include "protocol.h"
#include "netlink.h"
#include "fo_recv.h"
#include "fo_access.h"
#include "dbg.h"

static void pk(void) {
    printk(KERN_DEBUG "netdev: file operation from:\"%s\", PID: %d - %pS\n",
            current->comm,
            current->pid,
            __builtin_return_address(0));
}

int fo_send(
    short msgtype,
    struct fo_access *acc,
    void *args,
    size_t size,
    void *data,
    size_t data_size)
{
    int rvalue = 0;
    size_t bufflen = 0;
    void *buffer = NULL;
    struct fo_req *req = NULL;

    //pk(); /* for debugging only */

    if (!acc->nddata->active) {
        return -EBUSY;
    }

    /* increment reference counter of netdev_data */
    ndmgm_get(acc->nddata);

    req = kmem_cache_alloc(acc->nddata->queue_pool, GFP_KERNEL);
    if (!req) {
        printk(KERN_ERR "senf_fo: failed to allocate queue_pool\n");
        rvalue = -ENOMEM;
        goto out;
    }

    req->rvalue = -1; /* assume failure, server has to change it to 0 */
    req->msgtype = msgtype;
    req->access_id = acc->access_id;
    req->args = args;
    req->size = size;
    req->data = data;
    req->data_size = data_size;
    init_completion(&req->comp);

    /* add the req to a queue of requests */
    fo_acc_foreq_add(acc, req);

    buffer = fo_serialize(req, &bufflen);
    if (!buffer) {
        printk(KERN_ERR "fo_send: failed to serialize req\n");
        rvalue = -ENODATA;
        goto out;
    }

    if (!(req->seq = ndmgm_incseq(acc->nddata))) {
        printk(KERN_ERR "fo_send: failed to increment curseq\n");
        rvalue = -EBUSY;
        goto out;
    }
    /* send the file operation request */
    rvalue = netlink_send(acc->nddata,
                            req->seq,
                            req->msgtype,
                            NLM_F_REQUEST,
                            buffer,
                            bufflen);

    if (rvalue < 0) {
        printk(KERN_ERR "fo_send: failed to send file operation\n");
        rvalue = -ECANCELED;
        goto out;
    }

    /* wait for completion, it will be signaled once a reply is received */
    wait_for_completion(&req->comp);

    rvalue = req->rvalue;
out:
    kfree(buffer);
    ndmgm_put(acc->nddata);
    kmem_cache_free(acc->nddata->queue_pool, req);
    if (msgtype == MSGT_FO_RELEASE) {
        fo_acc_destroy(acc);
    }
    return rvalue;
}

/* this function will be executed as a new thread with kthread_run */
int fo_recv(
    void *data)
{
    struct sk_buff *skb = data;
    struct netdev_data *nddata = NULL;
    struct fo_access *acc = NULL;
    struct nlmsghdr *nlh = NULL;
    int msgtype = 0;
    int rvalue = 0; /* success */
    int pid = 0;

    nlh = nlmsg_hdr(skb);
    msgtype = nlh->nlmsg_type;

    nddata = ndmgm_find(nlh->nlmsg_pid);
    if (IS_ERR(nddata)) {
        printk(KERN_ERR "fo_recv: failed to find device for pid = %d\n",
                nlh->nlmsg_pid);
        do_exit(-ENODEV); /* failure */
    }
    ndmgm_get(nddata);

    memcpy(&pid, NLMSG_DATA(nlh), sizeof(pid));
    acc = ndmgm_find_acc(nddata, pid);

    if (!acc) {
        if (msgtype != MSGT_FO_OPEN) {
            printk(KERN_ERR "fo_recv: no opened file for pid = %d\n", pid);
            do_exit(-ENFILE);
        }
        acc = fo_acc_start(nddata, pid);
        if (!acc) {
            printk(KERN_ERR "fo_recv: failed to allocate acc\n");
            do_exit(-ENOMEM);
        }
    }

    if (nddata->dummy) {
        rvalue = fo_complete(acc, nlh, skb);
    } else {
        rvalue = fo_execute(acc, nlh, skb);
    }

    /* if open failed we need to free acc */
    if (rvalue == -1 && msgtype == MSGT_FO_OPEN) {
        fo_acc_destroy(acc);
    }

    ndmgm_put(nddata);
    do_exit(rvalue);
}

int fo_complete(
    struct fo_access *acc,
    struct nlmsghdr *nlh,
    struct sk_buff *skb)
{
    struct fo_req *req = NULL;
    //debug("completing, pid = %d, seq = %d", acc->access_id, nlh->nlmsg_seq);

    /* first element in paylod is access ID */
    req = fo_acc_foreq_find(acc, nlh->nlmsg_seq);
    if (!req) {
        printk(KERN_ERR "fo_complete: failed to obtain fo request\n");
        return -1;
    }

    if (!fo_deserialize_toreq(req, NLMSG_DATA(nlh))) {
        printk(KERN_ERR "fo_complete: failed to deserialize req\n");
        return -1;
    }

    complete(&req->comp);
    dev_kfree_skb(skb); /* fo_execute frees skb by sending unicat */
    return 0;
}

int fo_execute(
    struct fo_access *acc,
    struct nlmsghdr *nlh,
    struct sk_buff *skb)
{
    int (*fofun)(struct fo_access*, struct fo_req*) = NULL;
    struct sk_buff *skbtmp = NULL;
    struct fo_req *req = NULL;
    void *buff = NULL;
    size_t bufflen = 0;
    int fonum = 0;
    int rvalue = -ENODATA;
    int msgtype = nlh->nlmsg_type;

    req = fo_deserialize(acc, NLMSG_DATA(nlh));
    if (!req) {
        printk(KERN_ERR "fo_execute: failed to deserialize req\n");
        goto err;
    }
    //debug("executing, pid = %d, seq = %d", acc->access_id, nlh->nlmsg_seq);

    /* get number of file operation for array */
    fonum = msgtype - (MSGT_FO_START+1);
    /* get the correct file operation function from the array */
    fofun = netdev_recv_fops[fonum];
    if (!fofun) {
        printk(KERN_ERR "fo_execute: file operation not implemented\n");
        goto err;
    }

    /* execute the file operation */
    req->rvalue = fofun(acc, req);
    if (req->rvalue == -1) {
        printk(KERN_ERR "fo_execute: file operation failed = %d\n", msgtype);
        goto err;
    }

    /* send back the result to the server in userspace */
    buff = fo_serialize(req, &bufflen);
    if (!buff) {
        printk(KERN_ERR "fo_execute: failed to serialize req\n");
        goto err;
    }

    if (bufflen == nlmsg_len(nlh)) {
        memcpy(nlmsg_data(nlh), buff, bufflen);
    } else {
        /* expand skb and put in data, possibly split into parts */
        skbtmp = netlink_pack_skb(nlh, buff, bufflen);
        if (!skb) {
            printk(KERN_ERR "fo_execute: failed to put data into skb\n");
            goto err;
        }
        dev_kfree_skb(skb);
        skb = skbtmp;
    }

    rvalue = 0; /* success */
err:
    /* rvalue is -1 so sending it back untouched will mean failure */
    netlink_send_skb(acc->nddata, skb);
    kfree(req->args);
    kfree(req->data);
    kmem_cache_free(acc->nddata->queue_pool, req);
    kfree(buff);
    if (msgtype == MSGT_FO_RELEASE) {
        fo_acc_destroy(acc);
    }
    return rvalue;
}

/* file operation structure:
 * 0 - int    - access ID (HAS TO BE FIRST)
 * 1 - int    - return value of operation
 * 2 - size_t - size of args structure
 * 3 - size_t - size of data/payload
 * 4 - args   - struct with fo args
 * 5 - data   - payload
 */
void * fo_serialize(
    struct fo_req *req,
    size_t *bufflen)
{
    void *data = NULL;
    size_t size = 0;
    *bufflen =  sizeof(req->access_id) +
                sizeof(req->rvalue) +
                sizeof(req->size) +
                sizeof(req->data_size) +
                req->size +
                req->data_size;

    data = kzalloc(*bufflen, GFP_KERNEL);
    if (!data) {
        printk(KERN_ERR "fo_serialize: failed to allocate data\n");
        return NULL; /* failure */
    }

    memcpy(data + size, &req->access_id, sizeof(req->access_id));
    size += sizeof(req->access_id);
    memcpy(data + size, &req->rvalue,    sizeof(req->rvalue));
    size += sizeof(req->rvalue);
    memcpy(data + size, &req->size,      sizeof(req->size));
    size += sizeof(req->size);
    memcpy(data + size, &req->data_size, sizeof(req->data_size));
    size += sizeof(req->data_size);
    if (req->args == NULL) {
        return data;
    }
    memcpy(data + size, req->args,       req->size);
    size += req->size;
    if (req->data_size == 0) {
        return data;
    }
    memcpy(data + size, req->data,       req->data_size);


    return data;
}

struct fo_req * fo_deserialize_toreq(
    struct fo_req *req,
    void *data)
{
    size_t size = 0;

    if (!req) {
        printk(KERN_ERR "fo_deserialize_toreq: req is NULL\n");
        return NULL; /* failure */
    }

    /* get all the data */
    memcpy(&req->access_id, data + size, sizeof(req->access_id));
    size += sizeof(req->access_id);
    memcpy(&req->rvalue,    data + size, sizeof(req->rvalue));
    size += sizeof(req->rvalue);
    memcpy(&req->size,      data + size, sizeof(req->size));
    size += sizeof(req->size);
    memcpy(&req->data_size, data + size, sizeof(req->data_size));
    if (req->size == 0) {
        return req;
    }
    size += sizeof(req->data_size);
    if (!req->args) {
        req->args = kzalloc(req->size, GFP_KERNEL);
        if (!req->args) {
            printk(KERN_ERR "fo_deserialize_toreq: failed to allocate args\n");
            return NULL;
        }
    }
    memcpy(req->args,       data + size, req->size);
    if (req->data_size == 0) {
        return req;
    }
    size += req->size;
    if (!req->data) {
        req->data = kzalloc(req->data_size, GFP_KERNEL);
        if (!req->data) {
            printk(KERN_ERR "fo_deserialize: failed to allocate data\n");
            return NULL;
        }
    }
    memcpy(req->data,       data + size, req->data_size);

    return req;
}

struct fo_req * fo_deserialize(
    struct fo_access *acc,
    void *data)
{
    struct fo_req *req = NULL;
    size_t size = 0;

    req = kmem_cache_alloc(acc->nddata->queue_pool, GFP_KERNEL);
    if (!req) {
        printk(KERN_ERR "fo_deserialize: failed to allocate req\n");
        return NULL; /* failure */
    }
    req->args = NULL;
    req->data = NULL;

    /* get all the data */
    memcpy(&req->access_id, data + size, sizeof(req->access_id));
    size += sizeof(req->access_id);
    memcpy(&req->rvalue,    data + size, sizeof(req->rvalue));
    size += sizeof(req->rvalue);
    memcpy(&req->size,      data + size, sizeof(req->size));
    size += sizeof(req->size);
    memcpy(&req->data_size, data + size, sizeof(req->data_size));
    if (req->size == 0) {
        return req;
    }
    size += sizeof(req->data_size);
    req->args = kzalloc(req->size, GFP_KERNEL);
    if (!req->args) {
        printk(KERN_ERR "fo_deserialize: failed to allocate args\n");
        goto free_req;
    }
    memcpy(req->args,       data + size, req->size);
    if (req->data_size == 0) {
        return req;
    }
    size += req->size;
    req->data = kzalloc(req->data_size, GFP_KERNEL);
    if (!req->data) {
        printk(KERN_ERR "fo_deserialize: failed to allocate data\n");
        goto free_req;
    }
    memcpy(req->data,       data + size, req->data_size);

    return req;
free_req:
    kfree(req->data);
    kfree(req->args);
    kfree(req);
    return NULL;
}
<file_sep>/kernel/fo_recv.c
#include <linux/fs.h>
#include <linux/fcntl.h>

#include "fo_recv.h"
#include "fo_struct.h"
#include "fo_access.h"
#include "dbg.h"

int ndfo_recv_llseek(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_llseek *args = req->args;

    if (!acc->filp->f_op->llseek) {
        printk(KERN_ERR "ndfo_recv_llseek: operation is NULL\n");
        return -1;
    }
    args->rvalue = acc->filp->f_op->llseek(acc->filp,
                                            args->offset,
                                            args->whence);

    return 0;
}

int ndfo_recv_read(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_read *args = req->args;

    req->data = kzalloc(args->size , GFP_KERNEL);
    req->data_size = args->size;

    args->rvalue = acc->filp->f_op->read(acc->filp,
                                            req->data,
                                            args->size,
                                            &args->offset);

    return 0;
}

int ndfo_recv_write(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_write *args = req->args;

    args->rvalue = acc->filp->f_op->write(acc->filp,
                                            req->data,
                                            args->size,
                                            &args->offset);

    /* don't send written data back */
    kfree(req->data);
    req->data = NULL;
    req->data_size = 0;

    return 0;
}

int ndfo_recv_poll(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_unlocked_ioctl(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_unlocked_ioctl *args = req->args;

    args->rvalue = acc->filp->f_op->unlocked_ioctl(acc->filp,
                                                      args->cmd,
                                                      args->arg);

    return -1;
}

int ndfo_recv_compat_ioctl(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_compat_ioctl *args = req->args;

    args->rvalue = acc->filp->f_op->compat_ioctl(acc->filp,
                                                  args->cmd,
                                                  args->arg);

    return 0;
}

int ndfo_recv_mmap(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_open(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_open *args = req->args;
    int err = 0;
    int flags = O_RDWR | O_LARGEFILE;
    int mode = 0;

    acc->filp = filp_open(acc->nddata->devname, flags, mode);

    if (IS_ERR(acc->filp)) {
        err = PTR_ERR(acc->filp);
        printk(KERN_ERR "ndfo_recv_open_req: err = %d\n", err);
        return -1; /* failure */
    }

    if (!acc->filp->f_op) {
        printk(KERN_ERR "ndfo_recv_open_req: no file operations\n");
        return -1; /* failure */
    }

    args->rvalue = 0;
    return 0; /* success */
}

int ndfo_recv_flush(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_flush *args = req->args;

    if (!acc->filp->f_op->flush) {
        printk(KERN_ERR "ndfo_recv_flush: operation is NULL\n");
        return -1;
    }
    args->rvalue = acc->filp->f_op->flush(acc->filp, NULL);

    return 0;
}

int ndfo_recv_release(struct fo_access *acc, struct fo_req *req) {
    struct s_fo_release *args = req->args;

    args->rvalue = filp_close(acc->filp, NULL);

    return 0;
}

int ndfo_recv_fsync(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_aio_fsync(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_fasync(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_lock(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_check_flags(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_flock(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_splice_write(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_splice_read(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_setlease(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_fallocate(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int ndfo_recv_show_fdinfo(struct fo_access *acc, struct fo_req *req) {
    return -1;
}

int (*netdev_recv_fops[])(struct fo_access *, struct fo_req*) = {
    ndfo_recv_llseek,
    ndfo_recv_read,
    ndfo_recv_write,
    NULL, /* will be hard to implement because of kiocb */
    NULL, /* will be hard to implement because of kiocb */
    NULL, /* useless for devices */
    ndfo_recv_poll,/* NULL assumes it's not blocing */
    ndfo_recv_unlocked_ioctl,
    ndfo_recv_compat_ioctl,
    NULL, /* for mapping device memory to user space */
    ndfo_recv_open,
    ndfo_recv_flush, /* rarely used for devices */
    ndfo_recv_release,
    ndfo_recv_fsync,
    NULL, /* will be hard to implement because of kiocb */
    ndfo_recv_fasync,
    NULL, /* almost never implemented */
    NULL, /* usually not used, for sockets */
    NULL, /* mostly NULL */
    ndfo_recv_check_flags, /* fcntl(F_SETFL...) */
    ndfo_recv_flock,
    NULL, /* needs DMA */
    NULL, /* needs DMA */
    ndfo_recv_setlease,
    ndfo_recv_fallocate,
    ndfo_recv_show_fdinfo
};
<file_sep>/daemon/netlink.c
#include <stdio.h> /* perror */
#include <stdlib.h> /* malloc */

#include "netlink.h"
#include "protocol.h"
#include "netprotocol.h"
#include "conn.h"
#include "debug.h"

int netlink_setup(
    struct proxy_dev *pdev)
{
    int rvalue = 0;

    /* create socket for netlink */
    pdev->nl_fd = socket(PF_NETLINK, SOCK_RAW, NETDEV_PROTOCOL);

    /* check if socket was actually created */
    if (pdev->nl_fd < 0) {
        perror("netlink_setup(socket setup)");
        return -1;
    }

    /* allocate memory fo addreses and zero them out */
    pdev->nl_src_addr = malloc(sizeof(*pdev->nl_src_addr));
    pdev->nl_dst_addr = malloc(sizeof(*pdev->nl_dst_addr));
    memset(pdev->nl_src_addr, 0, sizeof(*pdev->nl_src_addr));
    memset(pdev->nl_dst_addr, 0, sizeof(*pdev->nl_dst_addr));

    /* set up source address: this process, for bind */
    pdev->nl_src_addr->nl_family = AF_NETLINK;
    pdev->nl_src_addr->nl_pid = pdev->pid; /* self pid */
    pdev->nl_src_addr->nl_groups = 0; /* unicast */

    /* set up destination address: kernel, for later */
    pdev->nl_dst_addr->nl_family = AF_NETLINK;
    pdev->nl_dst_addr->nl_pid = 0; /* For Linux Kernel */
    pdev->nl_dst_addr->nl_groups = 0; /* unicast */

    rvalue = bind(pdev->nl_fd,
                (struct sockaddr*)pdev->nl_src_addr,
                sizeof(*pdev->nl_src_addr));

    if (rvalue < 0) {
        perror("netlink_setup(binding socket)");
        return -1; /* failure */
    }

    printf("netlink_setup: complete\n");
    /* connect is not needed since we are not using TCP but a raw socket */
    return 0; /* success */
}

int netlink_send(
    struct proxy_dev *pdev,
    struct nlmsghdr *nlh)
{
    struct msghdr msgh = {0};
    struct iovec iov = {0};

    if (!nlh) {
        printf("netlink_send: nlh is NULL\n");
        return -1; /* failure */
    }

    /*
    debug("type: %d, pid: %d, size: %d",
            nlh->nlmsg_type, nlh->nlmsg_pid, nlh->nlmsg_len);
    */

    /* netlink header is our payload */
    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;

    msgh.msg_iov = &iov; /* this normally is an array of */
    msgh.msg_iovlen = 1; /* TODO, we won't always use just one */

    if (sendall(pdev->nl_fd, &msgh, nlh->nlmsg_len) == -1) {
        printf("netlink_send: failed to send message\n");
    }

    return 0; /* success */
}

int netlink_send_msg(
    struct proxy_dev *pdev,
    void *buff,
    size_t bsize,
    int msgtype,
    int flags)
{
    struct nlmsghdr *nlh = NULL;

    nlh = malloc(NLMSG_SPACE(bsize));
    if (!nlh) {
        printf("netlink_send_msg: failed to allocate nlh\n");
        return -1; /* failure */
    }
    memset(nlh, 0, NLMSG_SPACE(bsize));

    nlh->nlmsg_len = NLMSG_SPACE(bsize); /* struct size + payload */
    nlh->nlmsg_pid = pdev->pid;
    nlh->nlmsg_type = msgtype;
    /* delivery confirmation with NLMSG_ERROR and error field set to 0 */
    nlh->nlmsg_flags = flags | NLM_F_ACK;

    /* TODO check if we can't just assign the pointer */
    if (buff) {
        memcpy(NLMSG_DATA(nlh), buff, bsize);
    }

    if (netlink_send_nlh(pdev, nlh) == -1) {
        printf("netlink_send_msg: failed to send message\n");
        return -1; /* failure */
    }

    free(nlh);
    return 0; /* success */
}

int netlink_send_nlh(
    struct proxy_dev *pdev,
    struct nlmsghdr *nlh)
{
    struct nlmsgerr *msgerr = NULL;
    int rvalue = -1;
    nlh->nlmsg_pid = pdev->pid;

    /* send everything */
    if (netlink_send(pdev, nlh) == -1) {
        printf("netlink_send_nlh: failed to send message\n");
        return -1; /* failure */
    }
    /* get confirmation of delivery */
    if ((nlh = netlink_recv(pdev)) == NULL) {
        printf("netlink_send_nlh: failed to receive confirmation\n");
        return -1; /* failure */
    }

    /* check confirmation */
    if ( nlh->nlmsg_type == NLMSG_ERROR ) {
        msgerr = ((struct nlmsgerr*)NLMSG_DATA(nlh));
        if (msgerr->error != 0) {
            debug("delivery failure, msgerr->error = %d", msgerr->error);
            goto err;
        }
    } else {
        printf("netlink_send: next message was not confirmation!\n");
        goto err;
    }

    rvalue = 0; /* success */
err:
    free(nlh);
    return rvalue;
}

struct nlmsghdr * netlink_recv(
    struct proxy_dev *pdev)
{
    struct nlmsghdr *nlh = NULL;
    struct msghdr msgh = {0};
    struct iovec iov = {0};
    size_t bufflen = sizeof(*nlh);
    void *buffer = malloc(bufflen);

    /* netlink header is our payload */
    iov.iov_base = buffer;
    iov.iov_len = bufflen;
    msgh.msg_iov = &iov; /* this normally is an array of */
    msgh.msg_iovlen = 1;
    msgh.msg_flags = MSG_PEEK; /* first we need the full msg size */

    /* the minimum is the size of the nlmsghdr alone */
    bufflen = recvall(pdev->nl_fd, &msgh, bufflen);
    if (bufflen == -1) {
        printf("netlink_recv: failed to read message\n");
        goto err;
    }

    nlh = buffer;
    /*
    debug("type: %d, pid: %d, size: %d",
            nlh->nlmsg_type, nlh->nlmsg_pid, nlh->nlmsg_len);
    */

    /* increase the buffer size if needbe */
    bufflen = nlh->nlmsg_len;
    buffer = realloc(buffer, bufflen);
    iov.iov_base = buffer;
    iov.iov_len = bufflen;
    msgh.msg_flags = 0; /* get rid of MSG_PEEK */

    /* get the rest of message */
    bufflen = recvall(pdev->nl_fd, &msgh, bufflen);
    if (bufflen == -1) {
        printf("netlink_recv: failed to read message\n");
        goto err;
    }

    nlh = buffer;
    return nlh;
err:
    free(buffer);
    return NULL;
}

int netlink_reg_dummy_dev(
    struct proxy_dev *pdev)
{
    return netlink_send_msg(pdev,
                        pdev->dummy_dev_name,
                        strlen(pdev->dummy_dev_name)+1,
                        MSGT_CONTROL_REG_DUMMY,
                        NLM_F_REQUEST);
}

int netlink_reg_remote_dev(
    struct proxy_dev *pdev)
{
    return netlink_send_msg(pdev,
                        pdev->remote_dev_name,
                        strlen(pdev->remote_dev_name)+1,
                        MSGT_CONTROL_REG_SERVER,
                        NLM_F_REQUEST);
}

int netlink_unregister_dev(
    struct proxy_dev *pdev)
{
    return netlink_send_msg(pdev,
                        NULL, /* no payload */
                        0, /* 0 payload size */
                        MSGT_CONTROL_UNREGISTER,
                        NLM_F_REQUEST);
}
<file_sep>/kernel/dbg.h
#ifndef _DBG_H
#define _DBG_H


#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) printk(KERN_DEBUG "NETDEV_DEBUG: %20s:%4d:\t\t" M "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif

#endif /* _DBG_H */
<file_sep>/include/fo_struct.h
#ifndef _FO_STRUCT_H
#define _FO_STRUCT_H

#include <linux/types.h>
#include <linux/fs.h>

/* structures used to pass file operation arguments to sending function */
struct s_fo_llseek {
    loff_t offset;
    int whence;
    loff_t rvalue;
};
struct s_fo_read {
    char __user *data;
    size_t size;
    loff_t offset;
    ssize_t rvalue;
};
struct s_fo_write {
    char __user *data;
    size_t size;
    loff_t offset;
    ssize_t rvalue;
};
struct s_fo_aio_read {
    struct kiocb *a;
    const struct iovec *b;
    unsigned long c;
    loff_t offset;
    ssize_t rvalue;
};
struct s_fo_aio_write {
    struct kiocb *a;
    struct iovec *b;
    unsigned long c;
    loff_t offset;
    ssize_t rvalue;
};
struct s_dev_fo_poll {
    struct poll_table_struct *wait;
    unsigned long rvalue;
};
struct s_fo_unlocked_ioctl {
    unsigned int cmd;
    unsigned long arg;
    long rvalue;
};
struct s_fo_compat_ioctl {
    unsigned int cmd;
    unsigned long arg;
    long rvalue;
};
struct s_fo_mmap {
    struct vm_area_struct *b;
    int rvalue;
};
struct s_fo_open {
    struct inode *inode;
    int rvalue;
};
struct s_fo_flush {
    int rvalue;
};
struct s_fo_release {
    int rvalue;
};
struct s_fo_fsync {
    loff_t b;
    loff_t c;
    int d;
    int rvalue;
};
struct s_fo_aio_fsync {
    struct kiocb *a;
    int b;
    int rvalue;
};
struct s_fo_fasync {
    int a;
    int c;
    int rvalue;
};
struct s_fo_lock {
    int b;
    int rvalue;
};
struct s_fo_sendpage {
    struct page *b;
    int c;
    size_t size;
    loff_t *offset;
    int f;
    ssize_t rvalue;
};
struct s_fo_get_unmapped_area{
    unsigned long b;
    unsigned long c;
    unsigned long d;
    unsigned long e;
    unsigned long rvalue;
};
struct s_fo_check_flags {
    int a;
    int rvalue;
};
struct s_fo_flock {
    int b;
    int rvalue;
};
struct s_fo_splice_write {
    struct pipe_inode_info *a;
    loff_t *offset;
    size_t size;
    unsigned int e;
    ssize_t rvalue;
};
struct s_fo_splice_read {
    loff_t *b;
    struct pipe_inode_info *c;
    size_t size;
    unsigned int e;
    ssize_t rvalue;
};
struct s_fo_setlease {
    long b;
    int rvalue;
};
struct s_fo_fallocate {
    int b;
    loff_t offset;
    loff_t len;
    long rvalue;
};
struct s_fo_show_fdinfo {
    struct seq_file *a;
    int rvalue;
};

#endif /* _FO_STRUCT_H */
<file_sep>/daemon/signal.h
#ifndef _SIGNAL_H
#define _SIGNAL_H

extern typedef void    Sigfunc(int);   /* for signal handlers */

Sigfunc * signal(int signo, Sigfunc *func);

#endif
<file_sep>/kernel/fo.c
#include <linux/module.h>

#include "fo.h"
#include "fo_send.h"

/* TODO this struct will have to be generated dynamically based on the
 * operations supported by the device on the other end */
struct file_operations netdev_fops = {
    .owner             = THIS_MODULE,
    .llseek            = ndfo_send_llseek,
    .read              = ndfo_send_read,
    .write             = ndfo_send_write,
    .aio_read          = NULL, /* will be hard to implement because of kiocb */
    .aio_write         = NULL, /* will be hard to implement because of kiocb */
    .readdir           = NULL, /* useless for devices */
    .poll              = ndfo_send_poll,/* NULL assumes it's not blocing */
    .unlocked_ioctl    = ndfo_send_unlocked_ioctl,
    .compat_ioctl      = ndfo_send_compat_ioctl,
    .mmap              = NULL, /* for mapping device memory to user space */
    .open              = ndfo_send_open,
    .flush             = NULL, /* rarely used for devices */
    .release           = ndfo_send_release,
    .fsync             = ndfo_send_fsync,
    .aio_fsync         = NULL, /* will be hard to implement because of kiocb */
    .fasync            = ndfo_send_fasync,
    .lock              = NULL, /* almost never implemented */
    .sendpage          = NULL, /* usually not used, for sockets */
    .get_unmapped_area = NULL, /* mostly NULL */
    .check_flags       = ndfo_send_check_flags, /* fcntl(F_SETFL...) */
    .flock             = ndfo_send_flock,
    .splice_write      = NULL, /* needs DMA */
    .splice_read       = NULL, /* needs DMA */
    .setlease          = ndfo_send_setlease,
    .fallocate         = ndfo_send_fallocate,
    .show_fdinfo       = ndfo_send_show_fdinfo
};
<file_sep>/daemon/conn.c
#include <stdio.h> /* perror */
#include <stdlib.h> /* malloc */
#include <arpa/inet.h> /* inet_pton */

#include "conn.h"
#include "protocol.h"
#include "netlink.h"
#include "debug.h"

int conn_send(struct proxy_dev *pdev, struct netdev_header *ndhead) {
    struct msghdr msgh = {0};
    struct iovec iov = {0};
    size_t size = sizeof(*ndhead) + ndhead->size;
    void *buff = NULL;

    //debug("sending bytes = %zu", ndhead->size);

    buff = malloc(size);
    if (!buff) {
        perror("conn_send(malloc)");
        return -1;
    }
    msgh.msg_iov = &iov;
    msgh.msg_iovlen = 1;
    iov.iov_len = size;
    iov.iov_base = buff;

    memcpy(iov.iov_base, ndhead, sizeof(*ndhead));
    memcpy(iov.iov_base + sizeof(*ndhead), ndhead->payload, ndhead->size);

    if (sendall(pdev->rm_fd, &msgh, size) == -1) {
        printf("conn_send: failed to send data\n");
        free(buff);
        return -1; /* failure */
    }

    free(buff);
    return 0; /* success */
}

struct netdev_header * conn_recv(struct proxy_dev *pdev) {
    struct netdev_header *ndhead = NULL;
    struct msghdr msgh = {0};
    struct iovec iov = {0};
    int ndsize = sizeof(*ndhead);

    ndhead = malloc(ndsize);

    msgh.msg_iov = &iov;
    msgh.msg_iovlen = 1;
    iov.iov_base = (void *)ndhead;
    iov.iov_len = ndsize;

    if (recvall(pdev->rm_fd, &msgh, ndsize) == -1) {
        printf("conn_recv: failed to read message header\n");
        return NULL;
    }

    //debug("paylod size = %zu", ndhead->size);

    ndhead->payload = malloc(ndhead->size);
    iov.iov_base = ndhead->payload;
    iov.iov_len = ndhead->size;

    if (recvall(pdev->rm_fd, &msgh, ndhead->size) == -1) {
        printf("conn_recv: failed to read rest of the message\n");
        free(ndhead);
        return NULL;
    }

    return ndhead;
}

int conn_server(struct proxy_dev *pdev) {
    struct netdev_header *ndhead;
    int version = NETDEV_PROTOCOL_VERSION;
    int rvalue = -1;

    /* create TCP/IP socket for connection with server */
    pdev->rm_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (pdev->rm_fd == -1) {
        perror("conn_server(socket)");
        return -1;
    }

    pdev->rm_addr = malloc(sizeof(*pdev->rm_addr));
    if (!pdev->rm_addr) {
        perror("conn_server(malloc)");
        return -1;
    }

    pdev->rm_addr->sin_family = AF_INET;
    pdev->rm_addr->sin_port = htons(pdev->rm_portaddr);
    inet_pton(AF_INET, (void*)pdev->rm_ipaddr, &pdev->rm_addr->sin_addr);

    printf("conn_server: connecting to: %s:%d\n",
            pdev->rm_ipaddr, pdev->rm_portaddr);

    rvalue = connect(pdev->rm_fd,
                    (struct sockaddr*)pdev->rm_addr,
                    sizeof(*pdev->rm_addr));
    if (rvalue == -1) {
        perror("conn_server(connect)");
        return -1;
    }

    ndhead = malloc(sizeof(*ndhead));
    if (!ndhead) {
        perror("conn_server(malloc)");
        return -1;
    }
    ndhead->msgtype = MSGT_CONTROL_VERSION;
    ndhead->size    = sizeof(version);
    ndhead->payload = malloc(ndhead->size);
    memcpy(ndhead->payload, &version, ndhead->size);

    /* send version */
    if (conn_send(pdev, ndhead) == -1) {
        printf("conn_server: failed to send version\n");
        goto err;
    }
    free(ndhead->payload);
    free(ndhead);
    /* receive reply */
    ndhead = conn_recv(pdev);

    if (!ndhead) {
        printf("conn_server: failed to receive version reply\n");
        goto err;
    }

    if (ndhead->size != sizeof(int)) {
        printf("conn_server: wrong payload size\n");
        goto err;
    }

    version = *((int*)ndhead->payload);
    if (version != 0) {
        printf("conn_server: wrong version = %d\n", version);
        goto err;
    }

    printf("conn_server: connection successful, version correct = %d\n",
            version);

    rvalue = 0; /* success */
err:
    free(ndhead->payload);
    free(ndhead);
    return rvalue;
}

int conn_client(struct proxy_dev *pdev) {
    struct netdev_header *ndhead;
    int rvalue = -1;
    int version;
    int reply = NETDEV_PROTOCOL_SUCCESS; /* success = 0 */

    ndhead = conn_recv(pdev);

    if (ndhead->msgtype != MSGT_CONTROL_VERSION) {
        printf("conn_client: wrong message type!\n");

    }

    if (ndhead->size != sizeof(int)) {
        printf("conn_client: wrong payload size!\n");
        return -1; /* failure */
    }

    version = *((int*)ndhead->payload);
    printf("conn_client: received version = %d\n", version);
    /* check version number */
    if (version != NETDEV_PROTOCOL_VERSION) {
        printf("conn_client: wrong protocol version = %d\n", version);
        reply = NETDEV_PROTOCOL_VERSION;
    }

    memcpy(ndhead->payload, &reply, sizeof(int));

    if (conn_send(pdev, ndhead) == -1) {
        printf("conn_client: failed to send version reply\n");
        goto err;
    }

    printf("conn_client: protocol version correct = %d\n", version);
    rvalue = reply;
err:
    free(ndhead->payload);
    free(ndhead);
    return rvalue; /* success */
}

int conn_send_dev_reg(struct proxy_dev *pdev) {
    struct netdev_header *ndhead;
    int rvalue = 0;

    ndhead = malloc(sizeof(*ndhead));
    if (!ndhead) {
        perror("conn_send_dev_reg(malloc)");
        return -1;
    }

    ndhead->msgtype = MSGT_CONTROL_REG_SERVER;
    ndhead->size    = strlen(pdev->remote_dev_name)+1; /* plus null */
    ndhead->payload = pdev->remote_dev_name;

    printf("conn_send_dev_reg: sending device name: %s\n",
            pdev->remote_dev_name);
    if (conn_send(pdev, ndhead) == -1) {
        printf("conn_send_dev_reg: failed to send device registration\n");
        free(ndhead);
        return -1;
    }

    free(ndhead);
    ndhead = conn_recv(pdev);

    if (!ndhead) {
        printf("conn_send_dev_reg: failed to receive reply\n");
        return -1;
    }

    rvalue = *((int*)ndhead->payload);

    if (rvalue != NETDEV_PROTOCOL_SUCCESS) {
        printf("conn_send_dev_reg: server failed to register device\n");
        return -1;
    }

    if (netlink_reg_dummy_dev(pdev) == -1) {
        printf("proxy_client: failed to register device on the server!\n");
        return -1;
    }
    printf("conn_send_dev_reg: device registered\n");
    return 0; /* success */
}

int conn_recv_dev_reg(struct proxy_dev *pdev) {
    struct netdev_header *ndhead;
    int reply = NETDEV_PROTOCOL_SUCCESS;

    ndhead = conn_recv(pdev);

    if (ndhead->msgtype != MSGT_CONTROL_REG_SERVER) {
        printf("conn_recv_dev_reg: wrong message type!\n");
        return -1; /* failure */
    }

    pdev->remote_dev_name = malloc(ndhead->size);
    if (!pdev->remote_dev_name) {
        perror("conn_recv_dev_reg(malloc)");
        return -1;
    }
    memcpy(pdev->remote_dev_name, ndhead->payload, ndhead->size);

    debug("registering device: %s", pdev->remote_dev_name);
    if (netlink_reg_remote_dev(pdev) == -1) {
        printf("conn_recv_dev_reg: failed to register device: %s\n",
                pdev->remote_dev_name);
        return -1;
    }

    ndhead->payload = malloc(sizeof(reply));
    memcpy(ndhead->payload, &reply, sizeof(reply));

    if (conn_send(pdev, ndhead) == -1) {
        printf("conn_recv_dev_reg: failed to send reply\n");
        return -1;
    }

    free(ndhead->payload);
    return 0; /* success */
}

/* size is the minimum that should be read */
int recvall(int conn_fd, struct msghdr *msgh, int size) {
    int rvalue = 0, bytes = 0;

    do {
        rvalue = recvmsg(conn_fd, msgh, msgh->msg_flags);
        //debug("rvalue = %d", rvalue);
        if (rvalue == -1) {
            perror("recvall(recvmsg)");
            return -1; /* falure */
        } else if (rvalue == 0 ) {
            return -1;
        }
        bytes += rvalue;
        msgh->msg_iov->iov_base += rvalue;
        msgh->msg_iov->iov_len = bytes;
    } while (bytes < size);

    return bytes; /* success */
}

int sendall(int conn_fd, struct msghdr *msgh, int size) {
    int rvalue = 0, bytes = 0;

    do {
        rvalue = sendmsg(conn_fd, msgh, msgh->msg_flags);
        //debug("rvalue = %d", rvalue);
        if (rvalue == -1) {
            perror("sendmsg(sendmsg)");
            return -1; /* falure */
        } else if (rvalue == 0 ) {
            return -1;
        }
        bytes += rvalue;
        msgh->msg_iov->iov_base += rvalue;
        msgh->msg_iov->iov_len = bytes;
    } while (bytes < size);

    return 0; /* success */
}
<file_sep>/daemon/conn.h
#ifndef _CONN_H
#define _CONN_H

#include "proxy.h"
#include "netprotocol.h"

int conn_send(struct proxy_dev *pdev, struct netdev_header *ndhead);
struct netdev_header * conn_recv(struct proxy_dev *pdev);
int conn_server(struct proxy_dev *pdev);
int conn_client(struct proxy_dev *pdev);
int conn_send_dev_reg(struct proxy_dev *pdev);
int conn_recv_dev_reg(struct proxy_dev *pdev);
int recvall(int conn_fd, struct msghdr *msgh, int size);
int sendall(int conn_fd, struct msghdr *msgh, int size);

#endif
<file_sep>/daemon/main.c
#include <string.h>        /* memset, memcopy, memmove, memcmp */
#include <stdio.h>         /* printf                           */
#include <stdlib.h>        /* malloc                           */
#include <unistd.h>        /* close, getpid                    */
#include <sys/socket.h>    /* socklen_t */
#include <sys/types.h>     /* pid_t                            */
#include <sys/wait.h>      /* waitpid */
#include <arpa/inet.h>      /* inet_notp */
#include <errno.h>
#include <netinet/ip.h>
#include <signal.h>         /* signal */

#include "signal.h"
#include "proxy.h"
#include "protocol.h"
#include "debug.h"

void parent_sig_chld(int signo) {
    pid_t pid;
    int stat;
    printf("sig_chld: received signal\n");

    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0 ) {
        /* TODO should't use printf in signal handling */
        printf("ALERT: child %d terminated\n", pid);
    }

    return;
}

int netdev_listener(
    int port)
{
    int listenfd, connfd;
    int rvalue;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;
    char str[INET_ADDRSTRLEN];
    void *ptr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    if ( listenfd < 0 ) {
        perror("netdev_listener: failed to create socket");
        return -1;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port   = htons(port);

    rvalue = bind(listenfd,
                (struct sockaddr*)&servaddr,
                sizeof(servaddr));

    if ( rvalue < 0 ) {
        perror("netdev_listener: failed to bind to address");
        return -1;
    }

    rvalue = listen(listenfd, NETDEV_LISTENQ);

    if ( rvalue < 0 ) {
        perror("netdev_listener: failed to listen");
        return -1;
    }

    clilen = sizeof(cliaddr);
    printf("netdev_listener: starting listener at port: %d\n", port);
    while (1) {
        connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
        if (connfd < 0) {
            if (errno == EINTR) {
                continue;
            } else {
                perror("netdev_listener: accept error");
            }
        }

        ptr = (char *)inet_ntop(cliaddr.sin_family ,
                                &cliaddr.sin_addr,
                                str,
                                sizeof(str));

        if ( ptr != NULL ) {
            printf("netdev_listener: new connection from %s:%d\n",
                    str,
                    ntohs(cliaddr.sin_port));
        }

        /* fork for every new client and serve it */
        if ( (childpid = fork()) ==0 ) {
            /* decrease the counter for listening socket,
             * this process won't use it */
            close(listenfd);

            proxy_server(connfd);

            exit(0);
        }

        /* decreas the counter for new connection */
        close(connfd);
    }

    return 0;
}

struct proxy_dev ** read_config(char *filename, int *count) {
    struct proxy_dev **pdevs = NULL;
    FILE *fp;
    char c;
    char line[100];
    int i = 0;
    int rvalue;
    *count = 0;

    if (filename == NULL) {
        return NULL;
    }

    fp = fopen(filename, "r");

    if (!fp) {
        perror("failed to open file");
        return NULL;
    }

    while (EOF != (fscanf(fp, "%c%*[^\n]", &c), fscanf(fp, "%*c"))) {
        if (c != '#') { /* ignore comments */
            ++*count;
        }
    }
    rewind(fp); /* go back to the beginning of the file */

    pdevs = malloc(*count * sizeof(struct proxy_dev*));
    memset(pdevs, 0, *count * sizeof(struct proxy_dev*));

    if (!pdevs) {
        perror("read_config: failed to allocate pdevs");
        return NULL; /* failure */
    }

    for (i = 0; i < *count; ) {
        fgets(line, sizeof(line), fp);
        if (line[0] == '#') { /* if first character is # it's a comment */
            continue;
        }

        if (!pdevs[i]) {
            pdevs[i] = malloc(sizeof(struct proxy_dev));
            memset(pdevs[i], 0, sizeof(struct proxy_dev));
            pdevs[i]->remote_dev_name = malloc(NETDEV_MAX_STRING);
            pdevs[i]->dummy_dev_name =  malloc(NETDEV_MAX_STRING);
            pdevs[i]->rm_ipaddr =       malloc(NETDEV_MAX_STRING);
        }

        pdevs[i]->client = true; /* this is a client instance */
        rvalue  = sscanf(line, "%20[^;];%20[^;];%20[^;];%d\n",
                        pdevs[i]->remote_dev_name,
                        pdevs[i]->dummy_dev_name,
                        pdevs[i]->rm_ipaddr,
                        &pdevs[i]->rm_portaddr);
        if (rvalue == EOF) {
            break;
        }
        if (rvalue < 4) {
            printf("failed to parse configuration in line %d\n", i+1);
            break;
        }
        i++;
    }
    *count = i;

    return pdevs; /* success */
}

int main(int argc, char *argv[]) {
    int pid, dev_count, i, port = NETDEV_DAEMON_PORT;
    char *conffile = NULL;
    char c;
    struct proxy_dev **pdevs = NULL;


    while ((c = getopt(argc, argv, ":hf:p:")) != -1) {
        switch(c) {
        case 'p':
            port = atoi(optarg);
            break;
        case 'f':
            conffile = optarg;
            break;
        case 'h':
            printf("usage: %s [-f CONFIGFILE] [-p SERVERPORT] [-h]\n", argv[0]);
            return 0;
        case ':':       /* -f or -o without operand */
            fprintf(stderr,
                "Option -%c requires an operand\n", optopt);
            return 1;
        case '?':
            fprintf(stderr,
                "Unrecognized option: '-%c'\n", optopt);
            return 1;
        }
    }

    /* to run waitpid for all forked proxies */
    signal(SIGCHLD, parent_sig_chld);

    if (!(pdevs = read_config(conffile, &dev_count))) {
        printf("failed to client read configuration\n");
    } else {
        for ( i = 0 ; i < dev_count ; i++ ) {
            if ( ( pid = fork() ) == 0 ) {
                proxy_client(pdevs[i]);
                exit(0);
            } else if ( pid < 0 ) {
                perror("main: failed to fork");
            }
        }
    }

    if ( netdev_listener(port) ) {
        printf("main: could not start listener\n");
    }

    return 0;
}
<file_sep>/kernel/netlink.h
#ifndef _NETLINK_H
#define _NETLINK_H

#include <linux/skbuff.h>

#include "netdevmgm.h"
#include "fo.h"

void prnttime(void);
int netlink_echo(int pid, int seq, char *msg);
void netlink_recv(struct sk_buff *skb);
int netlink_create_dummy(int pid, int seq, char *buff, size_t bsize);
int netlink_ackmsg(struct netdev_data *nddata, int seq, short msgtype);
int netlink_send(struct netdev_data *nddata, int seq, short msgtype, int flags, void *buff, size_t bufflen);
int netlink_send_msg( int nlpid, struct nlmsghdr *nlh, int flags, void *buff, size_t bufflen);
int netlink_send_skb(struct netdev_data *nddata, struct sk_buff *skb);
struct sk_buff * netlink_pack_skb( struct nlmsghdr *nlh, void *buff, size_t bufflen);
int netlink_send_fo(struct netdev_data *nddata, struct fo_req *req);
int netlink_init(void);
void netlink_exit(void);

#endif /* _NETLINK_H */
<file_sep>/kernel/main.c
#include <linux/compiler.h>
#include <linux/module.h>   /* for MODULE_ macros */
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>

#include "protocol.h"
#include "netlink.h"
#include "netdevmgm.h"
#include "fo.h"

MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("<NAME> <panswiata_at_<EMAIL>_com>");
MODULE_DESCRIPTION("This is an experimental kernel driver. Please use at your own risk.");

static int dev_count = NETDEV_MAX_DEVICES; /* TODO driver option */
static int netdev_major = NETDEV_MAJOR; /* TODO driver option */
dev_t netdev_devno;
struct class *netdev_class;

static void netdev_cleanup(void)
{
    while (ndmgm_end()) {
        printk(KERN_ERR "netdev_cleanup: failed to delete devices");
    }

    if (netdev_class)
        class_destroy(netdev_class);

    /* netdev_cleanup is not called if alloc_chrdev_region has failed */
    /* free the major and minor numbers */
    unregister_chrdev_region(netdev_devno, dev_count);

    printk(KERN_DEBUG "netdev: driver cleanup finished\n");

    return;
}

static int __init netdev_init(void)
{
    int err;
    int first_minor = 0;
    /* get a range of minor numbers (starting with 0) to work with
     * we are using alloc rather than register to get dynamic major */
    err = alloc_chrdev_region(&netdev_devno, first_minor, dev_count, NETDEV_NAME);

    /* fail gracefully if need be */
    if (err) {
        printk(KERN_ERR "netdev: error registering chrdev!\n");
        return err;
    }
    netdev_major = MAJOR(netdev_devno);

    netdev_class = class_create(THIS_MODULE, NETDEV_NAME);
    if (IS_ERR(netdev_class)) {
        err = PTR_ERR(netdev_class);
        goto fail;
    }
    
    /* create array and hashtable for devices */
    ndmgm_prepare();

    /* setup netlink */
    err = netlink_init();
    if (err) {
        printk(KERN_ERR "netdev: netlink setup failed!\n");
        goto fail;
    }

    return 0; /* success */

fail:
    netdev_cleanup();
    return err;
}

static void __exit netdev_exit(void)
{
    netlink_exit();
    netdev_cleanup();
    return;
}

module_exit(netdev_exit);
module_init(netdev_init);
<file_sep>/kernel/fo_send.h
#ifndef _FO_SEND_H
#define _FO_SEND_H

#include <linux/fs.h> /* fl_owner_t */

/* functions for sending and receiving file operations */
loff_t ndfo_send_llseek(struct file *flip, loff_t offset, int whence);
ssize_t ndfo_send_read(struct file *flip, char __user *data, size_t size, loff_t *offset);
ssize_t ndfo_send_write(struct file *flip, const char __user *data, size_t size, loff_t *offset);
size_t ndfo_send_aio_read(struct kiocb *a, const struct iovec *b, unsigned long c, loff_t offset);
ssize_t ndfo_send_aio_write(struct kiocb *a, const struct iovec *b, unsigned long c, loff_t d);
unsigned int ndfo_send_poll(struct file *filp, struct poll_table_struct *wait);
long ndfo_send_unlocked_ioctl(struct file *filp, unsigned int b, unsigned long c);
long ndfo_send_compat_ioctl(struct file *filp, unsigned int b, unsigned long c);
int ndfo_send_mmap(struct file *filp, struct vm_area_struct *b);
int ndfo_send_open(struct inode *inode, struct file *filp);
int ndfo_send_flush(struct file *filp, fl_owner_t id);
int ndfo_send_release(struct inode *a, struct file *filp);
int ndfo_send_fsync(struct file *filp, loff_t b, loff_t offset, int d);
int ndfo_send_aio_fsync(struct kiocb *a, int b);
int ndfo_send_fasync(int a, struct file *filp, int c);
int ndfo_send_lock(struct file *filp, int b, struct file_lock *c);
ssize_t ndfo_send_sendpage(struct file *filp, struct page *b, int c, size_t size, loff_t *offset, int f);
unsigned long ndfo_send_get_unmapped_area(struct file *filp, unsigned long b, unsigned long c,unsigned long d, unsigned long e);
int ndfo_send_check_flags(int a);
int ndfo_send_flock(struct file *filp, int b, struct file_lock *c);
ssize_t ndfo_send_splice_write(struct pipe_inode_info *a, struct file *filp, loff_t *offset, size_t size, unsigned int e);
ssize_t ndfo_send_splice_read(struct file *filp, loff_t *offset, struct pipe_inode_info *c, size_t size, unsigned int e);
int ndfo_send_setlease(struct file *filp, long b, struct file_lock **c);
long ndfo_send_fallocate(struct file *filp, int b, loff_t offset, loff_t len);
int ndfo_send_show_fdinfo(struct seq_file *a, struct file *filp);

#endif /* _FO_SEND_H */
<file_sep>/include/protocol.h
#ifndef _PROTOCOL_H
#define _PROTOCOL_H

/* this number HAS TO BE incremented every time anything in the 
 * the core protocol is changed */
#define NETDEV_PROTOCOL_VERSION 2
#define NETDEV_PROTOCOL_SUCCESS 0 /* 0 will always mean correct */
#define NETDEV_PROTOCOL_FAILURE -1 /* -1 will always mean failure */

/* size limit of messages sent through sendmsg */
#define NETDEV_MESSAGE_LIMIT    213000

/* defines the default daemon port */
#define NETDEV_DAEMON_PORT      9999

/* 2nd argument to listen(), max size of listen queue */
#define NETDEV_LISTENQ          1024

/* defines the netlink bus used, we want our own protocol so we use the 
 * last one of the 32 available buses */
#define NETDEV_PROTOCOL        31

/* define type of message for netlink driver control,
 * will be used in nlmsghdr->nlmsg_type */
#define MSGT_CONTROL_ECHO         1 /* test to check if diver works */
#define MSGT_CONTROL_SUCCESS      2 /* test to check if diver works */
#define MSGT_CONTROL_FAILURE      3 /* test to check if diver works */
#define MSGT_CONTROL_VERSION      4 /* check if driver is compatible */
#define MSGT_CONTROL_REG_DUMMY    5 /* register a new device */
#define MSGT_CONTROL_REG_SERVER   6 /* register a new device */
#define MSGT_CONTROL_UNREGISTER   7 /* unregister the device */
#define MSGT_CONTROL_RECOVER      8 /* recover a device after lost conn. */
#define MSGT_CONTROL_DRIVER_STOP  9 /* close all devices and stop driver */
#define MSGT_CONTROL_LOSTCONN     10 /* lost connection with other end */

/* limit of control messsages */
#define MSGT_CONTROL_START        0
#define MSGT_CONTROL_END          11

/* Definitions used to distinguis between file operation structures
 * that contain all the functiona arguments, best used with short or __u16
 * Will be either used in nlmsghdr->nlmsg_type or some inner variable */
#define MSGT_FO_LLSEEK            101
#define MSGT_FO_READ              102
#define MSGT_FO_WRITE             103
#define MSGT_FO_AIO_READ          104
#define MSGT_FO_AIO_WRITE         105
#define MSGT_FO_READDIR           106
#define MSGT_FO_POLL              107
#define MSGT_FO_UNLOCKED_IOCTL    108
#define MSGT_FO_COMPAT_IOCTL      109
#define MSGT_FO_MMAP              110
#define MSGT_FO_OPEN              111
#define MSGT_FO_FLUSH             112
#define MSGT_FO_RELEASE           113
#define MSGT_FO_FSYNC             114
#define MSGT_FO_AIO_FSYNC         115
#define MSGT_FO_FASYNC            116
#define MSGT_FO_LOCK              117
#define MSGT_FO_SENDPAGE          118
#define MSGT_FO_GET_UNMAPPED_AREA 119
#define MSGT_FO_CHECK_FLAGS       120
#define MSGT_FO_FLOCK             121
#define MSGT_FO_SPLICE_WRITE      122
#define MSGT_FO_SPLICE_READ       123
#define MSGT_FO_SETLEASE          124
#define MSGT_FO_FALLOCATE         125
#define MSGT_FO_SHOW_FDINF        126

#define MSGT_FO_START             100
#define MSGT_FO_END               127

#endif /* _PROTOCOL_H */
<file_sep>/daemon/proxy.h
#ifndef _PROXY_H
#define _PROXY_H

#include <linux/netlink.h>  /* sockaddr_nl */
#include <netinet/in.h>     /* sockaddr_in */
#include <sys/un.h>         /* sockaddr_un */

#define NETDEV_MAX_STRING 20

typedef enum {
    true,
    false
} bool;

struct proxy_dev {
    int pid; /* pid od the proxy process */
    int nl_fd; /* netlink file descryptor */
    int rm_fd; /* remote file descryptor */
    int us_fd[2]; /* unix socket pair, first one is for reading */
    bool client; /* defines if this is a client or a server */
    struct sockaddr_nl *nl_src_addr;
    struct sockaddr_nl *nl_dst_addr;
    struct sockaddr_in *rm_addr;
    struct sockaddr_un *us_addr;
    int rm_portaddr;
    char *rm_ipaddr;
    char *remote_dev_name;
    char *dummy_dev_name;
};

int proxy_netlink_setup(struct proxy_dev *pdev);
int proxy_register_device(struct proxy_dev *pdev, struct msghdr *p_msg, struct iovec *p_iov);
int proxy_unregister_device(void);
void proxy_setup_signals();
int proxy_setup_unixsoc(struct proxy_dev *pdev);
struct proxy_dev * proxy_alloc_dev(int connfd);
void proxy_client(struct proxy_dev *pdev);
void proxy_server(int connfd);
void proxy_sig_hup(int signo);
int proxy_loop();
int proxy_control(struct proxy_dev *pdev);
int proxy_handle_remote(struct proxy_dev *pdev);
int proxy_handle_netlink(struct proxy_dev *pdev);
int netlink_unregister_dev(struct proxy_dev *pdev);

#endif
<file_sep>/kernel/netdevmgm.h
#ifndef _NETDEVMGM_H
#define _NETDEVMGM_H

#include <linux/cdev.h>
#include <linux/module.h>   /* for THIS_MODULE */
#include <linux/hashtable.h>
#include <linux/rwsem.h>
#include <linux/kfifo.h>
#include <linux/slab.h>

#include "netdev.h"
#include "protocol.h"
#include "fo_comm.h"

/* necessary since netdevmgm.h and fo_comm.h reference each other */
struct fo_req;
/* __u32, unsigned 32bit type, 12 bit for majro, 20 minor */
extern dev_t netdev_devno;
extern struct class *netdev_class;

struct netdev_data {
    unsigned int nlpid; /* pid of the process that created this device */
    bool active; /* set to false if netlink connection is lost */
    bool dummy; /* defines if this is a server device or a dummy one */
    char *devname; /* name of the device provided by the process */
    atomic_t users; /* counter of threads using this objecto */
    atomic_t curseq; /* last used sequence number for this device */
    spinlock_t nllock; /* netlink lock ensuring proper order for ACK msg */
    struct cdev *cdev; /* internat struct representing char devices */
    struct device *device;
    struct hlist_node hnode; /* to use with hastable */
    struct rw_semaphore sem; /* necessary since threads can use this */
    struct kmem_cache *queue_pool; /* pool of memory for fo_req structs */
    /* hash table for active open files */
    DECLARE_HASHTABLE(foacc_htable, NETDEV_HTABLE_ACC_SIZE);
};

struct netdev_data * ndmgm_alloc_data(int nlpid, char *name, int minor);
int ndmgm_create_dummy(int nlpid, char *name);
int ndmgm_create_server(int nlpid, char *name);
void ndmgm_free_data(struct netdev_data *nddata);
struct netdev_data * ndmgm_find(int nlpid);
void ndmgm_put_minor(int minor);
int ndmgm_get_minor(int pid);
int ndmgm_find_pid(dev_t dev);
int ndmgm_incseq(struct netdev_data *nddata);
struct fo_access * ndmgm_find_acc(struct netdev_data *nddata, int access_id);
int ndmgm_find_destroy(int nlpid);
int ndmgm_destroy(struct netdev_data *nddata);
int ndmgm_destroy_allacc( struct netdev_data *nddata);
int ndmgm_destroy_dummy(struct netdev_data *nddata);
int ndmgm_destroy_server(struct netdev_data *nddata);
int ndmgm_end(void);
void ndmgm_prepare(void);
void ndmgm_get(struct netdev_data *nddata);
void ndmgm_put(struct netdev_data *nddata);
int ndmgm_refs(struct netdev_data *nddata);

#endif
<file_sep>/daemon/signal.c
#include <sys/types.h>     /* pid_t                            */
#include <sys/wait.h>      /* waitpid                          */
#include <stdio.h>         /* TODO should use write when handling signal */
#include <signal.h>
#include <unistd.h>

#include "signal.h"

typedef void    Sigfunc(int);   /* for signal handlers */

Sigfunc * signal(int signo, Sigfunc *func) {
    struct sigaction act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    if (signo == SIGALRM) {
        act.sa_flags |= SA_RESTART;
    }

    if (sigaction(signo, &act, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}
<file_sep>/daemon/Makefile
CC = gcc
CFLAGS = -g -Wall
INCLUDE = -I../include -I./lib
DEPS = ../include/protocol.h
OBJ = netlink.o conn.o signal.o proxy.o main.o
TARGET = netdevd

all: $(TARGET)

%.o: %.c $(DEPS)
	$(CC) $(CFLAGS) $(INCLUDE) -c -o $@ $<

$(TARGET): $(OBJ)
	$(CC) $(CFLAGS) -o $(TARGET) $^

clean:
	rm ./${TARGET} ./*.o
<file_sep>/README.md
netdev
======

Description
-----------

This project has a goal of allowing the sharing of physical devices like character and block devices between linux machines over TCP/IP.

Disclamer
---------

The code is not yet stable. Test at your own peril. This WILL cause kernel panics.

Usage
-----

Both kernel module and the daemon program can be compiled with make.

Daemon can be started with or without arguments.

    Usage: ./netdevd [-f CONFIGFILE] [-p LISTENPORT] [-h]

Format of configuration file:

    RemoteDevicePath;DummyDeviceName;DaemonIP;DaemonPort

Example:

    /dev/urandom;netdev_urandom;192.168.1.13;9999

Some static limitations apply. For example limit of devices is 32 and is defined in include/protocol.h with NETDEV_MAX_DEVICES and NETDEV_HTABLE_DEV_SIZE.

Notes
-----

If you want to play with this software please do, and please report any crashes and bugs you might encounter to my github at https://github.com/PonderingGrower/netdev .
<file_sep>/kernel/Makefile
# Makefile – makefile of our first driver
TARGET 		:= netdev
DEBUG 		:= -g
WARN   		:= -Wall -Wstrict-prototypes -Wmissing-prototypes
NOWARN 		:= -Wno-unused-function
INCLUDE 	:= -I$(src)/../include/
ccflags-y 	:= -O ${WARN} ${NOWARN} ${DEBUG} ${INCLUDE}

# if KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq (${KERNELRELEASE},)
    obj-m 	:= ${TARGET}.o
	${TARGET}-objs := fo_access.o fo_send.o fo_recv.o fo_comm.o fo.o netdevmgm.o netlink.o main.o
# Otherwise we were called directly from the command line.
# Invoke the kernel build system.
else

KERNEL_SOURCE := /usr/src/linux
PWD 		:= $(shell pwd)

default:
	${MAKE} -C ${KERNEL_SOURCE} M=${PWD} modules

clean:
	${MAKE} -C ${KERNEL_SOURCE} M=${PWD} clean

install:
	${MAKE} -C ${KERNEL_SOURCE} M=${PWD} modules_install
endif
<file_sep>/daemon/netlink.h
#ifndef _NETLINK_H
#define _NETLINK_H

#include "proxy.h"

int netlink_setup(struct proxy_dev *pdev);
int netlink_reg_dummy_dev(struct proxy_dev *pdev);
int netlink_reg_remote_dev(struct proxy_dev *pdev);
int netlink_send(struct proxy_dev *pdev, struct nlmsghdr *nlh);
int netlink_send_msg(struct proxy_dev *pdev, void *buff, size_t bsize, int msgtype, int flags);
int netlink_send_nlh(struct proxy_dev *pdev, struct nlmsghdr *nlh);
struct nlmsghdr * netlink_recv(struct proxy_dev *pdev);

#endif
<file_sep>/kernel/netlink.c
#include <linux/netlink.h>  /* for netlink sockets */
#include <linux/module.h>
#include <net/sock.h>
#include <linux/kthread.h> /* task_struct, kthread_run */
#include <linux/ktime.h> /* getnstimeofday */

#include "netlink.h"
#include "netdevmgm.h"
#include "protocol.h"
#include "dbg.h"

struct sock *nl_sk = NULL;

void prnttime(void) {
    struct timespec ts;

    getnstimeofday(&ts);

    printk(KERN_DEBUG " -- %pS - TIME: sec %ld, nsec %ld\n",
            __builtin_return_address(0),
            ts.tv_sec, ts.tv_nsec);
}

void netlink_recv(struct sk_buff *skb)
{
    struct netdev_data *nddata = NULL;
    struct task_struct *task = NULL;
    struct nlmsghdr *nlh = NULL;
    int pid, msgtype, err = 0;
    void *data;

    nlh = nlmsg_hdr(skb);

    pid     = nlh->nlmsg_pid;
    msgtype = nlh->nlmsg_type;

    //debug("type: %d, pid: %d, size: %d", msgtype, pid, nlh->nlmsg_len);
    if ( pid == 0 ) {
        printk(KERN_ERR "netlink_recv: message from kernel");
        return;
    }

    if (msgtype >= MSGT_CONTROL_START && msgtype < MSGT_CONTROL_END) {
        switch (msgtype) {
        case MSGT_CONTROL_ECHO:
            err = netlink_send_msg(pid, nlh, NLM_F_ECHO,
                                    NLMSG_DATA(nlh),
                                    sizeof(int));
            break;
        case MSGT_CONTROL_VERSION:
            msgtype = NETDEV_PROTOCOL_VERSION,
            err = netlink_send_msg(pid, nlh, NLM_F_ECHO,
                                    &msgtype,
                                    sizeof(msgtype));
            break;
        case MSGT_CONTROL_REG_DUMMY:
            err = ndmgm_create_dummy(pid, (char*)nlmsg_data(nlh));
            break;
        case MSGT_CONTROL_REG_SERVER:
            err = ndmgm_create_server(pid, (char*)nlmsg_data(nlh));
            break;
        case MSGT_CONTROL_UNREGISTER:
            err = ndmgm_find_destroy(pid);
            break;
        default:
            printk(KERN_ERR
                    "netlink_recv: unknown control msg type: %d\n",
                    msgtype);
            break;
        }
    } else if ( msgtype > MSGT_FO_START && msgtype < MSGT_FO_END ) {
        /* if it's file operation device must exist */
        nddata = ndmgm_find(pid);
        if (!nddata) {
            printk(KERN_ERR "netlink_recv: failed to find nddata\n");
            return;
        }

        /* we will use this skb for ACK*/
        data = skb_copy(skb, GFP_KERNEL);

        /* create a new thread so server doesn't wait for ACK */
        task = kthread_create(&fo_recv, data, "fo_recv");
        if (IS_ERR(task)) {
            printk(KERN_ERR
                    "fo_recv: failed to create thread, error = %ld\n",
                    PTR_ERR(task));
            err = PTR_ERR(task); /* failure */
        }

        /* when lock is before kthread_run it creates "spinlock wrong CPU"
         * error on the first open operation, that's why kthread_creat
         * and wake_up_pprocess is used */
        spin_lock(&nddata->nllock); /* make sure we send ACK first */

        /* start the thread */
        wake_up_process(task);
    } else {
        debug("unknown message type: %d", msgtype);
    }

    /* if error or if this message says it wants a response */
    if ( err || (nlh->nlmsg_flags & NLM_F_ACK )) {
        /* send messsage,nlmsg_unicast will take care of freeing skb */
        netlink_ack(skb, nlh, err); /* err should be 0 when no error */
    } else {
        dev_kfree_skb(skb);
    }
    /* if nddata is NULL the spinlock is not locked */
    if (nddata) {
        spin_unlock(&nddata->nllock);
    }
}

int netlink_send(
    struct netdev_data *nddata,
    int seq,
    short msgtype,
    int flags,
    void *buff,
    size_t bufflen)
{
    struct nlmsghdr *nlh;
    struct sk_buff *skb;
    int rvalue;

    //debug("type: %d, pid: %d, size: %zu", msgtype, nddata->nlpid, bufflen);
    if (!nddata) {
        printk(KERN_ERR "netlink_send: nddata is NULL\n");
        return -1;
    }

    /* allocate space for message header and it's payload */
    skb = nlmsg_new(bufflen, GFP_KERNEL);

    if(!skb) {
        printk(KERN_ERR "netlink_send: failed to allocate new sk_buff!\n");
        goto free_skb;
    }

    /* set pid, seq, message type, payload size and flags */
    nlh = nlmsg_put(
                    skb,
                    nddata->nlpid,
                    seq,
                    msgtype,
                    bufflen, /* adds nlh size (16bytes) for full size */
                    flags | NLM_F_ACK); /* for delivery confirmation */

    if (!nlh) {
        printk(KERN_ERR "netlink_send: failed to create nlh\n");
        goto free_skb;
    }

    NETLINK_CB(skb).portid = nddata->nlpid;
    NETLINK_CB(skb).dst_group = 0; /* not in mcast group */

    if (buff) { /* possible to send message without payload */
        memcpy(nlmsg_data(nlh) ,buff ,bufflen);
    }

    spin_lock(&nddata->nllock); /* to make sure ACK is sent first */
    /* send messsage,nlmsg_unicast will take care of freeing skb */
    rvalue = nlmsg_unicast(nl_sk, skb, nddata->nlpid);
    spin_unlock(&nddata->nllock);

    /* TODO get confirmation of delivery */

    if(rvalue < 0) {
        printk(KERN_INFO "netlink_send: error while sending back to user\n");
        return -1; /* failure */
    }

    return 0; /* success */
free_skb:
    nlmsg_free(skb);
    return -1;
}

int netlink_send_msg(
    int nlpid,
    struct nlmsghdr *nlh,
    int flags,
    void *buff,
    size_t bufflen)
{
    return netlink_send(ndmgm_find(nlpid),
                        nlh->nlmsg_seq,
                        nlh->nlmsg_type,
                        flags,
                        buff,
                        bufflen);
}

int netlink_send_skb(
    struct netdev_data *nddata,
    struct sk_buff *skb)
{
    int rvalue;

    if (!skb) {
        printk(KERN_ERR "netlink_send: skb is null\n");
        return -1;
    }

    /*
    debug("type: %d, pid: %d, size: %d",
            nlmsg_hdr(skb)->nlmsg_type,
            nddata->nlpid,
            nlmsg_hdr(skb)->nlmsg_len);
    */

    NETLINK_CB(skb).portid = nddata->nlpid;

    spin_lock(&nddata->nllock); /* to make sure ACK is sent first */
    /* send messsage,nlmsg_unicast will take care of freeing skb */
    rvalue = nlmsg_unicast(nl_sk, skb, nddata->nlpid);
    /* send messsage,nlmsg_unicast will take care of freeing skb */
    spin_unlock(&nddata->nllock);

    if(rvalue < 0) {
        printk(KERN_INFO "netlink_send: error while sending back to user\n");
        return -1; /* failure */
    }

    return 0; /* success */
}

struct sk_buff * netlink_pack_skb(
    struct nlmsghdr *nlh,
    void *buff,
    size_t bufflen)
{
    struct sk_buff *skb;

    nlh->nlmsg_len = NLMSG_SPACE(bufflen);

    skb = alloc_skb(nlh->nlmsg_len, GFP_KERNEL);
    if (!skb) {
        printk(KERN_ERR "fo_exectue: failed to expand skb\n");
        return NULL;
    }

    nlh = nlmsg_put(
                    skb,
                    nlh->nlmsg_pid,
                    nlh->nlmsg_seq,
                    nlh->nlmsg_type,
                    bufflen,
                    NLM_F_ACK);
    if (!nlh) {
        printk(KERN_ERR "netlink_pack_skb: insufficient tailroom\n");
        return NULL;
    }

    memcpy(nlmsg_data(nlh), buff, bufflen);

    return skb; /* success */
}

int netlink_init(void)
{
    /* NOTE: new structure for configuring netlink socket changed in
     * linux kernel 3.8
     * http://stackoverflow.com/questions/15937395/netlink-kernel-create-is-not-working-with-latest-linux-kernel
     */
    struct netlink_kernel_cfg nl_cfg;
    nl_cfg.groups = 0;           // used for multicast
    nl_cfg.flags = 0;            // TODO
    nl_cfg.input = netlink_recv; // function that will receive data

    nl_sk = netlink_kernel_create(&init_net,
                                    NETDEV_PROTOCOL,
                                    &nl_cfg);

    if(!nl_sk) {
        printk(KERN_ERR "netlink_init: error creating socket.\n");
        return -1;
    }

    printk(KERN_INFO "netlink_init: netlink setup complete\n");
    return 0;
}

void netlink_exit(void)
{
    printk(KERN_INFO "netlink_exit: closing netlink\n");

    /* can only be done once the process releases the socket, otherwise
     * kernel will refuse to unload the module */
    netlink_kernel_release(nl_sk);
}

MODULE_LICENSE("GPL");
<file_sep>/daemon/lib/helper.h
#ifndef _HELPER_H
#define _HELPER_H

#ifndef max
    #define max( a, b ) ( ((a) > (b)) ? (a) : (b) )
#endif

#ifndef min
    #define min( a, b ) ( ((a) < (b)) ? (a) : (b) )
#endif

#endif
<file_sep>/kernel/fo_recv.h
#ifndef _FO_RECV_H
#define _FO_RECV_H

#include "fo_comm.h"
#include "fo_access.h"

/* functions for executing file operations sent from client */
int ndfo_recv_llseek(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_read(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_write(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_aio_read(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_aio_write(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_poll(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_unlocked_ioctl(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_compat_ioctl(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_mmap(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_open(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_flush(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_release(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_fsync(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_aio_fsync(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_fasync(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_lock(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_sendpage(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_get_unmapped_area(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_check_flags(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_flock(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_splice_write(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_splice_read(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_setlease(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_fallocate(struct fo_access *acc, struct fo_req *req);
int ndfo_recv_show_fdinfo(struct fo_access *acc, struct fo_req *req);

extern int (*netdev_recv_fops[])(struct fo_access*, struct fo_req*);

#endif /* _FO_RECV_H */
