📄 snull.c
字号:
* Ethhdr is 14 bytes, but the kernel arranges for iphdr
* to be aligned (i.e., ethhdr is unaligned)
*/
ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
saddr = &ih->saddr;
daddr = &ih->daddr;
((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
((u8 *)daddr)[2] ^= 1;
ih->check = 0; /* and rebuild the checksum (ip needs it) */
ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
if (dev == snull_devs)
PDEBUGG("%08x:%05i --> %08x:%05i\n",
ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
else
PDEBUGG("%08x:%05i <-- %08x:%05i\n",
ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
/*
* Ok, now the packet is ready for transmission: first simulate a
* receive interrupt on the twin device, then a
* transmission-done on the transmitting device
*/
dest = snull_devs + (dev==snull_devs ? 1 : 0);
priv = (struct snull_priv *) dest->priv;
priv->status = SNULL_RX_INTR;
priv->rx_packetlen = len;
priv->rx_packetdata = buf;
snull_interrupt(0, dest, NULL);
priv = (struct snull_priv *) dev->priv;
priv->status = SNULL_TX_INTR;
priv->tx_packetlen = len;
priv->tx_packetdata = buf;
if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
/* Simulate a dropped transmit interrupt */
netif_stop_queue(dev);
PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,
(unsigned long) priv->stats.tx_packets);
}
else
snull_interrupt(0, dev, NULL);
}
/*
* Transmit a packet (called by the kernel)
*/
int snull_tx(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data;
struct snull_priv *priv = (struct snull_priv *) dev->priv;
#ifndef LINUX_24
if (dev->tbusy || skb == NULL) {
PDEBUG("tint for %p, tbusy %ld, skb %p\n", dev, dev->tbusy, skb);
snull_tx_timeout (dev);
if (skb == NULL)
return 0;
}
#endif
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
data = skb->data;
dev->trans_start = jiffies; /* save the timestamp */
/* Remember the skb, so we can free it at interrupt time */
priv->skb = skb;
/* actual deliver of data is device-specific, and not shown here */
snull_hw_tx(data, len, dev);
return 0; /* Our simple device can not fail */
}
/*
* Deal with a transmit timeout.
*/
void snull_tx_timeout (struct net_device *dev)
{
struct snull_priv *priv = (struct snull_priv *) dev->priv;
PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
jiffies - dev->trans_start);
priv->status = SNULL_TX_INTR;
snull_interrupt(0, dev, NULL);
priv->stats.tx_errors++;
netif_wake_queue(dev);
return;
}
/*
* Ioctl commands
*/
int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
PDEBUG("ioctl\n");
return 0;
}
/*
* Return statistics to the caller
*/
struct net_device_stats *snull_stats(struct net_device *dev)
{
struct snull_priv *priv = (struct snull_priv *) dev->priv;
return &priv->stats;
}
/*
* This function is called to fill up an eth header, since arp is not
* available on the interface
*/
#ifndef LINUX_20
int snull_rebuild_header(struct sk_buff *skb)
{
struct ethhdr *eth = (struct ethhdr *) skb->data;
struct net_device *dev = skb->dev;
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
return 0;
}
#else /* LINUX_20 */
int snull_rebuild_header(void *buff, struct net_device *dev, unsigned long dst,
struct sk_buff *skb)
{
struct ethhdr *eth = (struct ethhdr *)buff;
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
return 0;
}
#endif /* LINUX_20 */
int snull_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr,
unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
eth->h_proto = htons(type);
memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
return (dev->hard_header_len);
}
/*
* The "change_mtu" method is usually not needed.
* If you need it, it must be like this.
*/
int snull_change_mtu(struct net_device *dev, int new_mtu)
{
unsigned long flags;
spinlock_t *lock = &((struct snull_priv *) dev->priv)->lock;
/* check ranges */
if ((new_mtu < 68) || (new_mtu > 1500))
return -EINVAL;
/*
* Do anything you need, and the accept the value
*/
spin_lock_irqsave(lock, flags);
dev->mtu = new_mtu;
spin_unlock_irqrestore(lock, flags);
return 0; /* success */
}
/*
* The init function (sometimes called probe).
* It is invoked by register_netdev()
*/
int snull_init(struct net_device *dev)
{
#if 0
/*
* Make the usual checks: check_region(), probe irq, ... -ENODEV
* should be returned if no device found. No resource should be
* grabbed: this is done on open().
*/
#endif
/*
* Then, assign other fields in dev, using ether_setup() and some
* hand assignments
*/
ether_setup(dev); /* assign some of the fields */
dev->open = snull_open;
dev->stop = snull_release;
dev->set_config = snull_config;
dev->hard_start_xmit = snull_tx;
dev->do_ioctl = snull_ioctl;
dev->get_stats = snull_stats;
dev->change_mtu = snull_change_mtu;
dev->rebuild_header = snull_rebuild_header;
dev->hard_header = snull_header;
#ifdef HAVE_TX_TIMEOUT
dev->tx_timeout = snull_tx_timeout;
dev->watchdog_timeo = timeout;
#endif
/* keep the default flags, just add NOARP */
dev->flags |= IFF_NOARP;
#ifndef LINUX_20
dev->hard_header_cache = NULL; /* Disable caching */
#endif
SET_MODULE_OWNER(dev);
/*
* Then, allocate the priv field. This encloses the statistics
* and a few private fields.
*/
dev->priv = kmalloc(sizeof(struct snull_priv), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct snull_priv));
spin_lock_init(& ((struct snull_priv *) dev->priv)->lock);
return 0;
}
/*
* The devices
*/
#ifdef LINUX_24
struct net_device snull_devs[2] = {
{ init: snull_init, }, /* init, nothing more */
{ init: snull_init, }
};
#else /* pre-2.4 */
char snull_names[16];
struct net_device snull_devs[2] = {
{
name: snull_names,
init: snull_init, /* init function */
},
{
name: snull_names+8,
init: snull_init, /* init function */
}
};
#endif /* LINUX_24 */
/*
* Finally, the module stuff
*/
int snull_init_module(void)
{
int result, i, device_present = 0;
snull_eth = eth; /* copy the cfg datum in the non-static place */
if (!snull_eth) { /* call them "sn0" and "sn1" */
strcpy(snull_devs[0].name, "sn0");
strcpy(snull_devs[1].name, "sn1");
} else { /* use automatic assignment */
#ifdef LINUX_24
strcpy(snull_devs[0].name, "eth%d");
strcpy(snull_devs[1].name, "eth%d");
#else
snull_devs[0].name[0] = snull_devs[1].name[0] = ' ';
#endif
}
for (i=0; i<2; i++)
if ( (result = register_netdev(snull_devs + i)) )
printk("snull: error %i registering device \"%s\"\n",
result, snull_devs[i].name);
else device_present++;
#ifndef SNULL_DEBUG
EXPORT_NO_SYMBOLS;
#endif
return device_present ? 0 : -ENODEV;
}
void snull_cleanup(void)
{
int i;
for (i=0; i<2; i++) {
kfree(snull_devs[i].priv);
unregister_netdev(snull_devs + i);
}
return;
}
module_init(snull_init_module);
module_exit(snull_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -