www.pudn.com > dsr-uu-0.2.rar > dsr-dev.c


/* Copyright (C) Uppsala University
 *
 * This file is distributed under the terms of the GNU general Public
 * License (GPL), see the file LICENSE
 *
 * Author: Erik Nordström, 
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "debug.h"
#include "dsr.h"
#include "neigh.h"
#include "dsr-pkt.h"
#include "dsr-opt.h"
#include "dsr-rreq.h"
#include "link-cache.h"
#include "dsr-srt.h"
#include "dsr-ack.h"
#include "send-buf.h"
#include "maint-buf.h"
#include "dsr-io.h"

/* Our dsr device */
static struct net_device *dsr_dev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
/* dsr_node must be static on some older kernels, otherwise it segfaults on
 * module load */
static struct dsr_node *dsr_node;
#else
struct dsr_node *dsr_node;
#endif
static int rp_filter = 0;
static int forwarding = 0;


#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static int dsr_dev_llrecv(struct sk_buff *skb, struct net_device *indev,
			  struct packet_type *pt);
#else

static int dsr_dev_llrecv(struct sk_buff *skb, struct net_device *indev,
			  struct packet_type *pt, struct net_device *orig_dev);
#endif

static struct packet_type dsr_packet_type = {
	.type = __constant_htons(ETH_P_IP),
	.func = dsr_dev_llrecv,
};

struct sk_buff *dsr_skb_create(struct dsr_pkt *dp, struct net_device *dev)
{
	struct sk_buff *skb;
	char *buf;
	int ip_len;
	int tot_len;
	int dsr_opts_len = dsr_pkt_opts_len(dp);

	ip_len = dp->nh.iph->ihl << 2;

	tot_len = ip_len + dsr_opts_len + dp->payload_len;

	DEBUG("ip_len=%d dsr_opts_len=%d payload_len=%d tot_len=%d\n",
	      ip_len, dsr_opts_len, dp->payload_len, tot_len);
#ifdef KERNEL26
	skb = alloc_skb(tot_len + LL_RESERVED_SPACE(dev), GFP_ATOMIC);
#else
	skb = alloc_skb(dev->hard_header_len + 15 + tot_len, GFP_ATOMIC);
#endif
	if (!skb) {
		DEBUG("alloc_skb failed\n");
		return NULL;
	}

	/* We align to 16 bytes, for ethernet: 2 bytes + 14 bytes header */
#ifdef KERNEL26
	skb_reserve(skb, LL_RESERVED_SPACE(dev));
#else
	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
#endif
	skb->mac.raw = skb->data - 14;
	skb->nh.raw = skb->data;
	skb->dev = dev;
	skb->protocol = htons(ETH_P_IP);

	/* Copy in all the headers in the right order */
	buf = skb_put(skb, tot_len);

	memcpy(buf, dp->nh.raw, ip_len);

	/* For some reason the checksum has to be recalculated here, at least
	 * when there is a record route IP option */
	ip_send_check((struct iphdr *)buf);

	buf += ip_len;

	/* Add DSR header if it exists */
	if (dsr_opts_len) {
		memcpy(buf, dp->dh.raw, dsr_opts_len);
		buf += dsr_opts_len;
	}

	/* Add payload */
	if (dp->payload_len && dp->payload)
		memcpy(buf, dp->payload, dp->payload_len);

	return skb;
}

int dsr_hw_header_create(struct dsr_pkt *dp, struct sk_buff *skb)
{

	struct sockaddr broadcast =
	    { AF_UNSPEC, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} };
	struct neighbor_info neigh_info;

	if (dp->dst.s_addr == DSR_BROADCAST)
		memcpy(neigh_info.hw_addr.sa_data, broadcast.sa_data, ETH_ALEN);
	else {
		/* Get hardware destination address */
		if (neigh_tbl_query(dp->nxt_hop, &neigh_info) < 0) {
			DEBUG
			    ("Could not get hardware address for next hop %s\n",
			     print_ip(dp->nxt_hop));
			return -1;
		}
	}

	if (skb->dev->hard_header) {
		skb->dev->hard_header(skb, skb->dev, ETH_P_IP,
				      neigh_info.hw_addr.sa_data, 0, skb->len);
	} else {
		DEBUG("Missing hard_header\n");
		return -1;
	}
	return 0;
}

