[Q] Module created netdevices

Nicholas J. Leon (nicholas@binary9.net)
Thu, 11 Sep 1997 14:59:58 -0400 (EDT)


Hi kernel/net gurus!

I've been trying to teach myself a bit about kernel internals by
writing (yet another) VPN module.

Basically, I used new_tunnel.c & dummy.c as examples. My first test
was to simply create a network device that forwarded packets; no
tunneling or anything. It just takes the skb that is passed,
determines the route via ip_rt_route and forwards it via ip_forward.

Everything has worked well except for one thing. After loading my
module, ifconfig'ing the device and setting some routes, I try to ping
a machine through this device. A few packets go out (and come back),
but then ping causes a general protect fault (which I can't trace
because klogd isn't working for me and I don't have C++ installed to
compile ksymoops) [they are on my list of things to do:)]

I would like it if someone could look over my code (it isn't big) and
tell me what I'm doing wrong. I also have a few questions if youre in
the mind to answer them:

1. What does ip_rt_put do?
2. In new_tunnel.c, ip_rt_route is called twice, with a ip_rt_put in
between. Why? No doubt it has to do with #1.
3. Whats the difference between dev_kfree_skb & kfree_skb? Is
kfree_skb for skb_buff's that you create yourself? And dev_* is for
system created ones?

Well, I guess that's about it for now. This is VERY much uncharted
territory for me, so be nice :)

[ module.c ]
#define __KERNEL__
#define MODULE

#include <linux/module.h> /* can't do without it */
#include <linux/version.h> /* and this too */

#include <linux/types.h> /* ulong and friends */
#include <linux/sched.h> /* current, task_struct, other goodies */
#include <linux/fcntl.h> /* O_NONBLOCK etc. */
#include <linux/errno.h> /* return values */
#include <linux/config.h>
#include <linux/malloc.h>

#include "module.h"

static char * version="VPN driver pre1.0, (c) Nicholas J. Leon";

/******************************************************************************/
static char * szhost(__u32 ipaddr) {
char sz[256];
unsigned char *ip;

ip=(unsigned char*)&ipaddr;
sprintf(sz,"%u.%u.%u.%u",*ip,*(ip+1),*(ip+2),*(ip+3));
return sz;
}

