In the previous article (provided here), we looked a t a skeleton network Linux device driver module. In the current article, we will add some more substance to the skeleton device driver by providing a path back up the stack. That is, a packet sent to the virtual network device driver will be sent up the stack and some one who is listening on that interface can receive the packet.
The code for the loop-back virtual network device driver code is provided below. The code does not attempt to save the received packet from the stack in the driver. It immediately processes it and sends the packet up.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/sockios.h>
#define DRV_NAME "vivek_net"
MODULE_AUTHOR("Vivekananda Uppunda");
MODULE_DESCRIPTION("Virtual network device driver with loopback");
MODULE_LICENSE("GPL");
/* -------------------------------------------------- */
/* Private data */
/* -------------------------------------------------- */
struct vnet_priv {
struct sk_buff_head tx_queue; /* software TX buffer */
};
/* -------------------------------------------------- */
/* Software RX processing (simple loopback) */
/* -------------------------------------------------- */
static void vnet_process_tx_queue(struct net_device *dev)
{
struct vnet_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
char buf[100];
int offset = 0;
while ((skb = skb_dequeue(&priv->tx_queue)) != NULL) {
/* Convert TX skb into RX skb */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
netif_rx(skb);
}
}
/* -------------------------------------------------- */
/* Transmit function */
/* -------------------------------------------------- */
static netdev_tx_t vnet_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct vnet_priv *priv = netdev_priv(dev);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
/* Queue packet into software buffer */
skb_queue_tail(&priv->tx_queue, skb);
/* Immediately process queued packets (simple model) */
vnet_process_tx_queue(dev);
return NETDEV_TX_OK;
}
/* -------------------------------------------------- */
/* Open / Close */
/* -------------------------------------------------- */
static int vnet_open(struct net_device *dev)
{
struct vnet_priv *priv = netdev_priv(dev);
skb_queue_head_init(&priv->tx_queue);
netif_start_queue(dev);
pr_info("%s: device opened\n", dev->name);
return 0;
}
static int vnet_stop(struct net_device *dev)
{
struct vnet_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
netif_stop_queue(dev);
/* Flush software queue */
while ((skb = skb_dequeue(&priv->tx_queue)) != NULL)
dev_kfree_skb(skb);
pr_info("%s: device closed\n", dev->name);
return 0;
}
/* -------------------------------------------------- */
/* net_device operations */
/* -------------------------------------------------- */
static const struct net_device_ops vnet_netdev_ops = {
.ndo_open = vnet_open,
.ndo_stop = vnet_stop,
.ndo_start_xmit = vnet_start_xmit,
};
/* -------------------------------------------------- */
/* Device setup */
/* -------------------------------------------------- */
static void vnet_setup(struct net_device *dev)
{
ether_setup(dev);
dev->netdev_ops = &vnet_netdev_ops;
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_HW_CSUM;
eth_hw_addr_random(dev);
}
/* -------------------------------------------------- */
/* Module init / exit */
/* -------------------------------------------------- */
static struct net_device *vnet_dev;
static int __init vnet_init(void)
{
int ret;
vnet_dev = alloc_netdev(sizeof(struct vnet_priv),
DRV_NAME"%d",
NET_NAME_UNKNOWN,
vnet_setup);
if (!vnet_dev)
return -ENOMEM;
ret = register_netdev(vnet_dev);
if (ret) {
free_netdev(vnet_dev);
return ret;
}
pr_info(DRV_NAME ": virtual network device registered\n");
return 0;
}
static void __exit vnet_exit(void)
{
unregister_netdev(vnet_dev);
free_netdev(vnet_dev);
pr_info(DRV_NAME ": module unloaded\n");
}
module_init(vnet_init);
module_exit(vnet_exit);
The above code incorporates a method to send back the received network packet back to the stack. The API that performs this task is netif_rx . The software txqueue saves the packet. When the received packet is processed,
- It is dequeued from the transmit queue
- The packet’s upper layer protocol to be sent to is determined
- Packet stats are updated
- netif_rx(skb) is invoked to send the packet up the stack
We will look at a sample application code to send packet to this network driver and see if the application receives the packet back up in the next article.