static int dsr_dev_inetaddr_event(struct notifier_block *this,
				  unsigned long event, void *ptr)
{
	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
	struct in_device *indev;

	if (!ifa)
		return NOTIFY_DONE;

	indev = ifa->ifa_dev;

	if (!indev)
		return NOTIFY_DONE;

	switch (event) {
	case NETDEV_UP:
		DEBUG("inetdev UP\n");

		if (indev->dev == dsr_dev) {
			struct dsr_node *dnode;
			struct in_addr addr, bc;

			dnode = (struct dsr_node *)indev->dev->priv;

			dsr_node_lock(dnode);
			dnode->ifaddr.s_addr = ifa->ifa_address;
			dnode->bcaddr.s_addr = ifa->ifa_broadcast;

			dnode->slave_indev = in_dev_get(dnode->slave_dev);

                        /* Disable rp_filter and enable forwarding */
                        if (dnode->slave_indev) {
                                rp_filter = dnode->slave_indev->cnf.rp_filter;
                                forwarding = dnode->slave_indev->cnf.forwarding;                                dnode->slave_indev->cnf.rp_filter = 0;
                                dnode->slave_indev->cnf.forwarding = 1;
                        }			
			dsr_node_unlock(dnode);
			
			addr.s_addr = ifa->ifa_address;
			bc.s_addr = ifa->ifa_broadcast;
			
			DEBUG("New ip=%s broadcast=%s\n",
			      print_ip(addr), print_ip(bc));
		}
		break;
	default:
		break;
	};
	return NOTIFY_DONE;
}

static int dsr_dev_netdev_event(struct notifier_block *this,
				unsigned long event, void *ptr)
{
	struct net_device *dev = (struct net_device *)ptr;
	struct dsr_node *dnode = (struct dsr_node *)dsr_dev->priv;
	int slave_change = 0;

	if (!dev)
		return NOTIFY_DONE;

	switch (event) {
	case NETDEV_REGISTER:
		DEBUG("Netdev register %s\n", dev->name);
		if (dnode->slave_dev == NULL && 
		    strcmp(dev->name, dnode->slave_ifname) == 0) {
			
			DEBUG("Slave dev %s up\n", dev->name);
			
			dsr_node_lock(dnode);
			dnode->slave_dev = dev;
			dev_hold(dev);
			dsr_node_unlock(dnode);

			/* Reduce the MTU to allow DSR options of 100
			 * bytes. If larger, drop or implement
			 * fragmentation... ;-) Alternatively find a
			 * way to dynamically reduce the data size of
			 * packets depending on the size of the DSR
			 * header. */
			dsr_dev->mtu = dev->mtu - DSR_OPTS_MAX_SIZE;
			
			DEBUG("Registering packet type\n");
			dsr_packet_type.func = dsr_dev_llrecv;
			dsr_packet_type.dev = dev;
			dev_add_pack(&dsr_packet_type);
			
			slave_change = 1;
		}

		if (slave_change)
			DEBUG("New DSR slave interface %s\n", dev->name);
		break;
	case NETDEV_CHANGE:
		DEBUG("Netdev change\n");
		break;
	case NETDEV_UP:
		DEBUG("Netdev up %s\n", dev->name);
		if (ConfVal(PromiscOperation) &&
		    dev == dsr_dev && dnode->slave_dev)
			dev_set_promiscuity(dnode->slave_dev, +1);
		break;
	case NETDEV_UNREGISTER:
		DEBUG("Netdev unregister %s\n", dev->name);
		
		dsr_node_lock(dnode);
		if (dev == dnode->slave_dev) {
			dev_remove_pack(&dsr_packet_type);
			dsr_packet_type.func = NULL;
			slave_change = 1;
			dev_put(dev);
			dnode->slave_dev = NULL;
		}
		dsr_node_unlock(dnode);

		if (slave_change)
			DEBUG("DSR slave interface %s unregisterd\n",
			      dev->name);
		break;
	case NETDEV_DOWN:
		DEBUG("Netdev down %s\n", dev->name);
		if (dev == dsr_dev) {
			if (dnode->slave_dev && ConfVal(PromiscOperation))
				dev_set_promiscuity(dnode->slave_dev, -1);

			dsr_node_lock(dnode);
                        if (dnode->slave_indev) {
                                dnode->slave_indev->cnf.rp_filter = rp_filter;
                                dnode->slave_indev->cnf.forwarding = forwarding;                                in_dev_put(dnode->slave_indev);
                                dnode->slave_indev = NULL;
                        }
                        dsr_node_unlock(dnode);
		} else if (dev == dnode->slave_dev && dnode->slave_indev) {
			dsr_node_lock(dnode);
			dnode->slave_indev->cnf.rp_filter = rp_filter;
			dnode->slave_indev->cnf.forwarding = forwarding;                                in_dev_put(dnode->slave_indev);
			dnode->slave_indev = NULL;
                        dsr_node_unlock(dnode);
		} 
		break;
	default:
		break;
	};

	return NOTIFY_DONE;
}