void print_ip(struct iphdr *ip) {
unsigned char *ipaddr;

printk(KERN_DEBUG "IP packet:\n");
printk(KERN_DEBUG "--- header len = %d\n", ip->ihl*4);
printk(KERN_DEBUG "--- ip version: %d\n", ip->version);
printk(KERN_DEBUG "--- ip protocol: %d\n", ip->protocol);
ipaddr=(unsigned char *)&ip->saddr;
printk(KERN_DEBUG "--- source address: %s\n",szhost(ip->saddr));
// printk(KERN_DEBUG "--- source address: %u.%u.%u.%u\n",
// *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
ipaddr=(unsigned char *)&ip->daddr;
printk(KERN_DEBUG "--- destination address: %u.%u.%u.%u\n",
*ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
// printk(KERN_DEBUG "--- total packet len: %d\n", ntohs(ip->tot_len));
}

int vpn_open(struct device *dev) {
MOD_INC_USE_COUNT;
return 0;
}

int vpn_close(struct device *dev) {
MOD_DEC_USE_COUNT;
return 0;
}

static int vpn_xmit(struct sk_buff *skb,struct device *dev) {
struct enet_statistics *stats=NULL;
struct iphdr * iph;
struct rtable *rt;
__u32 target;

if (skb==NULL||dev==NULL) {
printk(KERN_DEBUG "nothing to do!\n");
return 0;
}

// make sure we're not busy
cli();
if (dev->tbusy!=0) {
sti();
stats->tx_errors++;
return 1;
}

dev->tbusy=1;
sti();

iph=(struct iphdr *)skb->data;
stats=(struct enet_statistics *)dev->priv;
print_ip(iph);

if ((rt=ip_rt_route(iph->daddr,0,skb->sk?skb->sk->bound_device:NULL))==NULL) {
// no route
stats->tx_errors++;
dev->tbusy=0;
dev_kfree_skb(skb,FREE_WRITE);
return 0;
}

if (rt->rt_flags & RTF_GATEWAY) {
printk(KERN_DEBUG "RTF_GATEWAY\n");
target=rt->rt_gateway;
}
else {
printk(KERN_DEBUG "Not gateway\n");
target=dev->pa_dstaddr;
}

printk(KERN_DEBUG "target is %s\n",szhost(target));

ip_rt_put(rt);

if (sysctl_ip_forward) {
if (ip_forward(skb,dev,IPFWD_NOTTLDEC,target)) {
dev_kfree_skb(skb,FREE_WRITE);
}
}
else {
dev_kfree_skb(skb,FREE_WRITE);
}

// dev_kfree_skb(skb,FREE_WRITE);

stats->tx_packets++;
dev->tbusy=0;

return 0;
}

struct enet_statistics * vpn_get_stats(struct device *dev) {
struct enet_statistics *stats=(struct enet_statistics*)dev->priv;
return stats;
}

int vpn_init(struct device *dev) {
int i;

dev->hard_start_xmit=vpn_xmit;
dev->open=vpn_open;
dev->stop=vpn_close;
dev->get_stats=vpn_get_stats;
dev->flags|=IFF_NOARP;
dev->type=ARPHRD_TUNNEL;
dev->hard_header_len=(sizeof(struct iphdr)+10);
dev->mtu=1500;
dev->addr_len=0;
dev->tx_queue_len=2;
memset(dev->broadcast,0xFF,ETH_ALEN);
dev->family=AF_INET;
dev->pa_alen=4;

for (i=0;i<DEV_NUMBUFFS;i++)
skb_queue_head_init(&dev->buffs[i]);

dev->priv=kmalloc(sizeof(struct enet_statistics),GFP_KERNEL);

if (dev->priv==NULL) {
printk(KERN_ERR "Couldn't allocate memory");
return -ENOMEM;
}

memset(dev->priv,0,sizeof(struct enet_statistics));

return 0;
}

/******************************************************************************/

static char *device=NULL;
static struct_vpn *dvpn=NULL;
static int numdevices=1;

//static struct device *dvpn;

int init_module(void) {
int i=1;
char name[16];
struct_vpn *tvpn=dvpn;

printk(KERN_DEBUG "%s, %d devices\n",version,numdevices);

while (numdevices--) {
// first device if tvpn==NULL
if (tvpn==NULL) {
tvpn=(struct_vpn *)kmalloc(sizeof(struct_vpn),GFP_KERNEL);
if (tvpn==NULL) {
printk(KERN_ERR "Couldn't allocate space for tvpn\n");
return -ENOMEM;
}
// make our global point to the first one in the chain
dvpn=tvpn;
}
else {
// second or greater device
tvpn->next=(struct_vpn *)kmalloc(sizeof(struct_vpn),GFP_KERNEL);
if (tvpn->next==NULL) {
printk(KERN_ERR "Couldn't allocate space for tvpn\n");
return -ENOMEM;
}
tvpn=tvpn->next;
}

// tvpn has our new, blank, record
memset(tvpn,0,sizeof(struct_vpn));

tvpn->dev=kmalloc(sizeof(struct device),GFP_KERNEL);
if (tvpn->dev==NULL) {
printk(KERN_ERR "Couldn't allocate space for dev\n");
return -ENOMEM;
}
memset(tvpn->dev,0,sizeof(struct device));

if (device)
strcpy(name,device);
else
strcpy(name,"vpn0");

while (dev_get(name)!=NULL) {
sprintf(name,"vpn%d",i);
i++;
}

tvpn->dev->name=(char *)kmalloc(sizeof(name),GFP_KERNEL);
strcpy(tvpn->dev->name,name);
tvpn->dev->init=vpn_init;

printk(KERN_DEBUG "Creating device %s\n",tvpn->dev->name);

if (register_netdev(tvpn->dev)!=0)
return -EIO;
}
return 0;
}

int cleanup_module(void) {
struct_vpn *tmp;

while (dvpn) {
printk(KERN_DEBUG "Removing device %s\n",dvpn->dev->name);
unregister_netdev(dvpn->dev);
kfree(dvpn->dev->name);
kfree(dvpn->dev);
tmp=dvpn->next;
kfree(dvpn);
dvpn=tmp;
}

return 0;
}

[ module.h ]
#ifndef __MODULE_H
#define __MODULE_H

#define __KERNEL__
#define MODULE

#include <linux/netdevice.h>
#include <net/ip.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/route.h>

typedef struct struct_vpn {
struct device *dev;
struct struct_vpn * next;
} struct_vpn;

#endif

------------------------------------------------------------------------------
Nicholas J. Leon <nicholas@binary9.net>
"Elegance through Simplicity" http://mrnick.binary9.net/
PGP:finger nicholas@neko.binary9.net Shinanyaku:[Chronx/War2/XvT]
------------------------------------------------------------------------------
[ -- Powered by a Linux/GNU System -- ]

--
Please ignore the following addresses, they are intended to determine and
catch bulk emailers that scan newsgroups and mailing lists.

>> linux-kernel_vger.rutgers.edu@catcher.binary9.net