static int dsr_dev_start_xmit(struct sk_buff *skb, struct net_device *dev);
static struct net_device_stats *dsr_dev_get_stats(struct net_device *dev);

static int dsr_dev_set_address(struct net_device *dev, void *p)
{
	struct sockaddr *sa = p;

	if (!is_valid_ether_addr(sa->sa_data))
		return -EADDRNOTAVAIL;

	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
	return 0;
}

/* fake multicast ability */
static void set_multicast_list(struct net_device *dev)
{
}

#ifdef CONFIG_NET_FASTROUTE
static int dsr_dev_accept_fastpath(struct net_device *dev, 
				   struct dst_entry *dst)
{
	return -1;
}
#endif
static int dsr_dev_open(struct net_device *dev)
{
	netif_start_queue(dev);
	return 0;
}

static int dsr_dev_stop(struct net_device *dev)
{
	netif_stop_queue(dev);
	return 0;
}

static void dsr_dev_uninit(struct net_device *dev)
{
	struct dsr_node *dnode = (struct dsr_node *)dev->priv;

	dsr_node_lock(dnode);
	
	if (dnode->slave_dev)
		dev_put(dnode->slave_dev);

	if (dnode->slave_indev)
		in_dev_put(dnode->slave_indev);

	dsr_node_unlock(dnode);

	if (dsr_packet_type.func) {
		DEBUG("Removing pack\n");
		dev_remove_pack(&dsr_packet_type);
		dsr_packet_type.func = NULL;
	}

	dev_put(dev);
	dsr_node = NULL;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
static int __init dsr_dev_setup(struct net_device *dev)
#else
static void __init dsr_dev_setup(struct net_device *dev)
#endif
{
	/* Fill in device structure with ethernet-generic values. */
	ether_setup(dev);
	/* Initialize the device structure. */
	dev->get_stats = dsr_dev_get_stats;
	dev->uninit = dsr_dev_uninit;
	dev->open = dsr_dev_open;
	dev->stop = dsr_dev_stop;

	dev->hard_start_xmit = dsr_dev_start_xmit;
	dev->set_multicast_list = set_multicast_list;
	dev->set_mac_address = dsr_dev_set_address;
#ifdef CONFIG_NET_FASTROUTE
	dev->accept_fastpath = dsr_dev_accept_fastpath;
#endif
	dev->tx_queue_len = 0;
	dev->flags |= IFF_NOARP;
	dev->flags &= ~IFF_MULTICAST;
	SET_MODULE_OWNER(dev);
	//random_ether_addr(dev->dev_addr);
	get_random_bytes(dev->dev_addr, 6);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	return 0;
#endif
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static int dsr_dev_llrecv(struct sk_buff *skb,
			  struct net_device *indev, 
			  struct packet_type *pt)
#else
static int dsr_dev_llrecv(struct sk_buff *skb,
			  struct net_device *indev, 
			  struct packet_type *pt, 
			  struct net_device *orig_dev)
#endif
{
	/* DEBUG("Packet recvd\n"); */

/* 	if (do_mackill(skb->mac.raw + ETH_ALEN)) { */
/* 		kfree_skb(skb); */
/* 		return 0; */
/* 	} */
	if (skb->pkt_type == PACKET_OTHERHOST)
		dsr_ip_recv(skb);
	else
		dev_kfree_skb_any(skb);

	return 0;
}

int dsr_dev_deliver(struct dsr_pkt *dp)
{
	struct sk_buff *skb = NULL;
	struct ethhdr *ethh;
	int len;

	if (!dp)
		return -1;

	/* Super ugly hack to fix record route options */
	if (dp->skb->nh.iph->ihl > 5) {
		struct ip_options *opt = &(IPCB(dp->skb)->opt);
		unsigned char *ptr = dp->skb->nh.raw;
	
		if (opt->rr) {	
			struct ipopt {
				u_int8_t code;
				u_int8_t len;
				u_int8_t off;
			} *rr = (struct ipopt *)&ptr[opt->rr];
			
			if (rr->off < 32) {
				
				/* Remove the last recorded address since it will
				 * recorded again when passed up the IP stack for the
				 * second time on the virtual interface. */
				rr->off -= 4;
				rr->len -= 4;
				opt->optlen -= 4;
			}
			/* ip_send_check(dp->skb->nh.iph); */
		}
	}

	if (dp->dh.raw)
		len = dsr_opt_remove(dp);

	skb = dsr_skb_create(dp, dsr_dev);

	if (!skb) {
		DEBUG("Could not allocate skb\n");
		dsr_pkt_free(dp);
		return -1;
	}

	/* Need to make hardware header visible again since we are going down a
	 * layer */
	skb->mac.raw = skb->data - dsr_dev->hard_header_len;
	skb->ip_summed = CHECKSUM_UNNECESSARY;
	
	ethh = (struct ethhdr *)skb->mac.raw;

	memcpy(ethh->h_dest, dsr_dev->dev_addr, ETH_ALEN);
	memset(ethh->h_source, 0, ETH_ALEN);
	ethh->h_proto = htons(ETH_P_IP);

	dsr_node_lock(dsr_node);
	dsr_node->stats.rx_packets++;
	dsr_node->stats.rx_bytes += skb->len;
	dsr_node_unlock(dsr_node);

	netif_rx(skb);

	dsr_pkt_free(dp);

	return 0;
}

int dsr_dev_xmit(struct dsr_pkt *dp)
{
	struct sk_buff *skb;
	struct net_device *slave_dev;
	struct in_addr dst;
	int res = -1;
	int len = 0;	

	if (!dp)
		return -1;

	if (dp->flags & PKT_REQUEST_ACK)
		maint_buf_add(dp);

	dsr_node_lock(dsr_node);

	if (dsr_node->slave_dev)
		slave_dev = dsr_node->slave_dev;
	else {
		dsr_node_unlock(dsr_node);
		goto out_err;
	}
	dsr_node_unlock(dsr_node);

	skb = dsr_skb_create(dp, slave_dev);

	if (!skb) {
		DEBUG("Could not create skb!\n");
		goto out_err;
	}

	/* Create hardware header */
	if (dsr_hw_header_create(dp, skb) < 0) {
		DEBUG("Could not create hardware header\n");
		dev_kfree_skb_any(skb);
		goto out_err;
	}
	
	len = skb->len;
	dst.s_addr = skb->nh.iph->daddr;
	
	DEBUG("Sending %d bytes data_len=%d %s : %s\n",
	      len, skb->data_len,
	      print_eth(skb->mac.raw),
	      print_ip(dst));
		
	/* TODO: Should consider using ip_finish_output instead */
	res = dev_queue_xmit(skb);

	if (res < 0)
		goto out_err;

	dsr_node_lock(dsr_node);
	dsr_node->stats.tx_packets++;
	dsr_node->stats.tx_bytes += len;
	dsr_node_unlock(dsr_node);

out_err:
	dsr_pkt_free(dp);

	return res;
}

/* Main receive function for packets originated in user space */
static int dsr_dev_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct dsr_node *dnode = (struct dsr_node *)dev->priv;
	struct ethhdr *ethh;
	struct dsr_pkt *dp;
#ifdef DEBUG
	atomic_inc(&num_pkts);
#endif
	if (dnode->slave_dev == NULL) {
		dev_kfree_skb_any(skb);
		DEBUG("Packet dropped\n");
		return 0;
	}

	ethh = (struct ethhdr *)skb->data;

	switch (ntohs(ethh->h_proto)) {
	case ETH_P_IP:

		DEBUG("dst=%s len=%d\n",
		      print_ip(*((struct in_addr *)&skb->nh.iph->daddr)),
		      skb->len);

		dp = dsr_pkt_alloc(skb);
		
		if (!dp) {
			dev_kfree_skb_any(skb);
			return 0;
		}			

		dsr_start_xmit(dp);
		break;
	default:
		DEBUG("Unknown packet type, dropping...\n");
		dev_kfree_skb_any(skb);
	}
	return 0;
}

static struct net_device_stats *dsr_dev_get_stats(struct net_device *dev)
{
	return &(((struct dsr_node *)dev->priv)->stats);
}

static struct notifier_block netdev_notifier = {
      notifier_call:dsr_dev_netdev_event,
};

/* Notifier for inetaddr addition/deletion events.  */
static struct notifier_block inetaddr_notifier = {
	.notifier_call = dsr_dev_inetaddr_event,
};

int dsr_dev_init(char *ifname)
{
	int res = 0;
	struct dsr_node *dnode;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	dsr_dev = alloc_etherdev(sizeof(struct dsr_node));

	if (!dsr_dev)
		return -ENOMEM;
	
	dsr_dev->init = dsr_dev_setup;

	dev_alloc_name(dsr_dev, "dsr%d");
	dsr_dev->init = &dsr_dev_setup;
#else
	dsr_dev = alloc_netdev(sizeof(struct dsr_node), "dsr%d", dsr_dev_setup);
	if (!dsr_dev)
		return -ENOMEM;
#endif
	dnode = dsr_node = (struct dsr_node *)dsr_dev->priv;

	dsr_node_init(dnode, ifname);

	if (ifname) {
		memcpy(dnode->slave_ifname, ifname, IFNAMSIZ);
	} else {
		struct net_device *tmp_dev;
		
		read_lock(&dev_base_lock);
		for (tmp_dev = dev_base; tmp_dev != NULL; 
		     tmp_dev = tmp_dev->next) {

			if (tmp_dev->get_wireless_stats) {
				memcpy(dnode->slave_ifname, tmp_dev->name, IFNAMSIZ);
			
				read_unlock(&dev_base_lock);
				goto dev_ok;
			}
		}
		read_unlock(&dev_base_lock);
	
		DEBUG("No proper slave device found\n");
		res = -1;
		goto cleanup_netdev;
	}
dev_ok:	
	DEBUG("Slave device is %s\n", dnode->slave_ifname);	
		
	res = register_netdev(dsr_dev);

	dsr_packet_type.func = NULL;

	if (res < 0)
		goto cleanup_netdev;

	res = register_netdevice_notifier(&netdev_notifier);

	if (res < 0)
		goto cleanup_netdev_register;

	res = register_inetaddr_notifier(&inetaddr_notifier);

	if (res < 0)
		goto cleanup_netdevice_notifier;

	/* We increment usage count since we hold a reference */
	dev_hold(dsr_dev);

	return 0;
      cleanup_netdevice_notifier:
	unregister_netdevice_notifier(&netdev_notifier);
      cleanup_netdev_register:
	unregister_netdev(dsr_dev);
      cleanup_netdev:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	free_netdev(dsr_dev);
#else
	kfree(dsr_dev);
#endif
	return res;
}

void __exit dsr_dev_cleanup(void)
{

	unregister_netdevice_notifier(&netdev_notifier);
	unregister_inetaddr_notifier(&inetaddr_notifier);
	unregister_netdev(dsr_dev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	free_netdev(dsr_dev);
#else
	kfree(dsr_dev);
#endif
}