VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 3
+SUBLEVEL = 4
all: Version zImage
#bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'NE2000/NE1000 support' CONFIG_NE2000 n
-bool 'WD80E3 support' CONFIG_WD80x3 y
+bool 'WD80*3 support' CONFIG_WD80x3 y
bool 'SMC Ultra support' CONFIG_ULTRA n
bool '3c501 support' CONFIG_EL1 n
bool '3c503 support' CONFIG_EL2 n
#include <asm/io.h>
#include <errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c, should be in a *.h file. */
extern struct device *irq2dev_map[16];
#endif
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
\f
/* Index to functions. */
int el1_probe(struct device *dev);
static void el_reset(struct device *dev);
static int el1_close(struct device *dev);
static struct enet_statistics *el1_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
#define EL_NAME "EtherLink 3c501"
dev->hard_start_xmit = &el_start_xmit;
dev->stop = &el1_close;
dev->get_stats = &el1_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
-
- /* Fill in the generic field of the device structure. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
+ /* Setup the generic properties */
+ ether_setup(dev);
return 0;
}
return 0;
}
- /* Fill in the ethernet header. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
if (skb->len <= 0)
return 0;
static void
el_receive(struct device *dev)
{
- int sksize, pkt_len;
+ int pkt_len;
struct sk_buff *skb;
pkt_len = inw(RX_LOW);
}
outb(AX_SYS, AX_CMD);
- sksize = sizeof(struct sk_buff) + pkt_len;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
outw(0x00, GP_LOW);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
el_status.stats.rx_dropped++;
return;
} else {
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
insb(DATAPORT, skb->data, pkt_len);
-#ifdef HAVE_NETIF_RX
- netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_skbmem(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
+ netif_rx(skb);
el_status.stats.rx_packets++;
}
return;
return &el_status.stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
inb(RX_STATUS);
}
}
-#endif
\f
/*
* Local variables:
#include <asm/io.h>
#include <asm/system.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "8390.h"
#include "3c503.h"
#include <asm/dma.h>
#include <linux/errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
-
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#else
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include <linux/malloc.h>
-#endif
/* use 0 for production, 1 for verification, 2..7 for debug */
#ifndef NET_DEBUG
dev->hard_start_xmit = el16_send_packet;
dev->get_stats = el16_get_stats;
- /* Fill in the fields of the device structure with ethernet-generic values.
- This should be in a common file instead of per-driver. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
+ ether_setup(dev); /* Generic ethernet behaviour */
return 0;
}
return 0;
}
- /* For ethernet, fill in the header. This should really be done by a
- higher level, rather than duplicated for each ethernet adaptor. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
/* Block a timer-based transmit from overlapping. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
if (frame_status & 0x0080) lp->stats.rx_length_errors++;
} else {
/* Malloc up new buffer. */
- int sksize;
struct sk_buff *skb;
pkt_len &= 0x3fff;
- sksize = sizeof(struct sk_buff) + pkt_len;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
memcpy(skb->data, data_frame + 5, pkt_len);
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_skbmem(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
#include <asm/bitops.h>
#include <asm/io.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#endif
#ifdef EL3_DEBUG
/* First check for a board on the EISA bus. */
if (EISA_bus) {
for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
- /* Check the standard EISA ID register for an encoded '3Com'. */
- if (inw(ioaddr + 0xC80) != 0x6d50)
+ if (inw(ioaddr) != 0x6d50)
continue;
- /* Change the register set to the configuration window 0. */
- outw(0x0800, ioaddr + 0xC80 + EL3_CMD);
-
irq = inw(ioaddr + 8) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
- /* Restore the "Product ID" to the EEPROM read register. */
- read_eeprom(ioaddr, 3);
+ /* Restore the "Manufacturer ID" to the EEPROM read register. */
+ /* The manual says to restore "Product ID" (reg. 3). !???! */
+ read_eeprom(ioaddr, 7);
/* Was the EISA code an add-on hack? Nahhhhh... */
goto found;
#endif
/* Fill in the generic fields of the device structure. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-
+ ether_setup(dev);
return 0;
}
return 0;
}
- /* Fill in the ethernet header. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
if (skb->len <= 0)
return 0;
if (++i > 10) {
printk("%s: Infinite loop in interrupt, status %4.4x.\n",
dev->name, status);
- /* Clear all interrupts we have handled. */
+ /* Clear all interrupts we have handled */
outw(0x68FF, ioaddr + EL3_CMD);
break;
}
if ( (! (rx_status & 0x4000))
|| ! (rx_status & 0x1000)) { /* Dribble bits are OK. */
short pkt_len = rx_status & 0x7ff;
- int sksize = sizeof(struct sk_buff) + pkt_len + 3;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len+3, GFP_ATOMIC);
if (el3_debug > 4)
- printk(" Receiving packet size %d status %4.4x.\n",
+ printk("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
continue;
} else {
printk("%s: receive buffers full.\n", dev->name);
- kfree_s(skb, sksize);
+ kfree_s(skb, FREE_READ);
}
#endif
} else if (el3_debug)
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, sksize);
+ dev->name, pkt_len);
}
lp->stats.rx_dropped++;
outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
#include <linux/in.h>
#include <linux/interrupt.h>
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include "8390.h"
else {
/* The 8390 probably hasn't gotten on the cable yet. */
printk(KERN_DEBUG "%s: Possible network cable problem?\n", dev->name);
- if (ei_local->stat.tx_packets == 0)
- ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ if(ei_local->stat.tx_packets==0)
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
}
/* Try to restart the card. Perhaps the user has fixed something. */
ei_reset_8390(dev);
dev_tint(dev);
return 0;
}
- /* Fill in the ethernet header. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
length = skb->len;
if (skb->len <= 0)
rx_frame.next);
ei_local->stat.rx_errors++;
} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
if (ei_debug > 1)
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, sksize);
+ dev->name, pkt_len);
ei_local->stat.rx_dropped++;
break;
} else {
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* Initialize the rest of the 8390 device structure. */
int ethdev_init(struct device *dev)
{
- int i;
-
if (ei_debug > 1)
printk(version);
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
#endif
-
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-
+
+ ether_setup(dev);
+
return 0;
}
\f
# are difficult for users to deal with.
include CONFIG
-NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o)
+NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o) net.a(loopback.o)
CFLAGS := $(CFLAGS) -I../../net/inet
CPP := $(CPP) -I../../net/inet
*/
#include <linux/config.h>
#include <linux/ddi.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#define LOOPBACK /* always present, right? */
#include <asm/dma.h>
#include <errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c, in ioport.h for later versions. */
extern struct device *irq2dev_map[16];
#endif
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 2
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
\f
/* Check for a network adaptor of this type, and return '0' iff one exists.
dev->stop = net_close;
dev->hard_start_xmit = net_send_packet;
dev->get_stats = net_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
-
- /* Fill in the fields of the device structure with ethernet-generic values.
- This should be in a common file instead of per-driver. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+
+ ether_setup(dev);
return 0;
}
return 0;
}
- /* For ethernet, fill in the header. This should really be done by a
- higher level, rather than duplicated for each ethernet adaptor. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
} else {
ushort pkt_len = inw(ioaddr + DATAPORT);
/* Malloc up new buffer. */
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
if (pkt_len > 1550) {
lp->stats.rx_errors++;
break;
}
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet (len %d).\n",
dev->name, pkt_len);
lp->stats.rx_dropped++;
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
printk(".\n");
}
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_s(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
if (--boguscount <= 0)
return &lp->stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
} else
outw(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */
}
-#endif
\f
/*
* Local variables:
#include <asm/dma.h>
#include <errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include "atp.h"
extern int atp_probe(struct device *dev);
static int atp_probe1(struct device *dev, short ioaddr);
-static void init_dev(struct device *dev);
static void get_node_ID(struct device *dev);
static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
static int net_open(struct device *dev);
static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
\f
/* Check for a network adaptor of this type, and return '0' iff one exists.
printk(version);
/* Initialize the device structure. */
- init_dev(dev);
+ ether_setup(dev);
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct net_local));
dev->stop = net_close;
dev->hard_start_xmit = net_send_packet;
dev->get_stats = net_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
return 0;
}
-/* Fill in the fields of the device structure with ethernet-generic values.
- This should be in a common file instead of per-driver. */
-static void init_dev(struct device *dev)
-{
- int i;
-
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-}
-
/* Read the station address PROM, usually a word-wide EEPROM. */
static void get_node_ID(struct device *dev)
{
return 0;
}
- /* For ethernet, fill in the header. This should really be done by a
- higher level, rather than duplicated for each ethernet adaptor. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
} else {
/* Malloc up new buffer. */
int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
goto done;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
data[12], data[13]);
}
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_s(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
done:
return &lp->stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
write_reg_high(ioaddr, CMR2, lp->addr_mode);
}
-#endif
\f
/*
* Local variables:
#include <linux/sched.h>
#include <asm/bitops.h>
#include <asm/io.h>
-#include "dev.h"
+#include <linux/netdevice.h>
/*#include <asm/system.h>*/
struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
#include <asm/system.h>
#include <errno.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#define netstats enet_statistics
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size,pri) (struct sk_buff *)kmalloc(size,pri)
-#endif
-
/**************************************************
* *
* Definition of D-Link Ethernet Pocket adapter *
*
* This fix is better than changing in tcp.h IMHO
*/
+#if 0
tcp_prot.rspace = d_link_rspace; /* was: sock_rspace */
+#endif
+
return 0;
}
free_irq(D_LINK_IRQ);
irq2dev_map[D_LINK_IRQ] = NULL;
dev->start = 0;
+#if 0
tcp_prot.rspace = sock_rspace; /* see comment above! */
-
+#endif
return 0;
}
return 0;
}
- /* For ethernet, fill in the header (hardware addresses) with an arp. */
- if (!skb->arp)
- if(dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp = 1;
-
if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */
tickssofar = jiffies - dev->trans_start;
int i;
int read_from;
int size;
- int sksize;
register unsigned char *buffer;
cli();
if ((size < 32) || (size > 1535))
printk("%s: Bogus packet size %d.\n", dev->name, size);
- sksize = sizeof(struct sk_buff) + size;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(size, GFP_ATOMIC);
sti();
if (skb == NULL) {
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, sksize);
+ dev->name, size);
return;
}
/* else */
skb->lock = 0;
- skb->mem_len = sksize;
- skb->mem_addr = skb;
/* 'skb->data' points to the start of sk_buff data area. */
buffer = skb->data;
/* Initialize the device structure. */
dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct netstats));
-
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
dev->get_stats = get_stats;
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
dev->open = d_link_open;
dev->stop = d_link_close;
dev->hard_start_xmit = &d_link_start_xmit;
- /* These are ethernet specific. */
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-
+ ether_setup(dev);
+
select_prn();
return 0;
}
#include <asm/io.h>
#include <asm/dma.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "iow.h" /* left in for pl13/14 compatibility... */
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include "depca.h"
#ifdef DEPCA_DEBUG
#endif /* CRC_POLYNOMIAL */
#endif /* HAVE_MULTICAST */
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif /* HAVE_ALLOC_SKB */
-
/*
** The DEPCA Rx and Tx ring descriptors.
*/
if (status & R_BUFF) lp->stats.rx_fifo_errors++;
} else { /* Malloc up new buffer, compatible with net-2e. */
short pkt_len = lp->rx_ring[entry].msg_length;
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, deferring packet.\n", dev->name);
lp->stats.rx_dropped++; /* Really, deferred. */
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
memcpy(skb->data,
** Notify the upper protocol layers that there is another
** packet to handle
*/
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_skbmem(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
+
/* eexpress.c: Intel EtherExpress device driver for Linux. */
/*
Written 1993 by Donald Becker.
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
-#include <linux/in.h>
#include <linux/string.h>
+#include <linux/in.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#else
-/* This isn't quite right, but it's the best version define I can find right now. */
#include <linux/malloc.h>
-#endif
/* use 0 for production, 1 for verification, 2..7 for debug */
#ifndef NET_DEBUG
static void eexp_rx(struct device *dev);
static int eexp_close(struct device *dev);
static struct enet_statistics *eexp_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
static int read_eeprom(int ioaddr, int location);
static void hardware_send_packet(struct device *dev, void *buf, short length);
dev->stop = eexp_close;
dev->hard_start_xmit = eexp_send_packet;
dev->get_stats = eexp_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
-
- /* Fill in the fields of the device structure with ethernet-generic values.
- This should be in a common file instead of per-driver. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+
+ ether_setup(dev);
+
return 0;
}
return 0;
}
- /* For ethernet, fill in the header. This should really be done by a
- higher level, rather than duplicated for each ethernet adaptor. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
/* Block a timer-based transmit from overlapping. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
return &lp->stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
}
}
-#endif
/* The horrible routine to read a word from the serial EEPROM. */
if (frame_status & 0x0080) lp->stats.rx_length_errors++;
} else {
/* Malloc up new buffer. */
- int sksize;
struct sk_buff *skb;
pkt_len &= 0x3fff;
- sksize = sizeof(struct sk_buff) + pkt_len;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_s(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
#include <asm/system.h>
#include <asm/io.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "8390.h"
#ifndef HAVE_PORTRESERVE
#include <asm/io.h>
#include <asm/dma.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#ifndef HAVE_PORTRESERVE
#define check_region(addr, size) 0
#define snarf_region(addr, size) do ; while(0)
#endif
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif
-
struct device *init_etherdev(struct device *dev, int sizeof_private,
unsigned long *mem_startp);
dev->hard_start_xmit = &lance_start_xmit;
dev->stop = &lance_close;
dev->get_stats = &lance_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
return mem_start;
}
return 0;
}
- /* Fill in the ethernet header. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
if (skb->len <= 0)
return 0;
} else {
/* Malloc up new buffer, compatible with net-2e. */
short pkt_len = lp->rx_ring[entry].msg_length;
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, deferring packet.\n", dev->name);
lp->stats.rx_dropped++; /* Really, deferred. */
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
memcpy(skb->data,
(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
pkt_len);
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_skbmem(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
return &lp->stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
outw(0, ioaddr+LANCE_ADDR);
outw(0x0142, ioaddr+LANCE_DATA); /* Resume normal operation. */
}
-#endif
#ifdef HAVE_DEVLIST
static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
--- /dev/null
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Pseudo-driver for the loopback interface.
+ *
+ * Version: @(#)loopback.c 1.0.4b 08/16/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/if_ether.h> /* For the statistics structure. */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+
+static int
+loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ int done;
+
+ DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
+ if (skb == NULL || dev == NULL) return(0);
+
+ cli();
+ if (dev->tbusy != 0) {
+ sti();
+ printk("loopback error: called by %08lx\n",
+ ((unsigned long *)&skb)[-1]);
+ stats->tx_errors++;
+ return(1);
+ }
+ dev->tbusy = 1;
+ sti();
+
+ start_bh_atomic();
+ done = dev_rint(skb->data, skb->len, 0, dev);
+ if (skb->free) kfree_skb(skb, FREE_WRITE);
+ end_bh_atomic();
+
+ while (done != 1) {
+ start_bh_atomic();
+ done = dev_rint(NULL, 0, 0, dev);
+ end_bh_atomic();
+ }
+ stats->tx_packets++;
+
+ dev->tbusy = 0;
+
+#if 1
+ __asm__("cmpl $0,_intr_count\n\t"
+ "jne 1f\n\t"
+ "movl _bh_active,%%eax\n\t"
+ "testl _bh_mask,%%eax\n\t"
+ "je 1f\n\t"
+ "incl _intr_count\n\t"
+ "call _do_bottom_half\n\t"
+ "decl _intr_count\n"
+ "1:"
+ :
+ :
+ : "ax", "dx", "cx");
+#endif
+
+ return(0);
+}
+
+static struct enet_statistics *
+get_stats(struct device *dev)
+{
+ return (struct enet_statistics *)dev->priv;
+}
+
+/* Initialize the rest of the LOOPBACK device. */
+int
+loopback_init(struct device *dev)
+{
+ int i;
+
+ dev->mtu = 2000; /* MTU */
+ dev->tbusy = 0;
+ dev->hard_start_xmit = loopback_xmit;
+ dev->open = NULL;
+#if 1
+ dev->hard_header = eth_header;
+ dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->addr_len = ETH_ALEN; /* 6 */
+ dev->type = ARPHRD_ETHER; /* 0x0001 */
+ dev->type_trans = eth_type_trans;
+ dev->rebuild_header = eth_rebuild_header;
+#else
+ dev->hard_header_length = 0;
+ dev->addr_len = 0;
+ dev->type = 0; /* loopback_type (0) */
+ dev->hard_header = NULL;
+ dev->type_trans = NULL;
+ dev->rebuild_header = NULL;
+#endif
+
+ /* New-style flags. */
+ dev->flags = IFF_LOOPBACK;
+ dev->family = AF_INET;
+ dev->pa_addr = in_aton("127.0.0.1");
+ dev->pa_brdaddr = in_aton("127.255.255.255");
+ dev->pa_mask = in_aton("255.0.0.0");
+ dev->pa_alen = sizeof(unsigned long);
+ dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct enet_statistics));
+ dev->get_stats = get_stats;
+
+ /* Fill in the generic fields of the device structure. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ return(0);
+};
#include <asm/system.h>
#include <asm/io.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "8390.h"
#define NE_BASE (dev->base_addr)
#include <linux/malloc.h>
#include <linux/if_ether.h>
#include <linux/string.h>
-#include "dev.h"
-#include "eth.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
/* The network devices currently exist only in the socket namespace, so these
entries are unused. The only ones that make sense are
Given that almost all of these functions are handled in the current
socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+
+ [Removed all support for /dev network devices. When someone adds streams then
+ by magic we get them, but otherwise they are un-needed and a space waste]
*/
/* The next device number/name to assign: "eth0", "eth1", etc. */
static int next_ethdev_number = 0;
-#ifdef NET_MAJOR_NUM
-static struct file_operations netcard_fops = {
- NULL, /* lseek */
- NULL, /* read */
- NULL, /* write */
- NULL, /* readdir */
- NULL, /* select */
- NULL, /* ioctl */
- NULL, /* mmap */
- NULL, /* open */
- NULL, /* release */
- NULL /* fsync */
-};
-#endif
-
unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
/*
unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
{
-#ifdef NET_MAJOR_NUM
- if (register_chrdev(NET_MAJOR_NUM, "network",&netcard_fops))
- printk("WARNING: Unable to get major %d for the network devices.\n",
- NET_MAJOR_NUM);
-#endif
-
#if defined(CONFIG_LANCE) /* Note this is _not_ CONFIG_AT1500. */
mem_start = lance_init(mem_start, mem_end);
#endif
sprintf(dev->name, "eth%d", next_ethdev_number++);
for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
+ skb_queue_head_init(&dev->buffs[i]);
dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
return dev;
}
+void ether_setup(struct device *dev)
+{
+ int i;
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->broadcast[i]=0xff;
+ }
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+}
+
+
\f
/*
* Local variables:
-/* Plip.c: A parallel port "network" driver for linux. */
/*
- Written 1993 by Donald Becker and TANABE Hiroyasu.
- This code is distributed under the GPL.
-
- The current author is reached as hiro@sanpo.t.u-tokyo.ac.jp .
- For more information do 'whois -h whois.nic.ad.jp HT043JP'
-
- The original author may be reached as becker@super.org or
- C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
-
- This is parallel port packet pusher. It's actually more general
- than the "IP" in its name suggests -- but 'plip' is just such a
- great name!
-
- This driver was first developed by D. Becker, when he was inspired by
- Russ Nelson's parallel port packet driver. He also did the update
- to 0.99.10.
-
- It was further developed by Tommy Thorn (tthorn@daimi.aau.dk).
-
- Recent versions were debugged and maintained by TANABE Hiroyasu.
+ * Plip.c: A parallel port "network" driver for linux.
+ */
- Updated for 0.99pl12 by Donald Becker.
-
- Changes even more Alan Cox <iiitac@pyr.swan.ac.uk>
- Fixed: sets skb->arp=1, always claims success like ethernet, doesn't
- free skb and then claim fail. Incorrect brackets causing reset problem
- Attempting to make it work (works for me - email me if it does work)
-
- Bugs:
- Should be timer oriented state machine.
- Should never use jiffies for timeouts.
- Protocol is buggy when broadcasts occur (Must ask Russ Nelson)
- Can hang forever on collisions (tough - you fix it!).
- I get 15K/second NFS throughput (about 20-25K second IP).
- Change the protocol back.
-
-*/
+/*
+ * Developement History:
+ *
+ * Original version and the name 'PLIP' from Donald Becker <becker@super.org>
+ * inspired by Russ Nelson's parallel port packet driver.
+ * Further development by Tommy Thorn <thorn@daimi.aau.dk>
+ * Some changes by Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp>
+ * Upgraded for PL12 by Donald Becker
+ * Minor hacks by Alan Cox <gw4pts@gw4pts.ampr.org> to get it working
+ * more reliably (Ha!)
+ * Changes even more Peter Bauer (100136.3530@compuserve.com)
+ * Protocol changed back to original plip as in crynwr's packet-drivers.
+ * Tested this against ncsa-telnet 2.3 and pcip_pkt using plip.com (which
+ * contains "version equ 0" and ";History:562,1" in the firts 2
+ * source-lines 28-Mar-94
+ *
+ *
+ *
+ * This is parallel port packet pusher. It's actually more general
+ * than the "IP" in its name suggests -- but 'plip' is just such a
+ * great name!
+ *
+ *
+ * Bugs: Please read this: The PLIP driver is a nasty hack and like all nasty hacks
+ * has some 'features'.
+ *
+ * Can lock machines solid if one end goes down or crashes, or due to cable faults.
+ * Can lock both machines solid on a broadcast collision.
+ * Some laptops don't have all the wires we use.
+ * Doesn't match the original Russ Nelson protocol so won't talk to Amiga or PC drivers.
+ * Waits far too long with interrupts off [X is unbearable, forget action games, xntp is a joke]
+ * Doesn't work on some fast 486DX machines
+ *
+ * If it works be thankful, if not fix it!
+ *
+ * Info:
+ * I <Alan> got 15K/second NFS throughput (about 20-25K second IP). I also got some ethernet cards
+ * so don't ask me for help. This code needs a real major rewrite. Any volunteers ?
+ */
static char *version =
- "Net2Debugged PLIP 1.01 (from plip.c:v0.15 for 0.99pl12+, 8/11/93)\n";
+ "NET3 PLIP.010 (from plip.c:v0.15 for 0.99pl12+, 8/11/93)\n";
#include <linux/config.h>
#include <netinet/in.h>
#include <errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#ifdef PRINTK
#undef PRINTK
static int plip_close(struct device *dev);
static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
static int plip_header (unsigned char *buff, struct device *dev,
- unsigned short type, unsigned long h_dest,
- unsigned long h_source, unsigned len);
+ unsigned short type, void *dest,
+ void *source, unsigned len, struct sk_buff *skb);
/* variables used internally. */
#define INITIALTIMEOUTFACTOR 4
static void plip_receiver_error(struct device *dev);
static void plip_set_physicaladdr(struct device *dev, unsigned long ipaddr);
static int plip_addrcmp(struct ethhdr *eth);
-static int plip_send_enethdr(struct device *dev, struct ethhdr *eth);
-static int plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
- unsigned char h_dest, unsigned char h_source,
- unsigned short type);
static void cold_sleep(int tics);
static void plip_interrupt(int reg_ptr); /* Dispatch from interrupts. */
static int plip_receive_packet(struct device *dev);
memset(dev->priv, 0, sizeof(struct netstats));
for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
+ skb_queue_head_init(&dev->buffs[i]);
+
dev->hard_header = &plip_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
return 0;
}
- /* Pretend we are an ethernet and fill in the header. This could use
- a simplified routine someday. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
dev->trans_start = jiffies;
ret_val = plip_send_packet(dev, skb->data, skb->len);
if (skb->free)
static int
plip_header (unsigned char *buff, struct device *dev,
- unsigned short type, unsigned long h_dest,
- unsigned long h_source, unsigned len)
+ unsigned short type, void *daddr ,
+ void *saddr, unsigned len, struct sk_buff *skb)
{
if (dev->dev_addr[0] == 0) {
/* set physical address */
- plip_set_physicaladdr(dev, h_source);
+ plip_set_physicaladdr(dev, dev->pa_addr);
}
- return eth_header(buff, dev, type, h_dest, h_source, len);
+ return eth_header(buff, dev, type, daddr, saddr, len, skb);
}
\f
static void
} while ( (val & 0x80) );
val = inb(dev->base_addr + PAR_STATUS);
low_nibble = (val >> 3) & 0x0f;
- outb(0x11, dev->base_addr + PAR_DATA);
+ outb(0x10, dev->base_addr + PAR_DATA);
timeout = jiffies + timeoutfactor * 2;
do {
oldval = val;
val = inb(dev->base_addr + PAR_STATUS);
PRINTK2(("%02x %s ", low_nibble | ((val << 1) & 0xf0),
error ? "t":""));
- outb(0x01, dev->base_addr + PAR_DATA);
+ outb(0x00, dev->base_addr + PAR_DATA);
if (error) {
/* timeout error */
double_timeoutfactor();
static int
plip_receive_packet(struct device *dev)
{
- int plip_type;
unsigned length;
int checksum = 0;
struct sk_buff *skb;
{
/* get header octet and length of packet */
- plip_type = get_byte(dev);
- if (plip_type < 0) return 1; /* probably wrong interrupt */
- length = get_byte(dev) << 8;
- length |= get_byte(dev);
- switch ( plip_type ) {
- case PLIP_HEADER_TYPE1:
- {
- int i;
- unsigned char *eth_p = (unsigned char*)ð
- for ( i = 0; i < sizeof(eth); i++, eth_p++) {
- *eth_p = get_byte(dev);
- }
- }
- break;
- case PLIP_HEADER_TYPE2:
- {
- unsigned char h_dest, h_source;
- unsigned short type;
- h_dest = get_byte(dev);
- h_source = get_byte(dev);
- type = get_byte(dev) << 8;
- type |= get_byte(dev);
- plip_rebuild_enethdr(dev, ð, h_dest, h_source, type);
- }
- break;
- default:
- PRINTK(("%s: wrong header octet\n", dev->name));
- }
+
+ length = get_byte(dev);
+ length |= get_byte(dev) << 8;
+ {
+ int i;
+ unsigned char *eth_p = (unsigned char*)ð
+ for ( i = 0; i < sizeof(eth); i++, eth_p++) {
+ *eth_p = get_byte(dev);
+ }
+ }
PRINTK2(("length = %d\n", length));
if (length > dev->mtu || length < 8) {
PRINTK2(("%s: bogus packet size %d.\n", dev->name, length));
/* get skb area from kernel and
* set appropriate values to skb
*/
- int sksize;
- sksize = sizeof(struct sk_buff) + length;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(length, GFP_ATOMIC);
if (skb == NULL) {
PRINTK(("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, sksize));
+ dev->name,length));
return 1;
}
skb->lock = 0;
- skb->mem_len = sksize;
- skb->mem_addr = skb;
}
{
/* phase of receiving the data */
{
int timeout;
int error = 0;
- if (!(inb(dev->base_addr+PAR_STATUS) & 0x08)) {
- PRINTK(("remote end become unready while sending\n"));
- return -1;
- }
PRINTK2((" S%02x", val));
- outb(val, dev->base_addr); /* this makes data bits more stable */
- outb(0x10 | val, dev->base_addr);
+ outb((val & 0xf), dev->base_addr); /* this makes data bits more stable */
+ /* (especially the &0xf :-> PB ) */
+ outb(0x10 | (val & 0xf), dev->base_addr);
timeout = jiffies + timeoutfactor;
while( inb(dev->base_addr+PAR_STATUS) & 0x80 )
if ( timeout < jiffies ) {
plip_send_packet(struct device *dev, unsigned char *buf, int length)
{
int error = 0;
- int plip_type;
struct netstats *localstats;
PRINTK2(("%s: plip_send_packet(%d) %02x %02x %02x %02x %02x...",
if (plip_send_start(dev, (struct ethhdr *)buf) < 0)
return 1;
- /* select plip type */
{
- /* Use stripped ethernet header if each first 5 octet of eth
- * address is same.
+ /* send packet's length
+ the byte order has changed now and then. Today it's sent as in
+ the original crynwr-plip ...
+ Gruss PB
*/
- int i;
- struct ethhdr *eth = (struct ethhdr *)buf;
-
- plip_type = PLIP_HEADER_TYPE2;
- for ( i = 0; i < ETH_ALEN - 1; i++)
- if (eth->h_dest[i] != eth->h_source[i])
- plip_type = PLIP_HEADER_TYPE1;
- }
-
- send_byte(dev, plip_type); /* send header octet */
-
- {
- /* send packet's length */
- /*
- * in original plip (before v0.1), it was sent with little endian.
- * but in internet, network byteorder is big endian,
- * so changed to use big endian.
- * maybe using 'ntos()' is better.
- */
- send_byte(dev, length >> 8); send_byte(dev, length);
+ send_byte(dev, length);
+ send_byte(dev, length >> 8);
}
{
/* phase of sending data */
int i;
int checksum = 0;
- if (plip_type == PLIP_HEADER_TYPE2) {
- plip_send_enethdr(dev, (struct ethhdr*)buf);
- }
for ( i = 0; i < sizeof(struct ethhdr); i++ ) {
- if (plip_type == PLIP_HEADER_TYPE1) {
- send_byte(dev, *buf);
- }
+ send_byte(dev, *buf);
checksum += *buf++;
}
return 0;
}
-static int
-plip_send_enethdr(struct device *dev, struct ethhdr *eth)
-{
- send_byte(dev, eth->h_dest[ETH_ALEN-1]);
- send_byte(dev, eth->h_source[ETH_ALEN-1]);
- send_byte(dev, eth->h_proto >> 8);
- send_byte(dev, eth->h_proto);
- return 0;
-}
-
-static int
-plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
- unsigned char dest, unsigned char source,
- unsigned short type)
-{
- eth->h_proto = type;
- memcpy(eth->h_dest, dev->dev_addr, ETH_ALEN-1);
- eth->h_dest[ETH_ALEN-1] = dest;
- memcpy(eth->h_source, dev->dev_addr, ETH_ALEN-1);
- eth->h_source[ETH_ALEN-1] = source;
- return 0;
-}
-
/* This function is evil, evil, evil. This should be a
_kernel_, rescheduling sleep!. */
static void
#include <asm/dma.h>
#include <errno.h>
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c, in ioport.h for later versions. */
extern struct device *irq2dev_map[16];
#endif
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
#ifndef HAVE_PORTRESERVE
#define check_region(ioaddr, size) 0
#define snarf_region(ioaddr, size); do ; while (0)
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
/* Example routines you must write ;->. */
#define tx_done(dev) 1
dev->stop = net_close;
dev->hard_start_xmit = net_send_packet;
dev->get_stats = net_get_stats;
-#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_multicast_list;
-#endif
- /* Fill in the fields of the device structure with ethernet-generic values.
- This should be in a common file instead of per-driver. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
-
- dev->hard_header = eth_header;
- dev->add_arp = eth_add_arp;
- dev->queue_xmit = dev_queue_xmit;
- dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
-
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- for (i = 0; i < ETH_ALEN; i++) {
- dev->broadcast[i]=0xff;
- }
-
- /* New-style flags. */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+
+ ether_setup(dev);
return 0;
}
return 0;
}
- /* For ethernet, fill in the header. This should really be done by a
- higher level, rather than duplicated for each ethernet adaptor. */
- if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
- skb->dev = dev;
- arp_queue (skb);
- return 0;
- }
- skb->arp=1;
-
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
if (status & 0x04) lp->stats.rx_fifo_errors++;
} else {
/* Malloc up new buffer. */
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* or */
insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
-#ifdef HAVE_NETIF_RX
netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_s(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
lp->stats.rx_packets++;
}
} while (--boguscount);
return &lp->stats;
}
-#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
} else
outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
}
-#endif
\f
/*
* Local variables:
* allow zero or one slots
* separate routines
* status display
+ *
+ *
+ * This module is a difficult issue. Its clearly inet code but its also clearly
+ * driver code belonging close to PPP and SLIP
*/
+#include <linux/config.h>
+#ifdef CONFIG_INET
+/* Entire module is for IP only */
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/termios.h>
#include <linux/in.h>
#include <linux/fcntl.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "icmp.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
-#include "arp.h"
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
}
}
+#endif /* CONFIG_INET */
* Michael Riepe : Automatic CSLIP recognition added
* Charles Hedrick : CSLIP header length problem fix.
* Alan Cox : Corrected non-IP cases of the above.
+ *
+ *
+ * FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
+ * CONFIG_INET defined.
*/
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#ifdef CONFIG_AX25
#include "ax25.h"
#endif
-#include "eth.h"
+#include <linux/etherdevice.h>
+#ifdef CONFIG_INET
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
+#endif
+#include <linux/skbuff.h>
#include "sock.h"
-#include "arp.h"
#include "slip.h"
+#ifdef CONFIG_INET
#include "slhc.h"
+#endif
#define SLIP_VERSION "0.7.5"
int count;
count = sl->rcount;
+#ifdef CONFIG_INET
if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
#if 1
DPRINTF((DBG_SLIP, "<< \"%s\" recv:\r\n", sl->dev->name));
ip_dump(sl->rbuff, sl->rcount);
-
+#endif
/* Bump the datagram to the upper layers... */
do {
DPRINTF((DBG_SLIP, "SLIP: packet is %d at 0x%X\n",
}
p = icp;
+#ifdef CONFIG_INET
if(sl->mode & SL_MODE_CSLIP)
len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
-
-#ifdef OLD
- /*
- * Send an initial END character to flush out any
- * data that may have accumulated in the receiver
- * due to line noise.
- */
- bp = sl->xbuff;
- *bp++ = END;
- count = 1;
-
- /*
- * For each byte in the packet, send the appropriate
- * character sequence, according to the SLIP protocol.
- */
- while(len-- > 0) {
- c = *p++;
- switch(c) {
- case END:
- *bp++ = ESC;
- *bp++ = ESC_END;
- count += 2;
- break;
- case ESC:
- *bp++ = ESC;
- *bp++ = ESC_ESC;
- count += 2;
- break;
- default:
- *bp++ = c;
- count++;
- }
- }
- *bp++ = END;
- count++;
-#else
+#endif
if(sl->mode & SL_MODE_SLIP6)
count=slip_esc6(p, (unsigned char *)sl->xbuff,len);
else
count=slip_esc(p, (unsigned char *)sl->xbuff,len);
-#endif
sl->spacket++;
bp = sl->xbuff;
/* We were not, so we are now... :-) */
if (skb != NULL) {
-#ifdef CONFIG_AX25
- if(sl->mode & SL_MODE_AX25)
- {
- if(!skb->arp && dev->rebuild_header(skb->data,dev))
- {
- skb->dev=dev;
- arp_queue(skb);
- return 0;
- }
- skb->arp=1;
- }
+#if 0
+#ifdef CONFIG_AX25
+ if(sl->mode & SL_MODE_AX25)
+ {
+ if(!skb->arp && dev->rebuild_header(skb->data,dev))
+ {
+ skb->dev=dev;
+ arp_queue(skb);
+ return 0;
+ }
+ skb->arp=1;
+ }
#endif
+#endif
sl_lock(sl);
- size = skb->len;
- if (!(sl->mode & SL_MODE_AX25)) {
- if (size < sizeof(struct iphdr)) {
+
+ size=skb->len;
+
+ if(!(sl->mode&SL_MODE_AX25))
+ {
+ if(size<sizeof(struct iphdr))
+ {
printk("Runt IP frame fed to slip!\n");
- } else {
- size = ((struct iphdr *)(skb->data))->tot_len;
- size = ntohs(size);
+ }
+ else
+ {
+ size=((struct iphdr *)(skb->data))->tot_len;
+ size=ntohs(size);
+ /* sl_hex_dump(skb->data,skb->len);*/
}
}
- /* sl_hex_dump(skb->data,skb->len);*/
sl_encaps(sl, skb->data, size);
- if (skb->free)
+ if (skb->free)
kfree_skb(skb, FREE_WRITE);
}
return(0);
}
+
/* Return the frame type ID. This is normally IP but maybe be AX.25. */
static unsigned short
sl_type_trans (struct sk_buff *skb, struct device *dev)
#ifdef CONFIG_AX25
struct slip *sl=&sl_ctrl[dev->base_addr];
if(sl->mode&SL_MODE_AX25)
- return(NET16(ETH_P_AX25));
+ return htons(ETH_P_AX25);
#endif
- return(NET16(ETH_P_IP));
+ return htons(ETH_P_IP);
}
/* Fill in the MAC-level header. Not used by SLIP. */
static int
sl_header(unsigned char *buff, struct device *dev, unsigned short type,
- unsigned long daddr, unsigned long saddr, unsigned len)
+ void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
{
#ifdef CONFIG_AX25
struct slip *sl=&sl_ctrl[dev->base_addr];
- if((sl->mode&SL_MODE_AX25) && type!=NET16(ETH_P_AX25))
- return ax25_encapsulate_ip(buff,dev,type,daddr,saddr,len);
+ if((sl->mode&SL_MODE_AX25) && type!=htons(ETH_P_AX25))
+ return ax25_encapsulate(buff,dev,type,daddr,saddr,len,skb);
#endif
return(0);
}
-/* Add an ARP-entry for this device's broadcast address. Not used. */
-static void
-sl_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
-{
-#ifdef CONFIG_AX25
- struct slip *sl=&sl_ctrl[dev->base_addr];
-
- if(sl->mode&SL_MODE_AX25)
- arp_add(addr,((char *) skb->data)+8,dev);
-#endif
-}
-
-
/* Rebuild the MAC-level header. Not used by SLIP. */
static int
-sl_rebuild_header(void *buff, struct device *dev)
+sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+ struct sk_buff *skb)
{
#ifdef CONFIG_AX25
struct slip *sl=&sl_ctrl[dev->base_addr];
if(sl->mode&SL_MODE_AX25)
- return ax25_rebuild_header(buff,dev);
+ return ax25_rebuild_header(buff,dev,raddr, skb);
#endif
return(0);
}
break;
}
p = buff;
-#ifdef OLD
- while (count--) {
- c = *p++;
- if (sl->escape) {
- if (c == ESC_ESC)
- sl_enqueue(sl, ESC);
- else if (c == ESC_END)
- sl_enqueue(sl, END);
- else
- printk ("SLIP: received wrong character\n");
- sl->escape = 0;
- } else {
- if (c == ESC)
- sl->escape = 1;
- else if (c == END) {
- if (sl->rcount > 2) sl_bump(sl);
- sl_dequeue(sl, sl->rcount);
- sl->rcount = 0;
- } else sl_enqueue(sl, c);
- }
- }
-#else
if(sl->mode & SL_MODE_SLIP6)
slip_unesc6(sl,buff,count,error);
else
slip_unesc(sl,buff,count,error);
-#endif
} while(1);
}
DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
switch(cmd) {
case SIOCGIFNAME:
- err=verify_area(VERIFY_WRITE, arg, strlen(sl->dev->name) + 1);
+ err=verify_area(VERIFY_WRITE, arg, 16);
if(err)
- return err;
+ return -err;
memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
return(0);
case SIOCGIFENCAP:
if (already++ == 0) {
printk("SLIP: version %s (%d channels)\n",
SLIP_VERSION, SL_NRUNIT);
+#ifdef CONFIG_INET
printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+#endif
#ifdef CONFIG_AX25
printk("AX25: KISS encapsulation enabled\n");
#endif
dev->open = sl_open;
dev->stop = sl_close;
dev->hard_header = sl_header;
- dev->add_arp = sl_add_arp;
dev->type_trans = sl_type_trans;
dev->get_stats = sl_get_stats;
#ifdef HAVE_SET_MAC_ADDR
memcpy(dev->broadcast,ax25_bcast,7); /* Only activated in AX.25 mode */
memcpy(dev->dev_addr,ax25_test,7); /* "" "" "" "" */
#endif
- dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = sl_rebuild_header;
for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
+ skb_queue_head_init(&dev->buffs[i]);
/* New-style flags. */
dev->flags = 0;
#include <asm/io.h>
#include <asm/system.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "8390.h"
/* Compatibility definitions for earlier kernel versions. */
#include <asm/io.h>
#include <asm/system.h>
-#include "dev.h"
+#include <linux/netdevice.h>
#include "8390.h"
/* Compatibility definitions for earlier kernel versions. */
printk("Use sg, count %d %x %d\n", SCpnt->use_sg, count, dma_free_sectors);
printk("maxsg = %x, counted = %d this_count = %d\n", max_sg, counted, this_count);
while(bh){
- printk("[%8.8x %x] ", bh->b_data, bh->b_size);
+ printk("[%p %lx] ", bh->b_data, bh->b_size);
bh = bh->b_reqnext;
};
if(SCpnt->use_sg < 16)
for(count=0; count<SCpnt->use_sg; count++)
- printk("{%d:%8.8x %8.8x %d} ", count,
+ printk("{%d:%p %p %d} ", count,
sgpnt[count].address,
sgpnt[count].alt_address,
sgpnt[count].length);
/* If an unlocked buffer is not uptodate, there has
been an IO error. Skip it. */
if (wait && bh->b_req && !bh->b_lock &&
- !bh->b_dirt && !bh->b_uptodate)
- {
+ !bh->b_dirt && !bh->b_uptodate) {
err = 1;
- printk("Weird - unlocked, clean and not uptodate buffer on %d list %d\n", nlist);
+ printk("Weird - unlocked, clean and not uptodate buffer on list %d\n", nlist);
continue;
}
/* Don't write clean buffers. Don't write ANY buffers
ll_rw_block(WRITE, 1, &bh);
if(nlist != BUF_DIRTY) {
- printk("[%d %x %d] ", nlist, bh->b_dev, bh->b_blocknr);
+ printk("[%d %x %ld] ", nlist, bh->b_dev, bh->b_blocknr);
ncount++;
};
bh->b_count--;
to request some blocks in a filesystem that we know that we will
be needing ahead of time. */
- if(nr_free[isize] > 100) return 0;
+ if (nr_free[isize] > 100)
+ return;
/* If there are too many dirty buffers, we wake up the update process
now so as to ensure that there are still clean buffers available
* quite a bit, modularized the code.
* fvk 4/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
* Renamed "route_get_info()" to "rt_get_info()" for consistency.
+ * Alan Cox (gw4pts@gw4pts.ampr.org) 4/94
+ * Dusted off the code and added IPX. Fixed the 4K limit.
*
* proc net directory handling functions
*/
/* the get_*_info() functions are in the net code, and are configured
in via the standard mechanism... */
-extern int unix_get_info(char *);
+extern int unix_get_info(char *, char **, off_t, int);
#ifdef CONFIG_INET
-extern int tcp_get_info(char *);
-extern int udp_get_info(char *);
-extern int raw_get_info(char *);
-extern int arp_get_info(char *);
-extern int dev_get_info(char *);
-extern int rt_get_info(char *);
+extern int tcp_get_info(char *, char **, off_t, int);
+extern int udp_get_info(char *, char **, off_t, int);
+extern int raw_get_info(char *, char **, off_t, int);
+extern int arp_get_info(char *, char **, off_t, int);
+extern int dev_get_info(char *, char **, off_t, int);
+extern int rt_get_info(char *, char **, off_t, int);
#endif /* CONFIG_INET */
-
+#ifdef CONFIG_IPX
+extern int ipx_get_info(char *, char **, off_t, int);
+extern int ipx_rt_get_info(char *, char **, off_t, int);
+#endif /* CONFIG_IPX */
static struct file_operations proc_net_operations = {
NULL, /* lseek - default */
{ 133,3,"tcp" },
{ 134,3,"udp" }
#endif /* CONFIG_INET */
+#ifdef CONFIG_IPX
+ ,{ 135,9,"ipx_route" },
+ { 136,3,"ipx" }
+#endif /* CONFIG_IPX */
};
#define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
}
+#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */
+
static int proc_readnet(struct inode * inode, struct file * file,
char * buf, int count)
{
char * page;
int length;
- int end;
unsigned int ino;
+ int bytes=count;
+ int thistime;
+ int copied=0;
+ char *start;
if (count < 0)
return -EINVAL;
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
ino = inode->i_ino;
- switch (ino) {
- case 128:
- length = unix_get_info(page);
- break;
+
+ while(bytes>0)
+ {
+ thistime=bytes;
+ if(bytes>PROC_BLOCK_SIZE)
+ thistime=PROC_BLOCK_SIZE;
+
+ switch (ino)
+ {
+ case 128:
+ length = unix_get_info(page,&start,file->f_pos,thistime);
+ break;
#ifdef CONFIG_INET
- case 129:
- length = arp_get_info(page);
- break;
- case 130:
- length = rt_get_info(page);
- break;
- case 131:
- length = dev_get_info(page);
- break;
- case 132:
- length = raw_get_info(page);
- break;
- case 133:
- length = tcp_get_info(page);
- break;
- case 134:
- length = udp_get_info(page);
- break;
+ case 129:
+ length = arp_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 130:
+ length = rt_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 131:
+ length = dev_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 132:
+ length = raw_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 133:
+ length = tcp_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 134:
+ length = udp_get_info(page,&start,file->f_pos,thistime);
+ break;
#endif /* CONFIG_INET */
- default:
- free_page((unsigned long) page);
- return -EBADF;
- }
- if (file->f_pos >= length) {
- free_page((unsigned long) page);
- return 0;
+#ifdef CONFIG_IPX
+ case 135:
+ length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
+ break;
+ case 136:
+ length = ipx_get_info(page,&start,file->f_pos,thistime);
+ break;
+#endif /* CONFIG_IPX */
+ default:
+ free_page((unsigned long) page);
+ return -EBADF;
+ }
+
+ /*
+ * We have been given a non page aligned block of
+ * the data we asked for + a bit. We have been given
+ * the start pointer and we know the length..
+ */
+
+ /*
+ * Copy the bytes
+ */
+ memcpy_tofs(buf+copied, start, length);
+ file->f_pos+=length; /* Move down the file */
+ bytes-=length;
+ copied+=length;
+ if(length<thistime)
+ break; /* End of file */
}
- if (count + file->f_pos > length)
- count = length - file->f_pos;
- end = count + file->f_pos;
- memcpy_tofs(buf, page + file->f_pos, count);
free_page((unsigned long) page);
- file->f_pos = end;
- return count;
+ return copied;
}
--- /dev/null
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_ETHERDEVICE_H
+#define _LINUX_ETHERDEVICE_H
+
+
+#include <linux/if_ether.h>
+
+#ifdef __KERNEL__
+extern int eth_header(unsigned char *buff, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len,
+ struct sk_buff *skb);
+extern int eth_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev);
+
+#endif
+
+#endif /* _LINUX_ETHERDEVICE_H */
#include <linux/types.h> /* for "caddr_t" et al */
#include <linux/socket.h> /* for "struct sockaddr" et al */
-
-/* Structure defining a queue for a network interface. */
-#ifdef not_yet_in_linux
-struct ifnet {
- char *if_name; /* name, e.g. ``en'' or ``lo'' */
- short if_unit; /* sub-unit for device driver */
- short if_mtu; /* maximum transmission unit */
- short if_flags; /* up/down, broadcast, etc. */
- short if_timer; /* time 'til if_watchdog called */
- int if_metric; /* routing metric (not used) */
- struct ifaddr *if_addrlist; /* linked list of addrs per if */
- struct ifqueue {
- struct mbuf *ifq_head;
- struct mbuf *ifq_tail;
- int ifq_len;
- int ifq_maxlen;
- int ifq_drops;
- } if_snd; /* output queue */
-
- /* Procedure handles. */
- int (*if_init)(); /* init routine */
- int (*if_output)(); /* output routine */
- int (*if_ioctl)(); /* ioctl routine */
- int (*if_reset)(); /* bus reset routine */
- int (*if_watchdog)(); /* timer routine */
-
- /* Generic interface statistics. */
- int if_ipackets; /* packets recv'd on interface */
- int if_ierrors; /* input errors on interface */
- int if_opackets; /* packets sent on interface */
- int if_oerrors; /* output errors on interface */
- int if_collisions; /* collisions on CSMA i'faces */
-
- /* Linked list: pointer to next interface. */
- struct ifnet *if_next;
-};
-#endif
-
/* Standard interface flags. */
#define IFF_UP 0x1 /* interface is up */
#define IFF_BROADCAST 0x2 /* broadcast address valid */
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
- char ifrn_hwaddr[IFHWADDRLEN];
+ char ifrn_hwaddr[IFHWADDRLEN]; /* Obsolete */
} ifr_ifrn;
union {
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_metric;
int ifru_mtu;
};
#define ifr_name ifr_ifrn.ifrn_name /* interface name */
-#define ifr_hwaddr ifr_ifrn.ifrn_hwaddr /* interface hardware */
+#define old_ifr_hwaddr ifr_ifrn.ifrn_hwaddr /* interface hardware */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ARPOP_RREPLY 4 /* RARP reply */
-/*
- * Address Resolution Protocol.
- *
- * See RFC 826 for protocol description. ARP packets are variable
- * in size; the arphdr structure defines the fixed-length portion.
- * Protocol type values are the same as those for 10 Mb/s Ethernet.
- * It is followed by the variable-sized fields ar_sha, arp_spa,
- * arp_tha and arp_tpa in that order, according to the lengths
- * specified. Field names used correspond to RFC 826.
- */
-struct arphdr {
- unsigned short ar_hrd; /* format of hardware address */
- unsigned short ar_pro; /* format of protocol address */
- unsigned char ar_hln; /* length of hardware address */
- unsigned char ar_pln; /* length of protocol address */
- unsigned short ar_op; /* ARP opcode (command) */
-
- /* The rest is variable in size, according to the sizes above. */
-#if 0
- unsigned char ar_sha[]; /* sender hardware address */
- unsigned char ar_spa[]; /* sender protocol address */
- unsigned char ar_tha[]; /* target hardware address */
- unsigned char ar_tpa[]; /* target protocol address */
-#endif /* not actually included! */
-};
-
-
/* ARP ioctl request. */
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
};
/* ARP Flag values. */
-#define ATF_INUSE 0x01 /* entry in use */
#define ATF_COM 0x02 /* completed entry (ha valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* publish entry */
--- /dev/null
+/*
+ * Swansea University Computer Society NET3
+ *
+ * This work is derived from NET2Debugged, which is in turn derived
+ * from NET2D which was written by:
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This work was derived friom Ross Biro's inspirational work
+ * for the LINUX operating system. His version numbers were:
+ *
+ * $Id: Space.c,v 0.8.4.5 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: arp.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: arp.h,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: dev.c,v 0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: dev.h,v 0.8.4.7 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: eth.c,v 0.8.4.4 1993/01/22 23:21:38 bir7 Exp $
+ * $Id: eth.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: icmp.c,v 0.8.4.9 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: icmp.h,v 0.8.4.2 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: ip.c,v 0.8.4.8 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: ip.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: loopback.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: packet.c,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: protocols.c,v 0.8.4.3 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: raw.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: sock.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: sock.h,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.c,v 0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.h,v 0.8.4.7 1993/01/22 22:58:08 bir7 Exp $
+ * $Id: timer.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: timer.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: udp.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: udp.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: we.c,v 0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: wereg.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_INET_H
+#define _LINUX_INET_H
+
+
+#include <linux/ddi.h>
+
+
+#undef INET_DEBUG
+#ifdef INET_DEBUG
+# define DPRINTF(x) dprintf x
+#else
+# define DPRINTF(x) do ; while (0)
+#endif
+
+/* Debug levels. One per module. */
+#define DBG_OFF 0 /* no debugging */
+#define DBG_INET 1 /* sock.c */
+#define DBG_RT 2 /* route.c */
+#define DBG_DEV 3 /* dev.c */
+#define DBG_ETH 4 /* eth.c */
+#define DBG_PROTO 5 /* protocol.c */
+#define DBG_TMR 6 /* timer.c */
+#define DBG_PKT 7 /* packet.c */
+#define DBG_RAW 8 /* raw.c */
+
+#define DBG_LOOPB 10 /* loopback.c */
+#define DBG_SLIP 11 /* slip.c */
+
+#define DBG_ARP 20 /* arp.c */
+#define DBG_IP 21 /* ip.c */
+#define DBG_ICMP 22 /* icmp.c */
+#define DBG_TCP 23 /* tcp.c */
+#define DBG_UDP 24 /* udp.c */
+
+#ifdef __KERNEL__
+
+extern int inet_debug;
+
+
+extern void inet_proto_init(struct ddi_proto *pro);
+extern char *in_ntoa(unsigned long in);
+extern unsigned long in_aton(char *str);
+
+extern void dprintf(int level, char *fmt, ...);
+
+extern int dbg_ioctl(void *arg, int level);
+
+#endif
+#endif /* _LINUX_INET_H */
--- /dev/null
+struct sockaddr_ipx
+{
+ short sipx_family;
+ unsigned long sipx_network;
+ unsigned char sipx_node[6];
+ short sipx_port;
+};
+
+struct ipx_route_def
+{
+ unsigned long ipx_network;
+ unsigned long ipx_router_network;
+#define IPX_ROUTE_NO_ROUTER 0
+ unsigned char ipx_router_node[6];
+ unsigned char ipx_device[16];
+ unsigned short ipx_flags;
+#define IPX_RT_BLUEBOOK 2
+#define IPX_RT_ROUTED 1
+};
+
+#define IPX_MTU 576
+
+
--- /dev/null
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Interfaces handler.
+ *
+ * Version: @(#)dev.h 1.0.10 08/12/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Donald J. Becker, <becker@super.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Moved to /usr/include/linux for NET3
+ */
+#ifndef _LINUX_NETDEVICE_H
+#define _LINUX_NETDEVICE_H
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+/* for future expansion when we will have different priorities. */
+#define DEV_NUMBUFFS 3
+#define MAX_ADDR_LEN 7
+#define MAX_HEADER 18
+
+#define IS_MYADDR 1 /* address is (one of) our own */
+#define IS_LOOPBACK 2 /* address is for LOOPBACK */
+#define IS_BROADCAST 3 /* address is a valid broadcast */
+#define IS_INVBCAST 4 /* Wrong netmask bcast not for us */
+
+/*
+ * The DEVICE structure.
+ * Actually, this whole structure is a big mistake. It mixes I/O
+ * data with strictly "high-level" data, and it has to know about
+ * almost every data structure used in the INET module.
+ */
+struct device
+{
+
+ /*
+ * This is the first field of the "visible" part of this structure
+ * (i.e. as seen by users in the "Space.c" file). It is the name
+ * the interface.
+ */
+ char *name;
+
+ /* I/O specific fields. These will be moved to DDI soon. */
+ unsigned long rmem_end; /* shmem "recv" end */
+ unsigned long rmem_start; /* shmem "recv" start */
+ unsigned long mem_end; /* sahared mem end */
+ unsigned long mem_start; /* shared mem start */
+ unsigned short base_addr; /* device I/O address */
+ unsigned char irq; /* device IRQ number */
+
+ /* Low-level status flags. */
+ volatile unsigned char start, /* start an operation */
+ tbusy, /* transmitter busy */
+ interrupt; /* interrupt arrived */
+
+ /*
+ * Another mistake.
+ * This points to the next device in the "dev" chain. It will
+ * be moved to the "invisible" part of the structure as soon as
+ * it has been cleaned up. -FvK
+ */
+ struct device *next;
+
+ /* The device initialization function. Called only once. */
+ int (*init)(struct device *dev);
+
+ /* Some hardware also needs these fields, but they are not part of the
+ usual set specified in Space.c. */
+ unsigned char if_port; /* Selectable AUI, TP,..*/
+ unsigned char dma; /* DMA channel */
+
+ struct enet_statistics* (*get_stats)(struct device *dev);
+
+ /*
+ * This marks the end of the "visible" part of the structure. All
+ * fields hereafter are internal to the system, and may change at
+ * will (read: may be cleaned up at will).
+ */
+
+ /* These may be needed for future network-power-down code. */
+ unsigned long trans_start; /* Time (in jiffies) of last Tx */
+ unsigned long last_rx; /* Time of last Rx */
+
+ unsigned short flags; /* interface flags (a la BSD) */
+ unsigned short family; /* address family ID (AF_INET) */
+ unsigned short metric; /* routing metric (not used) */
+ unsigned short mtu; /* interface MTU value */
+ unsigned short type; /* interface hardware type */
+ unsigned short hard_header_len; /* hardware hdr length */
+ void *priv; /* pointer to private data */
+
+ /* Interface address info. */
+ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
+ unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */
+ unsigned char addr_len; /* harfware address length */
+ unsigned long pa_addr; /* protocol address */
+ unsigned long pa_brdaddr; /* protocol broadcast addr */
+ unsigned long pa_dstaddr; /* protocol P-P other side addr */
+ unsigned long pa_mask; /* protocol netmask */
+ unsigned short pa_alen; /* protocol address length */
+
+ /* Pointer to the interface buffers. */
+ struct sk_buff_head buffs[DEV_NUMBUFFS];
+
+ /* Pointers to interface service routines. */
+ int (*open)(struct device *dev);
+ int (*stop)(struct device *dev);
+ int (*hard_start_xmit) (struct sk_buff *skb,
+ struct device *dev);
+ int (*hard_header) (unsigned char *buff,
+ struct device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len,
+ struct sk_buff *skb);
+ int (*rebuild_header)(void *eth, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+ unsigned short (*type_trans) (struct sk_buff *skb,
+ struct device *dev);
+#define HAVE_MULTICAST
+ void (*set_multicast_list)(struct device *dev,
+ int num_addrs, void *addrs);
+#define HAVE_SET_MAC_ADDR
+ int (*set_mac_address)(struct device *dev, void *addr);
+};
+
+
+struct packet_type {
+ unsigned short type; /* This is really htons(ether_type). */
+ unsigned short copy:1;
+ int (*func) (struct sk_buff *, struct device *,
+ struct packet_type *);
+ void *data;
+ struct packet_type *next;
+};
+
+
+#ifdef __KERNEL__
+
+/* Used by dev_rint */
+#define IN_SKBUFF 1
+
+extern volatile char in_bh;
+
+extern struct device *dev_base;
+extern struct packet_type *ptype_base;
+
+
+extern int ip_addr_match(unsigned long addr1, unsigned long addr2);
+extern int ip_chk_addr(unsigned long addr);
+extern struct device *ip_dev_check(unsigned long daddr);
+extern unsigned long ip_my_addr(void);
+extern unsigned long ip_get_mask(unsigned long addr);
+
+extern void dev_add_pack(struct packet_type *pt);
+extern void dev_remove_pack(struct packet_type *pt);
+extern struct device *dev_get(char *name);
+extern int dev_open(struct device *dev);
+extern int dev_close(struct device *dev);
+extern void dev_queue_xmit(struct sk_buff *skb, struct device *dev,
+ int pri);
+#define HAVE_NETIF_RX 1
+extern void netif_rx(struct sk_buff *skb);
+/* The old interface to netif_rx(). */
+extern int dev_rint(unsigned char *buff, long len, int flags,
+ struct device * dev);
+extern void dev_transmit(void);
+extern int in_inet_bh(void);
+extern void inet_bh(void *tmp);
+extern void dev_tint(struct device *dev);
+extern int dev_get_info(char *buffer, char **start, off_t offset, int length);
+extern int dev_ioctl(unsigned int cmd, void *);
+
+extern void dev_init(void);
+
+/* This function lives elsewhere (drivers/net/net_init.c but is related) */
+
+extern void ether_setup(struct device *dev);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_DEV_H */
extern unsigned long intr_count;
+#define start_bh_atomic() \
+__asm__ __volatile__("incl _intr_count")
+
+#define end_bh_atomic() \
+__asm__ __volatile__("decl _intr_count")
+
/*
* Bus types (default is ISA, but people can check others with these..)
* MCA_bus hardcoded to 0 for now.
--- /dev/null
+/*
+ * Definitions for the 'struct sk_buff' memory handlers.
+ *
+ * Authors:
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+#include <linux/malloc.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#define CONFIG_SKB_CHECK 1
+
+#define HAVE_ALLOC_SKB /* For the drivers to know */
+
+
+#define FREE_READ 1
+#define FREE_WRITE 0
+
+
+struct sk_buff_head {
+ struct sk_buff * volatile next;
+ struct sk_buff * volatile prev;
+#if CONFIG_SKB_CHECK
+ int magic_debug_cookie;
+#endif
+};
+
+
+struct sk_buff {
+ struct sk_buff * volatile next;
+ struct sk_buff * volatile prev;
+#if CONFIG_SKB_CHECK
+ int magic_debug_cookie;
+#endif
+ struct sk_buff * volatile link3;
+ struct sock *sk;
+ volatile unsigned long when; /* used to compute rtt's */
+ struct timeval stamp;
+ struct device *dev;
+ void *mem_addr;
+ union {
+ struct tcphdr *th;
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ unsigned char *raw;
+ unsigned long seq;
+ } h;
+ struct iphdr *ip_hdr; /* For IPPROTO_RAW */
+ unsigned long mem_len;
+ unsigned long len;
+ unsigned long fraglen;
+ struct sk_buff *fraglist; /* Fragment list */
+ unsigned long truesize;
+ unsigned long saddr;
+ unsigned long daddr;
+ unsigned long raddr; /* next hop addr */
+ volatile char acked,
+ used,
+ free,
+ arp;
+ unsigned char tries,lock;
+ unsigned short users; /* User count - see datagram.c (and soon seqpacket.c/stream.c) */
+ unsigned long padding[0];
+ unsigned char data[0];
+};
+
+#define SK_WMEM_MAX 32767
+#define SK_RMEM_MAX 32767
+
+#ifdef CONFIG_SKB_CHECK
+#define SK_FREED_SKB 0x0DE2C0DE
+#define SK_GOOD_SKB 0xDEC0DED1
+#define SK_HEAD_SKB 0x12231298
+#endif
+
+#ifdef __KERNEL__
+/*
+ * Handling routines are only of interest to the kernel
+ */
+
+#if 0
+extern void print_skb(struct sk_buff *);
+#endif
+extern void kfree_skb(struct sk_buff *skb, int rw);
+extern void skb_queue_head_init(struct sk_buff_head *list);
+extern void skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
+extern void skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
+extern struct sk_buff * skb_dequeue(struct sk_buff_head *list);
+extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_append(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_unlink(struct sk_buff *buf);
+extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list);
+extern struct sk_buff * alloc_skb(unsigned int size, int priority);
+extern void kfree_skbmem(void *mem, unsigned size);
+extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority);
+extern void skb_kept_by_device(struct sk_buff *skb);
+extern void skb_device_release(struct sk_buff *skb,
+ int mode);
+extern int skb_device_locked(struct sk_buff *skb);
+/*
+ * Peek an sk_buff. Unlike most other operations you _MUST_
+ * be careful with this one. A peek leaves the buffer on the
+ * list and someone else may run off with it. For an interrupt
+ * type system cli() peek the buffer copy the data and sti();
+ */
+static __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+ struct sk_buff *list = (struct sk_buff *)list_;
+ return (list->next != list)? list->next : NULL;
+}
+
+#if CONFIG_SKB_CHECK
+extern int skb_check(struct sk_buff *skb,int,int, char *);
+#define IS_SKB(skb) skb_check((skb), 0, __LINE__,__FILE__)
+#define IS_SKB_HEAD(skb) skb_check((skb), 1, __LINE__,__FILE__)
+#else
+#define IS_SKB(skb) 0
+#define IS_SKB_HEAD(skb) 0
+#endif
+
+extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
+extern int datagram_select(struct sock *sk, int sel_type, select_table *wait);
+extern void skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
+extern void skb_free_datagram(struct sk_buff *skb);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SKBUFF_H */
#define _LINUX_SOCKIOS_H
/* This section will go away soon! */
-#if 1 /* FIXME: */
-#define MAX_IP_NAME 20
-#define IP_SET_DEV 0x2401
-
-struct ip_config {
- char name[MAX_IP_NAME];
- unsigned long paddr;
- unsigned long router;
- unsigned long net;
- unsigned up:1,destroy:1;
-};
-#endif /* FIXME: */
/* Socket-level I/O control calls. */
#define FIOSETOWN 0x8901
#define FIOGETOWN 0x8903
#define SIOCGPGRP 0x8904
#define SIOCATMARK 0x8905
+#define SIOCGSTAMP 0x8096 /* Get stamp */
/* Routing table calls. */
#define SIOCADDRT 0x890B /* add routing table entry */
#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
#define SIOCGIFMTU 0x8921 /* get MTU size */
#define SIOCSIFMTU 0x8922 /* set MTU size */
-#define SIOCGIFHWADDR 0x8923 /* get hardware address */
+#define OLD_SIOCGIFHWADDR 0x8923 /* get hardware address */
#define SIOCSIFHWADDR 0x8924 /* set hardware address (NI) */
#define SIOCGIFENCAP 0x8925 /* get/set slip encapsulation */
#define SIOCSIFENCAP 0x8926
+#define SIOCGIFHWADDR 0x8927 /* Get hardware address */
/* Routing table calls (oldrtent - don't use) */
#define SIOCADDRTOLD 0x8940 /* add routing table entry */
* Version: @(#)Space.c 1.0.2 04/22/93
*
* Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * Please see the comments in ddi.c - Alan
+ *
*/
+
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
# include "unix/unix.h"
#endif
#ifdef CONFIG_INET
-# include "inet/inet.h"
+# include <linux/inet.h>
#endif
#ifdef CONFIG_IPX
#include "inet/ipxcall.h"
};
-/*
- * Section B: Device Driver Modules.
- * This section defines which network device drivers
- * get linked into the Linux kernel. It is currently
- * only used by the INET protocol. Any takers for the
- * other protocols like XNS or Novell?
- *
- * WARNING: THIS SECTION IS NOT YET USED BY THE DRIVERS !!!!!
- */
-/*#include "drv/we8003/we8003.h" Western Digital WD-80[01]3 */
-/*#include "drv/dp8390/dp8390.h" Donald Becker's DP8390 kit */
-/*#inclde "drv/slip/slip.h" Laurence Culhane's SLIP kit */
-
-
-struct ddi_device devices[] = {
-#if CONF_WE8003
- { "WD80x3[EBT]",
- "", 0, 1, we8003_init, NULL,
- 19, 0, DDI_FCHRDEV,
- { 0x280, 0, 15, 0, 32768, 0xD0000 } },
-#endif
-#if CONF_DP8390
- { "DP8390/WD80x3",
- "", 0, 1, dpwd8003_init, NULL,
- 20, 0, DDI_FCHRDEV,
- { 0, 0, 0, 0, 0, 0, } },
- { "DP8390/NE-x000",
- "", 0, 1, dpne2000_init, NULL,
- 20, 8, DDI_FCHRDEV,
- { 0, 0, 0, 0, 0, 0, } },
- { "DP8390/3C50x",
- "", 0, 1, dpec503_init, NULL,
- 20, 16, DDI_FCHRDEV,
- { 0, 0, 0, 0, 0, 0, } },
-#endif
- { NULL,
- "", 0, 0, NULL, NULL,
- 0, 0, 0,
- { 0, 0, 0, 0, 0, 0 } }
-};
* Version: @(#)ddi.c 1.0.5 04/22/93
*
* Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *
+ * For the moment I'm classifying DDI as 'dead'. However if/when Fred
+ * produces a masterpiece of design DDI may get resurrected. With the
+ * current kernel as modules work by Peter MacDonald they might be
+ * redundant anyway. Thus I've removed all but the protocol initialise.
+ *
+ * We will end up with protocol initialisers and socket family initialisers.
*/
#include <asm/segment.h>
#include <asm/system.h>
#endif
-extern struct ddi_device devices[]; /* device driver map */
extern struct ddi_proto protocols[]; /* network protocols */
-/*
- * This function gets called with an ASCII string representing the
- * ID of some DDI driver. We loop through the DDI Devices table
- * and return the address of the control block that has a matching
- * "name" field. It is used by upper-level layers that want to
- * dynamically bind some UNIX-domain "/dev/XXXX" file name to a
- * DDI device driver. The "iflink(8)" program is an example of
- * this behaviour.
- */
-struct ddi_device *
-ddi_map(const char *id)
-{
- register struct ddi_device *dev;
-
- PRINTK (("DDI: MAP: looking for \"%s\": ", id));
- dev = devices;
- while (dev->title != NULL) {
- if (strncmp(dev->name, id, DDI_MAXNAME) == 0) {
- PRINTK (("OK at 0x%X\n", dev));
- return(dev);
- }
- dev++;
- }
- PRINTK (("NOT FOUND\n"));
- return(NULL);
-}
-
-
/*
* This is the function that is called by a kernel routine during
* system startup. Its purpose is to walk trough the "devices"
void
ddi_init(void)
{
- struct ddi_proto *pro;
- struct ddi_device *dev;
-
- PRINTK (("DDI: Starting up!\n"));
+ struct ddi_proto *pro;
- /* First off, kick all configured protocols. */
- pro = protocols;
- while (pro->name != NULL) {
- (*pro->init)(pro);
- pro++;
- }
-
- /* Done. Now kick all configured device drivers. */
- dev = devices;
- while (dev->title != NULL) {
- (*dev->init)(dev);
- dev++;
- }
+ PRINTK (("DDI: Starting up!\n"));
- /* We're all done... */
+ /* Kick all configured protocols. */
+ pro = protocols;
+ while (pro->name != NULL)
+ {
+ (*pro->init)(pro);
+ pro++;
+ }
+ /* We're all done... */
}
$(CC) $(CFLAGS) -S -o $*.s $<
-OBJS = sock.o utils.o route.o proc.o timer.o protocol.o loopback.o \
+OBJS = sock.o utils.o route.o proc.o timer.o protocol.o \
eth.o packet.o arp.o dev.o ip.o raw.o icmp.o tcp.o udp.o \
- datagram.o skbuff.o
-# ipx.o ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o
+ datagram.o skbuff.o devinet.o
+
+ifdef CONFIG_AX25
+
+OBJS := $(OBJS) ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o
+
+endif
+
+ifdef CONFIG_IPX
+
+OBJS := $(OBJS) ipx.o
+
+endif
ifdef CONFIG_INET
-NET2Debugged 1.24 README
-------------------------
-
-Major Changes
-
-o PLIP driver sort of works
-o UDP and RAW have been partially rewritten for speed
-o Internals heavily cleaned up, and memory monitoring of network
- memory is now done. (On shift-scroll-lock)
-o ARP should now not generate garbage
-o Using MSG_PEEK can't cause race conditions and crashes
-o Support for bootp clients.
-o Supports RFC931 TAP authd
-o NFS problems with certain types of network configuration are
- fixed.
-o Doesn't forward packets for other subnet (can cause packet storms)
-o TCP won't ack rst frames causing packet storms (especially with
- Lan workplace for DOS).
-o Numerous fixes for solidity
-o Verify_area used properly.
-o MSG_PEEK is faster again
-o Minor TCP fixes. Hopefully no more TCP lockups (ha!)
-o Donald's promiscuous mode. Go forth and write protocol analysers...
--------------------------------------------------------------------------
-NOTE:
- Drivers for this stack set must be using alloc_skb() not just
-kmalloc. If you get millions of 'non sk_buff...' errors please check the
-driver you are using. All Donald's drivers know about this. If you have
-a problem driver replace all cases of
-
- .. =(struct sk_buff *)kmalloc(sizeof(struct sk_buff)+...
-
-With
- ..=alloc_skb(sizeof(struct sk_buff)+...
-
-And if it uses kfree_s on the packet change that to use kfree_skbmem().
-
--------------------------------------------------------------------------
-Bug fixes and improvements for this section of the code should be mailed to
-iiitac@pyr.swan.ac.uk.
-
-
-Alan
+This is snapshot 010
+
+Notes:
+ARP
+ As of snapshot 006, ARP should compile and work correctly
+ for any protocol that has the right build_header support.
+
+AX25
+ This is an ALPHA release. It will not be a standard part
+ of the real release module. Please read the copyrights on
+ the AX.25 code carefully. When AX.25 is finished it will
+ be part of a seperatly available amateur radio add on. Also
+ please rememeber this is ALPHA code. It works well for a lot
+ of people but I know for a fact it is currently buggy.
+
+IPX
+ The IPX module in here is fairly complete, and certainly
+ usable for things. The IPX user code isn't yet very useful
+ (nobody has written a RIP/SAP daemon!).
+
+NetROM
+ I'm slowly doing bits of this code, but its not even fit
+ to include here.
+
+
+Status:
+
+Done:
+ Replaced ARP with Florian la Roche's ARP.
+ Replaced/improved sk_buff handlers (again from Florian)
+ Removed surplus DDI code.
+ Reformatted most modules.
+ Fixed ICMP handling bugs (ICMP error to ICMP error).
+ Fixed fragmentation bugs (both memory and mtu).
+ Moved some includes.
+ Drivers now build correctly with no IP layer.
+ Merged Linus 1.0.1 diffs and my patches 1-3.
+ Further fixups on clean driver build.
+ Loopback driver now lives where it belongs.
+ UDP verified against specification (passes).
+ IP verified against specification (two errors: Incorrect forwarding and
+ no mandatory option handling).
+ ARP verified against specification (passes: recommendation that ARP
+ rejects MAC broadcast/multicast addresses - this needs
+ driver changes doing).
+ All surplus skb->sk assignment and skb->mem_len skb->mem_addr removed.
+ eth.h became linux/etherdevice.h.
+ alloc_skb nows adds the sizeof(struct sk_buff) itself.
+ Now relative to Linux 1.0.4.
+ All IP wakeups are now callbacks.
+ IPX and AX.25 callbacks now use wake_up_interruptible correctly.
+ ICMP,IP and UDP collect snmp statistics.
+ Removed the 4K limit from the /proc/net/* files.
+ Routing bugs.
+ Cleaned up skb duplication.
+ IPX /proc from Mark Evans.
+ Driver packet ordering now enforced.
+ AX.25 unused SSID bits now set.
+
+In Progress:
+ Module by module validation against specifications.
+ TCP delayed ACK [RFC1122 requires this].
+ Byte-order fixes.
+ Core code restructure to enable a working non IP build
+ Trying to fix /proc to do >4K correctly, as well as dynamic addition
+ of /proc/ and /proc/net/ objects (for module protocol layers).
+ Adding the extra NET2E driver ioctl() support.
+ SNMP MIB statistic capture - finish TCP and add device layer when
+ Donald is ready.
+ Donald Beckers latest driver mods.
+ Adding the ICMP_TIMESTAMP support patch.
+ Routing bugs.
+ Packet level time stamping.
+ Merging in support for the I^2IT 'TICK' time synchronisation chip.
+ Crynwyr compliant PLIP driver.
+
+To Do:
+ Merge in sk_buff data handling module.
+ Socket family/protocol seperation.
+ Additional BSD options (SO_LOWAT etc).
+ IP option handling, especially on ip forwards.
+ AX.25 /proc support.
+ SNMP /proc support.
+ TCP MSS/Window and route metrics in the routing table.
+ TCP mtu discovery support.
+ NetROM.
+ TCP closing side state machine bug fixes.
+ NetBEUI (Lan Manager) [ie IEE802.3/IEE802.2/NetBIOS].
+ Make drivers record type and addressing category(Multicast/Broadcast..)
+ Speed it up.
+ Unix domain cleanup/rewrite.
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * This file implements the Address Resolution Protocol (ARP),
- * which is used by TCP/IP to map the IP addresses from a host
- * to a low-level hardware address (like an Ethernet address)
- * which it can use to talk to that host.
+/* linux/net/inet/arp.c
*
- * NOTE: This module will be rewritten completely in the near future,
- * because I want it to become a multi-address-family address
- * resolver, like it should be. It will be put in a separate
- * directory under 'net', being a protocol of its own. -FvK
+ * Copyright (C) 1994 by Florian La Roche
*
- * Version: @(#)arp.c 1.0.15 05/25/93
+ * This module implements the Address Resolution Protocol ARP (RFC 826),
+ * which is used to convert IP addresses (or in the future maybe other
+ * high-level addresses into a low-level hardware address (like an Ethernet
+ * address).
*
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- * Arnt Gulbrandsen, <agulbra@pvv.unit.no>
+ * FIXME:
+ * Experiment with better retransmit timers
+ * Clean up the timer deletions
+ * If you create a proxy entry set your interface address to the address
+ * and then delete it, proxies may get out of sync with reality - check this
*
- * Fixes:
- * Stephen A. Wood : arp problems
- * 'Mr Linux' : arp problems.
- * Alan Cox : arp_ioctl now checks memory areas with verify_area.
- * Alan Cox : Non IP arp message now only appears with debugging on.
- * Alan Cox : arp queue is volatile (may be altered by arp messages while doing sends)
- * Generic queue code is urgently needed!
- * Alan Cox : Deleting your own ip addr now gives EINVAL not a printk message.
- * Alan Cox : Fix to arp linked list error
- * Alan Cox : Ignore broadcast arp (Linus' idea 8-))
- * Alan Cox : arp_send memory leak removed
- * Alan Cox : generic skbuff code fixes.
- * Alan Cox : 'Bad Packet' only reported on debugging
- * Alan Cox : Proxy arp.
- * Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet.
- * Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet
- * one.
- * Dominik Kubla : Better checking
- * Tegge : Assorted corrections on cross port stuff
- * Alan Cox : ATF_PERM was backwards! - might be useful now (sigh)
- * Alan Cox : Arp timer added.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
*
- * To Fix:
- * : arp response allocates an skbuff to send. However there is a perfectly
- * good spare skbuff the right size about to be freed (the query). Use the
- * query for the reply. This avoids an out of memory case _and_ speeds arp
- * up.
- * : FREE_READ v FREE_WRITE errors. Not critical as loopback arps don't occur
*
+ * Fixes:
+ * Alan Cox : Removed the ethernet assumptions in Florians code
+ * Alan Cox : Fixed some small errors in the ARP logic
+ * Alan Cox : Allow >4K in /proc
+ * Alan Cox : Make ARP add its own protocol entry
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
+
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/socket.h>
#include <linux/sockios.h>
-#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <stdarg.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "arp.h"
+#ifdef CONFIG_AX25
+#include "ax25.h"
+#endif
+/*
+ * This structure defines the ARP mapping cache. As long as we make changes
+ * in this structure, we keep interrupts of. But normally we can copy the
+ * hardware address and the device pointer in a local variable and then make
+ * any "long calls" to send a packet out.
+ */
+
+struct arp_table
+{
+ struct arp_table *next; /* Linked entry list */
+ unsigned long last_used; /* For expiry */
+ unsigned int flags; /* Control status */
+ unsigned long ip; /* ip address of entry */
+ unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */
+ unsigned char hlen; /* Length of hardware address */
+ unsigned char htype; /* Type of hardware in use */
+ struct device *dev; /* Device the entry is tied to */
-#define ARP_MAX_TRIES 3
-
-
-static char *unk_print(unsigned char *, int);
-static char *eth_aprint(unsigned char *, int);
+ /*
+ * The following entries are only used for unresolved hw addresses.
+ */
+
+ struct timer_list timer; /* expire timer */
+ int retries; /* remaining retries */
+ struct sk_buff_head skb; /* list of queued packets */
+};
+/*
+ * This structure defines an ethernet arp header.
+ */
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+#if 0
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+#endif
-static char *arp_cmds[] = {
- "0x%04X",
- "REQUEST",
- "REPLY",
- "REVERSE REQUEST",
- "REVERSE REPLY",
- NULL
-};
-#define ARP_MAX_CMDS (sizeof(arp_cmds) / sizeof(arp_cmds[0]))
-
-static struct {
- char *name;
- char *(*print)(unsigned char *ptr, int len);
-} arp_types[] = {
- { "0x%04X", unk_print },
- { "10 Mbps Ethernet", eth_aprint },
- { "3 Mbps Ethernet", eth_aprint },
- { "AX.25", unk_print },
- { "Pronet", unk_print },
- { "Chaos", unk_print },
- { "IEEE 802.2 Ethernet (?)", eth_aprint },
- { "Arcnet", unk_print },
- { "AppleTalk", unk_print },
- { NULL, NULL }
};
-#define ARP_MAX_TYPE (sizeof(arp_types) / sizeof(arp_types[0]))
+/*
+ * Configurable Parameters (don't touch unless you know what you are doing
+ */
+
+/*
+ * If an arp request is send, ARP_RES_TIME is the timeout value until the
+ * next request is send.
+ */
+
+#define ARP_RES_TIME (250*(HZ/10))
+
+/*
+ * The number of times an arp request is send, until the host is
+ * considered unreachable.
+ */
+
+#define ARP_MAX_TRIES 3
-struct arp_table *arp_tables[ARP_TABLE_SIZE] = {
- NULL,
-};
+/*
+ * After that time, an unused entry is deleted from the arp table.
+ */
+
+#define ARP_TIMEOUT (600*HZ)
-static int arp_proxies=0; /* So we can avoid the proxy arp
- overhead with the usual case of
- no proxy arps */
+/*
+ * How often is the function 'arp_check_retries' called.
+ * An entry is invalidated in the time between ARP_TIMEOUT and
+ * (ARP_TIMEOUT+ARP_CHECK_INTERVAL).
+ */
-struct sk_buff * volatile arp_q = NULL;
+#define ARP_CHECK_INTERVAL (60 * HZ)
-static struct arp_table *arp_lookup(unsigned long addr);
-static struct arp_table *arp_lookup_proxy(unsigned long addr);
-/* Dump the ADDRESS bytes of an unknown hardware type. */
-static char *
-unk_print(unsigned char *ptr, int len)
-{
- static char buff[32];
- char *bufp = buff;
- int i;
+static void arp_check_expire (unsigned long); /* Forward declaration. */
- for (i = 0; i < len; i++)
- bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377));
- return(buff);
-}
+static struct timer_list arp_timer =
+ { NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
-/* Dump the ADDRESS bytes of an Ethernet hardware type. */
-static char *
-eth_aprint(unsigned char *ptr, int len)
-{
- if (len != ETH_ALEN) return("");
- return(eth_print(ptr));
-}
+/*
+ * The size of the hash table. Must be a power of two.
+ * Maybe we should remove hashing in the future for arp and concentrate
+ * on Patrick Schaaf's Host-Cache-Lookup...
+ */
+#define ARP_TABLE_SIZE 16
-/* Dump an ARP packet. Not complete yet for non-Ethernet packets. */
-static void
-arp_print(struct arphdr *arp)
+struct arp_table *arp_tables[ARP_TABLE_SIZE] =
{
- int len, idx;
- unsigned char *ptr;
+ NULL,
+};
- if (inet_debug != DBG_ARP) return;
+/*
+ * The last bits in the IP address are used for the cache lookup.
+ */
+
+#define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1))
- printk("ARP: ");
- if (arp == NULL) {
- printk("(null)\n");
- return;
- }
-
- /* Print the opcode name. */
- len = htons(arp->ar_op);
- if (len < ARP_MAX_CMDS) idx = len;
- else idx = 0;
- printk("op ");
- printk(arp_cmds[idx], len);
-
- /* Print the ARP header. */
- len = htons(arp->ar_hrd);
- if (len < ARP_MAX_TYPE) idx = len;
- else idx = 0;
- printk(" hrd = "); printk(arp_types[idx].name, len);
- printk(" pro = 0x%04X\n", htons(arp->ar_pro));
- printk(" hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln);
-
- /*
- * Print the variable data.
- * When ARP gets redone (after the formal introduction of NET-2),
- * this part will be redone. ARP will then be a multi-family address
- * resolver, and the code below will be made more general. -FvK
- */
- ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
- printk(" sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
- ptr += arp->ar_hln;
- printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
- ptr += arp->ar_pln;
- printk(" target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
- ptr += arp->ar_hln;
- printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
-}
+/*
+ * Number of proxy arp entries. This is normally zero and we use it to do
+ * some optimizing for normal uses
+ */
+
+static int proxies = 0;
-/* This will try to retransmit everything on the queue. */
-static void
-arp_send_q(void)
+/*
+ * Check if there are too old entries and remove them. If the ATF_PERM
+ * flag is set, they are always left in the arp cache (permanent entry).
+ * Note: Only fully resolved entries, which don't have any packets in
+ * the queue, can be deleted, since ARP_TIMEOUT is much greater than
+ * ARP_MAX_TRIES*ARP_RES_TIME.
+ */
+
+static void arp_check_expire(unsigned long dummy)
{
- struct sk_buff *skb;
- struct sk_buff *volatile work_q;
- cli();
- work_q = arp_q;
- skb_new_list_head(&work_q);
- arp_q = NULL;
- sti();
- while((skb=skb_dequeue(&work_q))!=NULL)
- {
- IS_SKB(skb);
- skb->magic = 0;
- skb->next = NULL;
- skb->prev = NULL;
-
- /* Decrement the 'tries' counter. */
+ int i;
+ unsigned long now = jiffies;
+ unsigned long flags;
+ save_flags(flags);
cli();
- skb->tries--;
- if (skb->tries == 0) {
- /*
- * Grmpf.
- * We have tried ARP_MAX_TRIES to resolve the IP address
- * from this datagram. This means that the machine does
- * not listen to our ARP requests. Perhaps someone tur-
- * ned off the thing?
- * In any case, trying further is useless. So, we kill
- * this packet from the queue. (grinnik) -FvK
- */
- skb->sk = NULL;
- if(skb->free)
- kfree_skb(skb, FREE_WRITE);
- /* If free was 0, magic is now 0, next is 0 and
- the write queue will notice and kill */
- sti();
- continue;
- }
-
- /* Can we now complete this packet? */
- sti();
- if (skb->arp || !skb->dev->rebuild_header(skb->data, skb->dev)) {
- skb->arp = 1;
- skb->dev->queue_xmit(skb, skb->dev, 0);
- } else {
- /* Alas. Re-queue it... */
- skb->magic = ARP_QUEUE_MAGIC;
- skb_queue_head(&arp_q,skb);
+
+ for (i = 0; i < ARP_TABLE_SIZE; i++)
+ {
+ struct arp_table *entry;
+ struct arp_table **pentry = &arp_tables[i];
+
+ while ((entry = *pentry) != NULL)
+ {
+ if ((now - entry->last_used) > ARP_TIMEOUT
+ && !(entry->flags & ATF_PERM))
+ {
+ *pentry = entry->next; /* remove from list */
+ if (entry->flags & ATF_PUBL)
+ proxies--;
+ del_timer(&entry->timer); /* Paranoia */
+ kfree_s(entry, sizeof(struct arp_table));
+ }
+ else
+ pentry = &entry->next; /* go to next entry */
+ }
}
- }
-}
+ restore_flags(flags);
+ /*
+ * Set the timer again.
+ */
-static struct timer_list arp_timer;
-
-static void arp_queue_ticker(unsigned long data);
-
-static void arp_queue_kick(void)
-{
- arp_timer.expires = 500; /* 5 seconds */
- arp_timer.data = 0;
- arp_timer.function = arp_queue_ticker;
del_timer(&arp_timer);
+ arp_timer.expires = ARP_CHECK_INTERVAL;
add_timer(&arp_timer);
}
-static void arp_queue_ticker(unsigned long data/*UNUSED*/)
-{
- arp_send_q();
- if (skb_peek(&arp_q))
- arp_queue_kick();
-}
-
-
-/* Create and send our response to an ARP request. */
-static int
-arp_response(struct arphdr *arp1, struct device *dev, int addrtype)
-{
- struct arphdr *arp2;
- struct sk_buff *skb;
- unsigned long src, dst;
- unsigned char *ptr1, *ptr2;
- int hlen;
- struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */
-
- /* Decode the source (REQUEST) message. */
- ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
- src = *((unsigned long *) (ptr1 + arp1->ar_hln));
- dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
-
- if(addrtype!=IS_MYADDR)
- {
- apt=arp_lookup_proxy(dst);
- if(apt==NULL)
- return(1);
- }
-
- /* Get some mem and initialize it for the return trip. */
- skb = alloc_skb(sizeof(struct sk_buff) +
- sizeof(struct arphdr) +
- (2 * arp1->ar_hln) + (2 * arp1->ar_pln) +
- dev->hard_header_len, GFP_ATOMIC);
- if (skb == NULL) {
- printk("ARP: no memory available for ARP REPLY!\n");
- return(1);
- }
-
- skb->mem_addr = skb;
- skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) +
- (2 * arp1->ar_pln) + dev->hard_header_len;
- skb->mem_len = sizeof(struct sk_buff) + skb->len;
- hlen = dev->hard_header(skb->data, dev, ETH_P_ARP, src, dst, skb->len);
- if (hlen < 0) {
- printk("ARP: cannot create HW frame header for REPLY !\n");
- kfree_skb(skb, FREE_WRITE);
- return(1);
- }
-
- /*
- * Fill in the ARP REPLY packet.
- * This looks ugly, but we have to deal with the variable-length
- * ARP packets and such. It is not as bad as it looks- FvK
- */
- arp2 = (struct arphdr *) (skb->data + hlen);
- ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short);
- arp2->ar_hrd = arp1->ar_hrd;
- arp2->ar_pro = arp1->ar_pro;
- arp2->ar_hln = arp1->ar_hln;
- arp2->ar_pln = arp1->ar_pln;
- arp2->ar_op = htons(ARPOP_REPLY);
- if(addrtype==IS_MYADDR)
- memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
- else /* Proxy arp, so pull from the table */
- memcpy(ptr2, apt->ha, arp2->ar_hln);
- ptr2 += arp2->ar_hln;
- memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln);
- ptr2 += arp2->ar_pln;
- memcpy(ptr2, ptr1, arp2->ar_hln);
- ptr2 += arp2->ar_hln;
- memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln);
-
- skb->free = 1;
- skb->arp = 1;
- skb->sk = NULL;
- skb->next = NULL;
-
- DPRINTF((DBG_ARP, ">>"));
- arp_print(arp2);
-
- /* Queue the packet for transmission. */
- dev->queue_xmit(skb, dev, 0);
- return(0);
-}
-
-
-/* This will find an entry in the ARP table by looking at the IP address. */
-static struct arp_table *
-arp_lookup(unsigned long paddr)
+/*
+ * Release all linked skb's and the memory for this entry.
+ */
+
+static void arp_release_entry(struct arp_table *entry)
{
- struct arp_table *apt;
- unsigned long hash;
-
- DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr)));
-
- /* We don't want to ARP ourselves. */
- if (chk_addr(paddr) == IS_MYADDR) {
- printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr));
- return(NULL);
- }
-
- /* Loop through the table for the desired address. */
- hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
- cli();
- apt = arp_tables[hash];
- while(apt != NULL) {
- if (apt->ip == paddr) {
- sti();
- return(apt);
+ struct sk_buff *skb;
+
+ if (entry->flags & ATF_PUBL)
+ proxies--;
+ /* Release the list of `skb' pointers. */
+ while ((skb = skb_dequeue(&entry->skb)) != NULL)
+ {
+ if (skb->free)
+ kfree_skb(skb, FREE_WRITE);
}
- apt = apt->next;
- }
- sti();
- return(NULL);
+ del_timer(&entry->timer);
+ kfree_s(entry, sizeof(struct arp_table));
+ return;
}
-/* This will find a proxy in the ARP table by looking at the IP address. */
-static struct arp_table *arp_lookup_proxy(unsigned long paddr)
+/*
+ * Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
+ * message.
+ */
+
+static void arp_send(int type, unsigned long dest_ip, struct device *dev,
+ unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw)
{
- struct arp_table *apt;
- unsigned long hash;
+ struct sk_buff *skb;
+ struct arphdr *arp;
+ unsigned char *arp_ptr;
- DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr)));
-
- /* Loop through the table for the desired address. */
- hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
- cli();
- apt = arp_tables[hash];
- while(apt != NULL) {
- if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) {
- sti();
- return(apt);
+ /*
+ * No arp on this interface.
+ */
+
+ if(dev->flags&IFF_NOARP)
+ return;
+
+ /*
+ * Allocate a buffer
+ */
+
+ skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ + dev->hard_header_len, GFP_ATOMIC);
+ if (skb == NULL)
+ {
+ printk("ARP: no memory to send an arp packet\n");
+ return;
}
- apt = apt->next;
- }
- sti();
- return(NULL);
-}
+ skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4);
+ skb->arp = 1;
+ skb->dev = dev;
+ skb->free = 1;
+ /*
+ * Fill the device header for the ARP frame
+ */
-/* Delete an ARP mapping entry in the cache. */
-void
-arp_destructor(unsigned long paddr, int force)
-{
- struct arp_table *apt;
- struct arp_table **lapt;
- unsigned long hash;
+ dev->hard_header(skb->data,dev,ETH_P_ARP,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);
+
+ /* Fill out the arp protocol part. */
+ arp = (struct arphdr *) (skb->data + dev->hard_header_len);
+ arp->ar_hrd = htons(dev->type);
+#ifdef CONFIG_AX25
+ arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP);
+#else
+ arp->ar_pro = htons(ETH_P_IP);
+#endif
+ arp->ar_hln = dev->addr_len;
+ arp->ar_pln = 4;
+ arp->ar_op = htons(type);
+
+ arp_ptr=(unsigned char *)(arp+1);
+
+ memcpy(arp_ptr, src_hw, dev->addr_len);
+ arp_ptr+=dev->addr_len;
+ memcpy(arp_ptr, &src_ip,4);
+ arp_ptr+=4;
+ if (dest_hw != NULL)
+ memcpy(arp_ptr, dest_hw, dev->addr_len);
+ else
+ memset(arp_ptr, 0, dev->addr_len);
+ arp_ptr+=dev->addr_len;
+ memcpy(arp_ptr, &dest_ip, 4);
+
+ dev_queue_xmit(skb, dev, 0);
+}
- DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr)));
- /* We cannot destroy our own ARP entry. */
- if (chk_addr(paddr) == IS_MYADDR) {
- DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n",
- in_ntoa(paddr)));
- return;
- }
- hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
-
- cli();
- lapt = &arp_tables[hash];
- while ((apt = *lapt) != NULL) {
- if (apt->ip == paddr) {
- if((apt->flags&ATF_PERM) && !force)
- return;
- *lapt = apt->next;
- if(apt->flags&ATF_PUBL)
- arp_proxies--;
- kfree_s(apt, sizeof(struct arp_table));
- sti();
+/*
+ * This function is called, if an entry is not resolved in ARP_RES_TIME.
+ * Either resend a request, or give it up and free the entry.
+ */
+
+static void arp_expire_request (unsigned long arg)
+{
+ struct arp_table *entry = (struct arp_table *) arg;
+ struct arp_table **pentry;
+ unsigned long hash;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Since all timeouts are handled with interrupts enabled, there is a
+ * small chance, that this entry has just been resolved by an incoming
+ * packet. This is the only race condition, but it is handled...
+ */
+
+ if (entry->flags & ATF_COM)
+ {
+ restore_flags(flags);
return;
}
- lapt = &apt->next;
- }
- sti();
+
+ if (--entry->retries > 0)
+ {
+ unsigned long ip = entry->ip;
+ struct device *dev = entry->dev;
+
+ /* Set new timer. */
+ del_timer(&entry->timer);
+ entry->timer.expires = ARP_RES_TIME;
+ add_timer(&entry->timer);
+ restore_flags(flags);
+ arp_send(ARPOP_REQUEST, ip, dev, dev->pa_addr, NULL,
+ dev->dev_addr);
+ return;
+ }
+
+ /*
+ * Arp request timed out. Delete entry and all waiting packets.
+ * If we give each entry a pointer to itself, we don't have to
+ * loop through everything again. Maybe hash is good enough, but
+ * I will look at it later.
+ */
+
+ hash = HASH(entry->ip);
+ pentry = &arp_tables[hash];
+ while (*pentry != NULL)
+ {
+ if (*pentry == entry)
+ {
+ *pentry = entry->next; /* delete from linked list */
+ del_timer(&entry->timer);
+ restore_flags(flags);
+ arp_release_entry(entry);
+ return;
+ }
+ pentry = &(*pentry)->next;
+ }
+ restore_flags(flags);
+ printk("Possible ARP queue corruption.\n");
+ /*
+ * We should never arrive here.
+ */
}
+
/*
- * Kill an entry - eg for ioctl()
+ * This will try to retransmit everything on the queue.
*/
-
-void arp_destroy(unsigned long paddr)
-{
- arp_destructor(paddr,1);
+
+static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
+{
+ struct sk_buff *skb;
+
+
+ /*
+ * Empty the entire queue, building its data up ready to send
+ */
+
+ if(!(entry->flags&ATF_COM))
+ {
+ printk("arp_send_q: incomplete entry for %s\n",
+ in_ntoa(entry->ip));
+ return;
+ }
+
+ while((skb = skb_dequeue(&entry->skb)) != NULL)
+ {
+ IS_SKB(skb);
+ if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb))
+ {
+ skb->arp = 1;
+ if(skb->sk==NULL)
+ dev_queue_xmit(skb, skb->dev, 0);
+ else
+ dev_queue_xmit(skb,skb->dev,skb->sk->priority);
+ }
+ else
+ {
+ /* This routine is only ever called when 'entry' is
+ complete. Thus this can't fail (but does) */
+ printk("arp_send_q: The impossible occurred. Please notify Alan.\n");
+ printk("arp_send_q: active entity %s\n",in_ntoa(entry->ip));
+ printk("arp_send_q: failed to find %s\n",in_ntoa(skb->raddr));
+ }
+ }
}
+
/*
- * Delete a possibly invalid entry (see timer.c)
+ * Delete an ARP mapping entry in the cache.
*/
-
-void arp_destroy_maybe(unsigned long paddr)
+
+void arp_destroy(unsigned long ip_addr, int force)
{
- arp_destructor(paddr,0);
-}
+ struct arp_table *entry;
+ struct arp_table **pentry;
+ unsigned long hash = HASH(ip_addr);
-/* Create an ARP entry. The caller should check for duplicates! */
-static struct arp_table *
-arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype)
-{
- struct arp_table *apt;
- unsigned long hash;
-
- DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr)));
- DPRINTF((DBG_ARP, "%s, ", eth_print(addr)));
- DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype));
-
- apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC);
- if (apt == NULL) {
- printk("ARP: no memory available for new ARP entry!\n");
- return(NULL);
- }
-
- /* Fill in the allocated ARP cache entry. */
- hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
- apt->ip = paddr;
- apt->hlen = hlen;
- apt->htype = htype;
- apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */
- memcpy(apt->ha, addr, hlen);
- apt->last_used = jiffies;
- cli();
- apt->next = arp_tables[hash];
- arp_tables[hash] = apt;
- sti();
- return(apt);
+ cli();
+ pentry = &arp_tables[hash];
+ while ((entry = *pentry) != NULL)
+ {
+ if (entry->ip == ip_addr)
+ {
+ if ((entry->flags & ATF_PERM) && !force)
+ return;
+ *pentry = entry->next;
+ sti();
+ del_timer(&entry->timer);
+ arp_release_entry(entry);
+ return;
+ }
+ pentry = &entry->next;
+ }
+ sti();
}
-/*
- * An ARP REQUEST packet has arrived.
- * We try to be smart here, and fetch the data of the sender of the
- * packet- we might need it later, so fetching it now can save us a
- * broadcast later.
- * Then, if the packet was meant for us (i.e. the TARGET address was
- * one of our own IP addresses), we set up and send out an ARP REPLY
- * packet to the sender.
+/*
+ * Receive an arp request by the device layer. Maybe I rewrite it, to
+ * use the incoming packet for the reply. The time for the current
+ * "overhead" isn't that high...
*/
-int
-arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+
+int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
- struct arphdr *arp;
- struct arp_table *tbl;
- unsigned long src, dst;
- unsigned char *ptr;
- int ret;
- int addr_hint;
-
- DPRINTF((DBG_ARP, "<<\n"));
- arp = skb->h.arp;
- arp_print(arp);
-
- /* If this test doesn't pass, its not IP. Might be DECNET or friends */
- if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd))
- {
- DPRINTF((DBG_ARP,"ARP: Bad packet received on device \"%s\" !\n", dev->name));
- kfree_skb(skb, FREE_READ);
- return(0);
- }
-
- /* For now we will only deal with IP addresses. */
- if (((arp->ar_pro != NET16(0x00CC) && dev->type==3) || (arp->ar_pro != NET16(ETH_P_IP) && dev->type!=3) ) || arp->ar_pln != 4)
- {
- if (arp->ar_op != NET16(ARPOP_REQUEST))
- DPRINTF((DBG_ARP,"ARP: Non-IP request on device \"%s\" !\n", dev->name));
- kfree_skb(skb, FREE_READ);
- return(0);
- }
-
- /*
- * As said before, we try to be smart by using the
- * info already present in the packet: the sender's
- * IP and hardware address.
- */
- ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
- memcpy(&src, ptr + arp->ar_hln, arp->ar_pln);
- tbl = arp_lookup(src);
- if (tbl != NULL) {
- DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src)));
- memcpy(tbl->ha, ptr, arp->ar_hln);
- tbl->hlen = arp->ar_hln;
- tbl->flags |= ATF_COM;
- tbl->last_used = jiffies;
- } else {
- memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
- if (chk_addr(dst) != IS_MYADDR && arp_proxies == 0) {
+ /*
+ * We shouldn't use this type conversion. Check later.
+ */
+
+ struct arphdr *arp = (struct arphdr *)skb->h.raw;
+ unsigned char *arp_ptr= (unsigned char *)(arp+1);
+ struct arp_table *entry;
+ struct arp_table *proxy_entry;
+ int addr_hint;
+ unsigned long hash;
+ unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */
+ long sip,tip;
+ unsigned char *sha,*tha;
+
+ /*
+ * If this test doesn't pass, its not IP, or we should ignore it anyway
+ */
+
+ if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /*
+ * For now we will only deal with IP addresses.
+ */
+ if (
+#ifdef CONFIG_AX25
+ (arp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+ (arp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
+ || arp->ar_pln != 4)
+ {
+ /* This packet is not for us. Remove it. */
kfree_skb(skb, FREE_READ);
- return(0);
- } else {
- tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd);
- if (tbl == NULL) {
+ return 0;
+ }
+
+ /*
+ * Extract variable width fields
+ */
+
+ sha=arp_ptr;
+ arp_ptr+=dev->addr_len;
+ memcpy(&sip,arp_ptr,4);
+ arp_ptr+=4;
+ tha=arp_ptr;
+ arp_ptr+=dev->addr_len;
+ memcpy(&tip,arp_ptr,4);
+
+ /*
+ * Process entry
+ */
+
+ addr_hint = ip_chk_addr(tip);
+
+ hash = HASH(sip);
+ proxy_entry = NULL;
+ if (proxies != 0 && addr_hint != IS_MYADDR)
+ {
+ unsigned long dest_hash = HASH(tip);
+ cli();
+ proxy_entry = arp_tables[dest_hash];
+ while (proxy_entry != NULL)
+ {
+ if (proxy_entry->ip == tip && proxy_entry->htype==arp->ar_hrd)
+ break;
+ proxy_entry = proxy_entry->next;
+ }
+ if (proxy_entry && (proxy_entry->flags & ATF_PUBL))
+ memcpy(ha, proxy_entry->ha, dev->addr_len);
+ else
+ proxy_entry = NULL;
+ }
+ else
+ cli();
+
+ for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+ if (entry->ip == sip)
+ break;
+
+ if (entry != NULL)
+ {
+ int old_flags = entry->flags;
+ memcpy(entry->ha, sha, arp->ar_hln);
+ entry->hlen = arp->ar_hln;
+ /* This seems sensible but not everyone gets it right ! */
+ entry->htype = ntohs(arp->ar_hrd);
+ if(entry->htype==0)
+ entry->htype = dev->type; /* Not good but we have no choice */
+ entry->last_used = jiffies;
+ if (!(entry->flags & ATF_COM))
+ {
+ del_timer(&entry->timer);
+ entry->flags |= ATF_COM;
+ }
+ sti();
+ if (!(old_flags & ATF_COM))
+ {
+ /* Send out waiting packets. We might have problems,
+ if someone is manually removing entries right now.
+ I will fix this one. */
+ arp_send_q(entry, sha);
+ }
+ if (addr_hint != IS_MYADDR && proxy_entry == NULL)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+ else
+ {
+ if (addr_hint != IS_MYADDR && proxy_entry == NULL)
+ {
+ /* We don't do "smart arp" and cache all possible
+ entries. That just makes us more work. */
+ sti();
kfree_skb(skb, FREE_READ);
- return(0);
+ return 0;
}
+ entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ {
+ sti();
+ kfree_skb(skb, FREE_READ);
+ printk("ARP: no memory for new arp entry\n");
+ return 0;
+ }
+ entry->ip = sip;
+ entry->hlen = arp->ar_hln;
+ entry->htype = arp->ar_hrd;
+ entry->flags = ATF_COM;
+ memcpy(entry->ha, sha, arp->ar_hln);
+ entry->last_used = jiffies;
+ entry->next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ entry->dev = skb->dev;
+ skb_queue_head_init(&entry->skb);
+ sti();
}
- }
-
- /*
- * Since we updated the ARP cache, we might have enough
- * information to send out some previously queued IP
- * datagrams....
- */
- arp_send_q();
-
- /*
- * OK, we used that part of the info. Now check if the
- * request was an ARP REQUEST for one of our own addresses..
- */
- if (arp->ar_op != NET16(ARPOP_REQUEST)) {
- kfree_skb(skb, FREE_READ);
- return(0);
- }
-/*
- * A broadcast arp, ignore it
- */
+ /* From here on, interrupts are enabled. Never touch entry->..
+ any more. */
+
+ if (arp->ar_op != htons(ARPOP_REQUEST)
+ || tip == INADDR_LOOPBACK)
+ {
+ /* This wasn't a request, or some bad request for 127.0.0.1
+ has made its way to the net, so delete it. */
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* Either we respond with our own hw address, or we do proxy arp for
+ another machine. */
+ arp_send(ARPOP_REPLY, sip, dev, tip, sha,
+ (addr_hint == IS_MYADDR)? dev->dev_addr : ha);
- if(chk_addr(dst)==IS_BROADCAST)
- {
kfree_skb(skb, FREE_READ);
return 0;
- }
-
- memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
- if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) {
- DPRINTF((DBG_ARP, "ARP: request was not for me!\n"));
- kfree_skb(skb, FREE_READ);
- return(0);
- }
-
- /*
- * Yes, it is for us.
- * Allocate, fill in and send an ARP REPLY packet.
- */
- ret = arp_response(arp, dev, addr_hint);
- kfree_skb(skb, FREE_READ);
- return(ret);
}
-/* Create and send an ARP REQUEST packet. */
-void
-arp_send(unsigned long paddr, struct device *dev, unsigned long saddr)
+/*
+ * Find an arp mapping in the cache. If not found, post a request.
+ */
+
+int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
+ unsigned long saddr, struct sk_buff *skb)
{
- struct sk_buff *skb;
- struct arphdr *arp;
- unsigned char *ptr;
- int tmp;
-
- DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr)));
- DPRINTF((DBG_ARP, "dev=%s, ", dev->name));
- DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr)));
-
- skb = alloc_skb(sizeof(struct sk_buff) +
- sizeof(struct arphdr) + (2 * dev->addr_len) +
- dev->hard_header_len +
- (2 * 4 /* arp->plen */), GFP_ATOMIC);
- if (skb == NULL) {
- printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr));
- return;
- }
-
- /* Fill in the request. */
- skb->sk = NULL;
- skb->mem_addr = skb;
- skb->len = sizeof(struct arphdr) +
- dev->hard_header_len + (2 * dev->addr_len) + 8;
- skb->mem_len = sizeof(struct sk_buff) + skb->len;
- skb->arp = 1;
- skb->dev = dev;
- skb->next = NULL;
- skb->free = 1;
- tmp = dev->hard_header(skb->data, dev, ETH_P_ARP, 0, saddr, skb->len);
- if (tmp < 0) {
- kfree_skb(skb,FREE_WRITE);
- return;
- }
- arp = (struct arphdr *) (skb->data + tmp);
- arp->ar_hrd = htons(dev->type);
- if(dev->type!=3) /* AX.25 */
- arp->ar_pro = htons(ETH_P_IP);
- else
- arp->ar_pro = htons(0xCC);
- arp->ar_hln = dev->addr_len;
- arp->ar_pln = 4;
- arp->ar_op = htons(ARPOP_REQUEST);
-
- ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
- memcpy(ptr, dev->dev_addr, arp->ar_hln);
- ptr += arp->ar_hln;
- memcpy(ptr, &saddr, arp->ar_pln);
- ptr += arp->ar_pln;
- /*memcpy(ptr, dev->broadcast, arp->ar_hln);*/
- memset(ptr,0,arp->ar_hln);
- ptr += arp->ar_hln;
- memcpy(ptr, &paddr, arp->ar_pln);
-
- DPRINTF((DBG_ARP, ">>\n"));
- arp_print(arp);
- dev->queue_xmit(skb, dev, 0);
-}
-
+ struct arp_table *entry;
+ unsigned long hash;
+
+ switch (ip_chk_addr(paddr))
+ {
+ case IS_MYADDR:
+ printk("ARP: arp called for own IP address\n");
+ memcpy(haddr, dev->dev_addr, dev->addr_len);
+ skb->arp = 1;
+ return 0;
+ case IS_BROADCAST:
+ memcpy(haddr, dev->broadcast, dev->addr_len);
+ skb->arp = 1;
+ return 0;
+ }
-/* Find an ARP mapping in the cache. If not found, post a REQUEST. */
-int
-arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
- unsigned long saddr)
-{
- struct arp_table *apt;
-
- DPRINTF((DBG_ARP, "ARP: find(haddr=%s, ", eth_print(haddr)));
- DPRINTF((DBG_ARP, "paddr=%s, ", in_ntoa(paddr)));
- DPRINTF((DBG_ARP, "dev=%s, saddr=%s)\n", dev->name, in_ntoa(saddr)));
-
- switch(chk_addr(paddr)) {
- case IS_MYADDR:
- memcpy(haddr, dev->dev_addr, dev->addr_len);
- return(0);
- case IS_BROADCAST:
- memcpy(haddr, dev->broadcast, dev->addr_len);
- return(0);
- }
-
- apt = arp_lookup(paddr);
- if (apt != NULL) {
+ hash = HASH(paddr);
+ cli();
+
/*
- * Make sure it's not too old. If it is too old, we will
- * just pretend we did not find it, and then arp_send will
- * verify the address for us.
+ * Find an entry
*/
- if ((apt->flags & ATF_PERM) ||
- (apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) {
- apt->last_used = jiffies;
- memcpy(haddr, apt->ha, dev->addr_len);
- return(0);
- } else {
- DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n",
- in_ntoa(apt->ip)));
+ for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+ if (entry->ip == paddr)
+ break;
+
+
+ if (entry != NULL) /* It exists */
+ {
+ if (!(entry->flags & ATF_COM))
+ {
+ /*
+ * A request was already send, but no reply yet. Thus
+ * queue the packet with the previous attempt
+ */
+
+ if (skb != NULL)
+ skb_queue_tail(&entry->skb, skb);
+ sti();
+ return 1;
+ }
+
+ /*
+ * Update the record
+ */
+
+ entry->last_used = jiffies;
+ memcpy(haddr, entry->ha, dev->addr_len);
+ if (skb)
+ skb->arp = 1;
+ sti();
+ return 0;
}
- }
- /*
- * This assume haddr are at least 4 bytes.
- * If this isn't true we can use a lookup table, one for every dev.
- * NOTE: this bit of code still looks fishy to me- FvK
- */
- *(unsigned long *)haddr = paddr;
+ /*
+ * Create a new unresolved entry.
+ */
+
+ entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+ GFP_ATOMIC);
+ if (entry != NULL)
+ {
+ entry->ip = paddr;
+ entry->hlen = dev->addr_len;
+ entry->htype = dev->type;
+ entry->flags = 0;
+ memset(entry->ha, 0, dev->addr_len);
+ entry->last_used = jiffies;
+ entry->next = arp_tables[hash];
+ entry->dev = dev;
+ arp_tables[hash] = entry;
+ entry->timer.function = arp_expire_request;
+ entry->timer.data = (unsigned long)entry;
+ entry->timer.expires = ARP_RES_TIME;
+ add_timer(&entry->timer);
+ entry->retries = ARP_MAX_TRIES;
+ skb_queue_head_init(&entry->skb);
+ if (skb != NULL)
+ skb_queue_tail(&entry->skb, skb);
+ }
+ else
+ {
+ if (skb != NULL && skb->free)
+ kfree_skb(skb, FREE_WRITE);
+ }
+ sti();
- /* If we didn't find an entry, we will try to send an ARP packet. */
- arp_send(paddr, dev, saddr);
+ /*
+ * If we didn't find an entry, we will try to send an ARP packet.
+ */
+
+ arp_send(ARPOP_REQUEST, paddr, dev, saddr, NULL, dev->dev_addr);
- return(1);
+ return 1;
}
-/* Add an entry to the ARP cache. Check for dupes! */
-void
-arp_add(unsigned long addr, unsigned char *haddr, struct device *dev)
+/*
+ * Write the contents of the ARP cache to a PROCfs file.
+ *
+ * Will change soon to ASCII format
+ */
+
+int arp_get_info(char *buffer, char **start, off_t offset, int length)
{
- struct arp_table *apt;
-
- DPRINTF((DBG_ARP, "ARP: add(%s, ", in_ntoa(addr)));
- DPRINTF((DBG_ARP, "%s, ", eth_print(haddr)));
- DPRINTF((DBG_ARP, "%d, %d)\n", dev->hard_header_len, dev->type));
+ struct arp_table *entry;
+ struct arpreq *req = (struct arpreq *) buffer;
+ int i;
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
- /* This is probably a good check... */
- if (addr == 0) {
- printk("ARP: add: will not add entry for 0.0.0.0 !\n");
- return;
- }
-
- /* First see if the address is already in the table. */
- apt = arp_lookup(addr);
- if (apt != NULL) {
- DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr)));
- apt->last_used = jiffies;
- memcpy(apt->ha, haddr , dev->addr_len);
- return;
- }
- arp_create(addr, haddr, dev->addr_len, dev->type);
+ cli();
+ /* Loop over the ARP table and copy structures to the buffer. */
+ for (i = 0; i < ARP_TABLE_SIZE; i++)
+ {
+ for (entry = arp_tables[i]; entry; entry = entry->next)
+ {
+ memset(req, 0, sizeof(struct arpreq));
+ req->arp_pa.sa_family = AF_INET;
+ memcpy(req->arp_pa.sa_data, &entry->ip, 4);
+ req->arp_ha.sa_family = entry->htype;
+ memcpy(req->arp_ha.sa_data, &entry->ha, MAX_ADDR_LEN);
+ req->arp_flags = entry->flags;
+ req++;
+ len+=sizeof(struct arpreq);
+ pos+=sizeof(struct arpreq);
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ req=(struct arpreq *) buffer;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ sti();
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
-/* Create an ARP entry for a device's broadcast address. */
-void
-arp_add_broad(unsigned long addr, struct device *dev)
+/*
+ * This will find an entry in the ARP table by looking at the IP address.
+ * Be careful, interrupts are turned off on exit!!!
+ */
+
+static struct arp_table *arp_lookup(unsigned long paddr)
{
- struct arp_table *apt;
+ struct arp_table *entry;
+ unsigned long hash = HASH(paddr);
- arp_add(addr, dev->broadcast, dev);
- apt = arp_lookup(addr);
- if (apt != NULL) {
- apt->flags |= ATF_PERM;
- }
+ cli();
+ for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+ if (entry->ip == paddr) break;
+ return entry;
}
-/* Queue an IP packet, while waiting for the ARP reply packet. */
-void
-arp_queue(struct sk_buff *skb)
+/*
+ * Set (create) an ARP cache entry.
+ */
+
+static int arp_req_set(struct arpreq *req)
{
- cli();
- skb->tries = ARP_MAX_TRIES;
+ struct arpreq r;
+ struct arp_table *entry;
+ struct sockaddr_in *si;
+ int htype, hlen;
+ unsigned long ip, hash;
+ struct rtable *rt;
+
+ memcpy_fromfs(&r, req, sizeof(r));
- if (skb->next != NULL) {
+ /* We only understand about IP addresses... */
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+
+ /*
+ * Find out about the hardware type.
+ * We have to be compatible with BSD UNIX, so we have to
+ * assume that a "not set" value (i.e. 0) means Ethernet.
+ */
+
+ switch (r.arp_ha.sa_family) {
+ case 0:
+ /* Moan about this. ARP family 0 is NetROM and _will_ be needed */
+ printk("Application using old BSD convention for arp set. Please recompile it.\n");
+ case ARPHRD_ETHER:
+ htype = ARPHRD_ETHER;
+ hlen = ETH_ALEN;
+ break;
+#ifdef CONFIG_AX25
+ case ARPHRD_AX25:
+ htype = ARPHRD_AX25;
+ hlen = 7;
+ break;
+#endif
+ default:
+ return -EPFNOSUPPORT;
+ }
+
+ si = (struct sockaddr_in *) &r.arp_pa;
+ ip = si->sin_addr.s_addr;
+ if (ip == 0)
+ {
+ printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Is it reachable directly ?
+ */
+
+ rt = ip_rt_route(ip, NULL, NULL);
+ if (rt == NULL)
+ return -ENETUNREACH;
+
+ /*
+ * Is there an existing entry for this address?
+ */
+
+ hash = HASH(ip);
+ cli();
+
+ /*
+ * Find the entry
+ */
+ for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+ if (entry->ip == ip)
+ break;
+
+ /*
+ * Do we need to create a new entry
+ */
+
+ if (entry == NULL)
+ {
+ entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ {
+ sti();
+ return -ENOMEM;
+ }
+ entry->ip = ip;
+ entry->hlen = hlen;
+ entry->htype = htype;
+ entry->next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ skb_queue_head_init(&entry->skb);
+ }
+ else
+ if (entry->flags & ATF_PUBL)
+ proxies--;
+ /*
+ * We now have a pointer to an ARP entry. Update it!
+ */
+
+ memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
+ entry->last_used = jiffies;
+ entry->flags = r.arp_flags | ATF_COM;
+ if (entry->flags & ATF_PUBL)
+ proxies++;
+ entry->dev = rt->rt_dev;
sti();
- printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic);
- return;
- }
- if(arp_q==NULL)
- arp_queue_kick();
- skb_queue_tail(&arp_q,skb);
- skb->magic = ARP_QUEUE_MAGIC;
- sti();
+
+ return 0;
}
/*
- * Write the contents of the ARP cache to a PROCfs file.
- * This is not by long perfect, as the internal ARP table doesn't
- * have all the info we would like to have. Oh well, it works for
- * now, eh? - FvK
- * Also note, that due to space limits, we cannot generate more than
- * 4Kbyte worth of data. This usually is enough, but I have seen
- * machines die from under me because of a *very* large ARP cache.
- * This can be simply tested by doing:
- *
- * # ping 255.255.255.255
- * # arp -a
- *
- * Perhaps we should redo PROCfs to handle larger buffers? Michael?
+ * Get an ARP cache entry.
*/
-int
-arp_get_info(char *buffer)
+
+static int arp_req_get(struct arpreq *req)
{
- struct arpreq *req;
- struct arp_table *apt;
- int i;
- char *pos;
-
- /* Loop over the ARP table and copy structures to the buffer. */
- pos = buffer;
- i = 0;
- for (i = 0; i < ARP_TABLE_SIZE; i++) {
- cli();
- apt = arp_tables[i];
- sti();
- while (apt != NULL) {
- if (pos < (buffer + 4000)) {
- req = (struct arpreq *) pos;
- memset((char *) req, 0, sizeof(struct arpreq));
- req->arp_pa.sa_family = AF_INET;
- memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4);
- req->arp_ha.sa_family = apt->htype;
- memcpy((char *) req->arp_ha.sa_data,
- (char *) &apt->ha, apt->hlen);
- req->arp_flags = apt->flags;
- }
- pos += sizeof(struct arpreq);
- cli();
- apt = apt->next;
+ struct arpreq r;
+ struct arp_table *entry;
+ struct sockaddr_in *si;
+
+ /*
+ * We only understand about IP addresses...
+ */
+
+ memcpy_fromfs(&r, req, sizeof(r));
+
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+
+ /*
+ * Is there an existing entry for this address?
+ */
+
+ si = (struct sockaddr_in *) &r.arp_pa;
+ entry = arp_lookup(si->sin_addr.s_addr);
+
+ if (entry == NULL)
+ {
sti();
+ return -ENXIO;
}
- }
- return(pos - buffer);
-}
+ /*
+ * We found it; copy into structure.
+ */
+
+ memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
+ r.arp_ha.sa_family = entry->htype;
+ r.arp_flags = entry->flags;
+ sti();
-/* Set (create) an ARP cache entry. */
-static int
-arp_req_set(struct arpreq *req)
-{
- struct arpreq r;
- struct arp_table *apt;
- struct sockaddr_in *si;
- int htype, hlen;
-
- /* We only understand about IP addresses... */
- memcpy_fromfs(&r, req, sizeof(r));
- if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
- /*
- * Find out about the hardware type.
- * We have to be compatible with BSD UNIX, so we have to
- * assume that a "not set" value (i.e. 0) means Ethernet.
- */
- si = (struct sockaddr_in *) &r.arp_pa;
- switch(r.arp_ha.sa_family) {
- case 0:
- case ARPHRD_ETHER:
- htype = ARPHRD_ETHER;
- hlen = ETH_ALEN;
- break;
- case ARPHRD_AX25:
- htype = ARPHRD_AX25;
- hlen = 7;
- break;
-
- default:
- return(-EPFNOSUPPORT);
- }
-
- /* Is there an existing entry for this address? */
- if (si->sin_addr.s_addr == 0) {
- printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
- return(-EINVAL);
- }
- apt = arp_lookup(si->sin_addr.s_addr);
- if (apt == NULL) {
- apt = arp_create(si->sin_addr.s_addr,
- (unsigned char *) r.arp_ha.sa_data, hlen, htype);
- if (apt == NULL) return(-ENOMEM);
- }
-
- /* We now have a pointer to an ARP entry. Update it! */
- memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen);
- apt->last_used = jiffies;
- apt->flags = r.arp_flags;
- if(apt->flags&ATF_PUBL)
- arp_proxies++; /* Count proxy arps so we know if to use it */
-
- return(0);
+ /*
+ * Copy the information back
+ */
+
+ memcpy_tofs(req, &r, sizeof(r));
+ return 0;
}
-/* Get an ARP cache entry. */
-static int
-arp_req_get(struct arpreq *req)
+/*
+ * Handle an ARP layer I/O control request.
+ */
+
+int arp_ioctl(unsigned int cmd, void *arg)
{
- struct arpreq r;
- struct arp_table *apt;
- struct sockaddr_in *si;
-
- /* We only understand about IP addresses... */
- memcpy_fromfs(&r, req, sizeof(r));
- if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
- /* Is there an existing entry for this address? */
- si = (struct sockaddr_in *) &r.arp_pa;
- apt = arp_lookup(si->sin_addr.s_addr);
- if (apt == NULL) return(-ENXIO);
-
- /* We found it; copy into structure. */
- memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen);
- r.arp_ha.sa_family = apt->htype;
-
- /* Copy the information back */
- memcpy_tofs(req, &r, sizeof(r));
- return(0);
+ struct arpreq r;
+ struct sockaddr_in *si;
+ int err;
+
+ switch(cmd)
+ {
+ case DDIOCSDBG:
+ return dbg_ioctl(arg, DBG_ARP);
+ case SIOCDARP:
+ if (!suser())
+ return -EPERM;
+ err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+ if(err)
+ return err;
+ memcpy_fromfs(&r, arg, sizeof(r));
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+ si = (struct sockaddr_in *) &r.arp_pa;
+ arp_destroy(si->sin_addr.s_addr, 1);
+ return 0;
+ case SIOCGARP:
+ err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+ if(err)
+ return err;
+ return arp_req_get((struct arpreq *)arg);
+ case SIOCSARP:
+ if (!suser())
+ return -EPERM;
+ err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+ if(err)
+ return err;
+ return arp_req_set((struct arpreq *)arg);
+ default:
+ return -EINVAL;
+ }
+ /*NOTREACHED*/
+ return 0;
}
-/* Delete an ARP cache entry. */
-static int
-arp_req_del(struct arpreq *req)
-{
- struct arpreq r;
- struct sockaddr_in *si;
-
- /* We only understand about IP addresses... */
- memcpy_fromfs(&r, req, sizeof(r));
- if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
- si = (struct sockaddr_in *) &r.arp_pa;
-
- /* The system cope with this but splats up a nasty kernel message
- We trap it beforehand and tell the user off */
- if(chk_addr(si->sin_addr.s_addr)==IS_MYADDR)
- return -EINVAL;
-
- arp_destroy(si->sin_addr.s_addr);
-
- return(0);
-}
-
+/*
+ * Called once on startup.
+ */
-/* Handle an ARP layer I/O control request. */
-int
-arp_ioctl(unsigned int cmd, void *arg)
+static struct packet_type arp_packet_type =
{
- int err;
- switch(cmd) {
- case DDIOCSDBG:
- return(dbg_ioctl(arg, DBG_ARP));
- case SIOCDARP:
- if (!suser()) return(-EPERM);
- err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq));
- if(err)
- return err;
- return(arp_req_del((struct arpreq *)arg));
- case SIOCGARP:
- err=verify_area(VERIFY_WRITE,arg,sizeof(struct arpreq));
- if(err)
- return err;
- return(arp_req_get((struct arpreq *)arg));
- case SIOCSARP:
- if (!suser()) return(-EPERM);
- err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq));
- if(err)
- return err;
- return(arp_req_set((struct arpreq *)arg));
- default:
- return(-EINVAL);
- }
- /*NOTREACHED*/
- return(0);
+ 0, /* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */
+ 0, /* copy */
+ arp_rcv,
+ NULL,
+ NULL
+};
+
+void arp_init (void)
+{
+ /* Register the packet type */
+ arp_packet_type.type=htons(ETH_P_ARP);
+ dev_add_pack(&arp_packet_type);
+ /* Start with the regular checks for expired arp entries. */
+ add_timer(&arp_timer);
}
+
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * Definitions for the ARP protocol module.
- *
- * Version: @(#)arp.h 1.0.6 05/21/93
- *
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
+/* linux/net/inet/arp.h */
#ifndef _ARP_H
#define _ARP_H
-#define ARP_TABLE_SIZE 16 /* size of ARP table */
-#define ARP_TIMEOUT 30000 /* five minutes */
-#define ARP_RES_TIME 250 /* 2.5 seconds */
-
-#define ARP_MAX_TRIES 3 /* max # of tries to send ARP */
-#define ARP_QUEUE_MAGIC 0x0432447A /* magic # for queues */
-
-
-/* This structure defines the ARP mapping cache. */
-struct arp_table {
- struct arp_table *next;
- volatile unsigned long last_used;
- unsigned int flags;
-#if 1
- unsigned long ip;
-#else
- unsigned char pa[MAX_ADDR_LEN];
- unsigned char plen;
- unsigned char ptype;
-#endif
- unsigned char ha[MAX_ADDR_LEN];
- unsigned char hlen;
- unsigned char htype;
-};
-
-
-/* This is also used in "sock.c" and "tcp.c" - YUCK! - FvK */
-extern struct sk_buff *arp_q;
-
-
-extern void arp_destroy(unsigned long paddr);
+extern void arp_init(void);
+extern void arp_destroy(unsigned long paddr, int force);
extern int arp_rcv(struct sk_buff *skb, struct device *dev,
struct packet_type *pt);
extern int arp_find(unsigned char *haddr, unsigned long paddr,
- struct device *dev, unsigned long saddr);
-extern void arp_add(unsigned long addr, unsigned char *haddr,
- struct device *dev);
-extern void arp_add_broad(unsigned long addr, struct device *dev);
-extern void arp_queue(struct sk_buff *skb);
-extern int arp_get_info(char *buffer);
+ struct device *dev, unsigned long saddr, struct sk_buff *skb);
+extern int arp_get_info(char *buffer, char **start, off_t origin, int length);
extern int arp_ioctl(unsigned int cmd, void *arg);
-extern void arp_destroy_maybe(unsigned long paddr);
#endif /* _ARP_H */
/*
- * SUCS NET2 Debugged.
+ * SUCS NET3:
*
* Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
* of these would make sense. Not tonight however 8-).
* Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
* AX.25 now works right, and SPX is feasible.
* Alan Cox : Fixed write select of non IP protocol crash.
+ * Florian La Roche: Changed for my new skbuff handling.
+ *
+ * Note:
+ * A lot of this will change when the protocol/socket seperation
+ * occurs. Using this will make things reasonably clean.
*/
#include <linux/config.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/sched.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
-#include "arp.h"
#include "route.h"
#include "tcp.h"
#include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
/* Socket is inuse - so the timer doesn't attack it */
restart:
sk->inuse = 1;
- while(sk->rqueue == NULL) /* No data */
+ while(skb_peek(&sk->receive_queue) == NULL) /* No data */
{
/* If we are shutdown then no more data is going to appear. We are done */
if (sk->shutdown & RCV_SHUTDOWN)
/* Interrupts off so that no packet arrives before we begin sleeping.
Otherwise we might miss our wake up */
cli();
- if (sk->rqueue == NULL)
+ if (skb_peek(&sk->receive_queue) == NULL)
{
interruptible_sleep_on(sk->sleep);
/* Signals may need a restart of the syscall */
sti();
}
/* Again only user level code calls this function, so nothing interrupt level
- will suddenely eat the rqueue */
+ will suddenely eat the receive_queue */
if (!(flags & MSG_PEEK))
{
- skb=skb_dequeue(&sk->rqueue);
+ skb=skb_dequeue(&sk->receive_queue);
if(skb!=NULL)
skb->users++;
else
else
{
cli();
- skb=skb_peek(&sk->rqueue);
+ skb=skb_peek(&sk->receive_queue);
if(skb!=NULL)
skb->users++;
sti();
return;
}
/* See if it needs destroying */
- if(skb->list == NULL) /* Been dequeued by someone - ie its read */
+ if(!skb->next && !skb->prev) /* Been dequeued by someone - ie its read */
kfree_skb(skb,FREE_READ);
restore_flags(flags);
}
/* Connection closed: Wake up */
return(1);
}
- if (sk->rqueue != NULL || sk->err != 0)
+ if (skb_peek(&sk->receive_queue) != NULL || sk->err != 0)
{ /* This appears to be consistent
with other stacks */
return(1);
/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * Interface (streams) handling functions.
- *
- * Version: @(#)dev.c 1.0.19 05/31/93
- *
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- * Mark Evans, <evansmp@uhura.aston.ac.uk>
- *
- * Fixes:
- * Alan Cox: check_addr returns a value for a wrong subnet
- * ie not us but don't forward this!
- * Alan Cox: block timer if the inet_bh handler is running
- * Alan Cox: generic queue code added. A lot neater now
- * C.E.Hawkins: SIOCGIFCONF only reports 'upped' interfaces
- * C.E.Hawkins: IFF_PROMISC support
- * Alan Cox: Supports Donald Beckers new hardware
- * multicast layer, but not yet multicast lists.
- * Alan Cox: ip_addr_match problems with class A/B nets.
- * C.E.Hawkins IP 0.0.0.0 and also same net route fix. [FIXME: Ought to cause ICMP_REDIRECT]
- * Alan Cox: Removed bogus subnet check now the subnet code
- * a) actually works for all A/B nets
- * b) doesn't forward off the same interface.
- * Alan Cox: Multiple extra protocols
- * Alan Cox: Fixed ifconfig up of dud device setting the up flag
- * Alan Cox: Fixed verify_area errors
- * Alan Cox: Removed IP_SET_DEV as per Fred's comment. I hope this doesn't give
- * anything away 8)
+ * NET3 Protocol independant device support routines.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
+ * Derived from the non IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Florian la Roche <rzsfl@rz.uni-sb.de>
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ * Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have
+ * the rest as well commented in the end.
+ */
+
+/*
+ * A lot of these includes will be going walkies very soon
*/
+
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include "ip.h"
#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "arp.h"
-#ifdef CONFIG_AX25
-#include "ax25.h"
-#endif
-#ifdef CONFIG_IPX
-
-static struct packet_type ipx_8023_type = {
- NET16(ETH_P_802_3),
- 0,
- ipx_rcv,
- NULL,
- NULL
-};
-
-static struct packet_type ipx_packet_type = {
- NET16(ETH_P_IPX),
- 0,
- ipx_rcv,
- NULL,
- &ipx_8023_type
-};
-
-#endif
+/*
+ * The list of packet types we will receive (as opposed to discard)
+ * and the routines to invoke.
+ */
-#ifdef CONFIG_AX25
-
-static struct packet_type ax25_packet_type = {
- NET16(ETH_P_AX25),
- 0,
- ax25_rcv,
- NULL,
-#ifdef CONFIG_IPX
- &ipx_packet_type
-#else
- NULL
-#endif
-};
-#endif
+struct packet_type *ptype_base = NULL;
+/*
+ * Device drivers call our routines to queue packets here. We empty the
+ * queue in the bottom half handler.
+ */
-static struct packet_type arp_packet_type = {
- NET16(ETH_P_ARP),
- 0, /* copy */
- arp_rcv,
- NULL,
-#ifdef CONFIG_IPX
-#ifndef CONFIG_AX25
- &ipx_packet_type
-#else
- &ax25_packet_type
-#endif
-#else
-#ifdef CONFIG_AX25
- &ax25_packet_type
-#else
- NULL /* next */
-#endif
+static struct sk_buff_head backlog =
+{
+ (struct sk_buff *)&backlog, (struct sk_buff *)&backlog
+#ifdef CONFIG_SKB_CHECK
+ ,SK_HEAD_SKB
#endif
};
-
-static struct packet_type ip_packet_type = {
- NET16(ETH_P_IP),
- 0, /* copy */
- ip_rcv,
- NULL,
- &arp_packet_type
-};
-
-
-struct packet_type *ptype_base = &ip_packet_type;
-static struct sk_buff *volatile backlog = NULL;
+/*
+ * We don't overdo the queue or we will thrash memory badly.
+ */
+
static int backlog_size = 0;
-static unsigned long ip_bcast = 0;
+/*
+ * The number of sockets open for 'all' protocol use. We have to
+ * know this to copy a buffer the correct number of times.
+ */
+
+static int dev_nit=0;
-/* Return the lesser of the two values. */
-static unsigned long
-min(unsigned long a, unsigned long b)
+/*
+ * Return the lesser of the two values.
+ */
+
+static __inline__ unsigned long min(unsigned long a, unsigned long b)
{
- if (a < b) return(a);
- return(b);
+ return (a < b)? a : b;
}
-/* Determine a default network mask, based on the IP address. */
-static unsigned long
-get_mask(unsigned long addr)
-{
- unsigned long dst;
-
- if (addr == 0L)
- return(0L); /* special case */
-
- dst = ntohl(addr);
- if (IN_CLASSA(dst))
- return(htonl(IN_CLASSA_NET));
- if (IN_CLASSB(dst))
- return(htonl(IN_CLASSB_NET));
- if (IN_CLASSC(dst))
- return(htonl(IN_CLASSC_NET));
-
- /* Something else, probably a subnet. */
- return(0);
-}
+/******************************************************************************************
+ Protocol management and registration routines
-int
-ip_addr_match(unsigned long me, unsigned long him)
-{
- int i;
- unsigned long mask=0xFFFFFFFF;
- DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
- DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
-
- if (me == him)
- return(1);
- for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) {
- if ((me & 0xFF) != (him & 0xFF)) {
- /*
- * The only way this could be a match is for
- * the rest of addr1 to be 0 or 255.
- */
- if (me != 0 && me != mask) return(0);
- return(1);
- }
- }
- return(1);
-}
-
-
-/* Check the address for our address, broadcasts, etc. */
-int chk_addr(unsigned long addr)
-{
- struct device *dev;
- unsigned long mask;
-
- /* Accept both `all ones' and `all zeros' as BROADCAST. */
- if (addr == INADDR_ANY || addr == INADDR_BROADCAST)
- return IS_BROADCAST;
-
- mask = get_mask(addr);
-
- /* Accept all of the `loopback' class A net. */
- if ((addr & mask) == htonl(0x7F000000L))
- return IS_MYADDR;
-
- /* OK, now check the interface addresses. */
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (!(dev->flags & IFF_UP))
- continue;
- if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/)
- return IS_MYADDR;
- /* Is it the exact IP address? */
- if (addr == dev->pa_addr)
- return IS_MYADDR;
- /* Is it our broadcast address? */
- if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
- return IS_BROADCAST;
- /* Nope. Check for a subnetwork broadcast. */
- if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0) {
- if ((addr & ~dev->pa_mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
- return IS_BROADCAST;
- }
- /* Nope. Check for Network broadcast. */
- if (((addr ^ dev->pa_addr) & mask) == 0) {
- if ((addr & ~mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~mask) == ~mask)
- return IS_BROADCAST;
- }
- }
- return 0; /* no match at all */
-}
+*******************************************************************************************/
/*
- * Retrieve our own address.
- * Because the loopback address (127.0.0.1) is already recognized
- * automatically, we can use the loopback interface's address as
- * our "primary" interface. This is the addressed used by IP et
- * al when it doesn't know which address to use (i.e. it does not
- * yet know from or to which interface to go...).
+ * Add a protocol ID to the list.
*/
-unsigned long
-my_addr(void)
+
+void dev_add_pack(struct packet_type *pt)
{
- struct device *dev;
-
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev->flags & IFF_LOOPBACK) return(dev->pa_addr);
- }
- return(0);
-}
-
+ struct packet_type *p1;
+ pt->next = ptype_base;
-static int dev_nit=0; /* Number of network taps running */
-
-/* Add a protocol ID to the list. This will change soon. */
-void
-dev_add_pack(struct packet_type *pt)
-{
- struct packet_type *p1;
- pt->next = ptype_base;
-
- /* Don't use copy counts on ETH_P_ALL. Instead keep a global
- count of number of these and use it and pt->copy to decide
- copies */
- pt->copy=0;
- if(pt->type==NET16(ETH_P_ALL))
- dev_nit++; /* I'd like a /dev/nit too one day 8) */
- else
- {
- /* See if we need to copy it. */
- for (p1 = ptype_base; p1 != NULL; p1 = p1->next) {
- if (p1->type == pt->type) {
- pt->copy = 1;
- break;
- }
- }
- }
+ /*
+ * Don't use copy counts on ETH_P_ALL. Instead keep a global
+ * count of number of these and use it and pt->copy to decide
+ * copies
+ */
+
+ pt->copy=0; /* Assume we will not be copying the buffer before
+ * this routine gets it
+ */
+
+ if(pt->type == htons(ETH_P_ALL))
+ dev_nit++; /* I'd like a /dev/nit too one day 8) */
+ else
+ {
+ /*
+ * See if we need to copy it - that is another process also
+ * wishes to receive this type of packet.
+ */
+ for (p1 = ptype_base; p1 != NULL; p1 = p1->next)
+ {
+ if (p1->type == pt->type)
+ {
+ pt->copy = 1; /* We will need to copy */
+ break;
+ }
+ }
+ }
/*
* NIT taps must go at the end or inet_bh will leak!
*/
- if(pt->type==NET16(ETH_P_ALL))
- {
- pt->next=NULL;
- if(ptype_base==NULL)
- ptype_base=pt;
- else
+ if (pt->type == htons(ETH_P_ALL))
{
- for(p1=ptype_base;p1->next!=NULL;p1=p1->next);
- p1->next=pt;
- }
- }
- else
- ptype_base = pt;
+ pt->next=NULL;
+ if(ptype_base==NULL)
+ ptype_base=pt;
+ else
+ {
+ /*
+ * Move to the end of the list
+ */
+ for(p1=ptype_base;p1->next!=NULL;p1=p1->next);
+ /*
+ * Hook on the end
+ */
+ p1->next=pt;
+ }
+ }
+ else
+/*
+ * It goes on the start
+ */
+ ptype_base = pt;
}
-/* Remove a protocol ID from the list. This will change soon. */
-void
-dev_remove_pack(struct packet_type *pt)
+/*
+ * Remove a protocol ID from the list.
+ */
+
+void dev_remove_pack(struct packet_type *pt)
{
- struct packet_type *lpt, *pt1;
+ struct packet_type *lpt, *pt1;
- if (pt->type == NET16(ETH_P_ALL))
- dev_nit--;
- if (pt == ptype_base) {
- ptype_base = pt->next;
- return;
- }
-
- lpt = NULL;
- for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) {
- if (pt1->next == pt ) {
- cli();
- if (!pt->copy && lpt)
- lpt->copy = 0;
- pt1->next = pt->next;
- sti();
+ /*
+ * Keep the count of nit (Network Interface Tap) sockets correct.
+ */
+
+ if (pt->type == htons(ETH_P_ALL))
+ dev_nit--;
+
+ /*
+ * If we are first, just unhook us.
+ */
+
+ if (pt == ptype_base)
+ {
+ ptype_base = pt->next;
return;
}
- if (pt1->next -> type == pt ->type && pt->type != NET16(ETH_P_ALL)) {
- lpt = pt1->next;
+ lpt = NULL;
+
+ /*
+ * This is harder. What we do is to walk the list of sockets
+ * for this type. We unhook the entry, and if there is a previous
+ * entry that is copying _and_ we are not copying, (ie we are the
+ * last entry for this type) then the previous one is set to
+ * non-copying as it is now the last.
+ */
+ for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next)
+ {
+ if (pt1->next == pt )
+ {
+ cli();
+ if (!pt->copy && lpt)
+ lpt->copy = 0;
+ pt1->next = pt->next;
+ sti();
+ return;
+ }
+ if (pt1->next->type == pt->type && pt->type != htons(ETH_P_ALL))
+ lpt = pt1->next;
}
- }
}
+/*****************************************************************************************
-/* Find an interface in the list. This will change soon. */
-struct device *
-dev_get(char *name)
-{
- struct device *dev;
-
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (strcmp(dev->name, name) == 0)
- return(dev);
- }
- return(NULL);
-}
+ Device Inteface Subroutines
+******************************************************************************************/
-/* Find an interface that can handle addresses for a certain address. */
-struct device * dev_check(unsigned long addr)
+/*
+ * Find an interface by name.
+ */
+
+struct device *dev_get(char *name)
{
struct device *dev;
- for (dev = dev_base; dev; dev = dev->next) {
- if (!(dev->flags & IFF_UP))
- continue;
- if (!(dev->flags & IFF_POINTOPOINT))
- continue;
- if (addr != dev->pa_dstaddr)
- continue;
- return dev;
- }
- for (dev = dev_base; dev; dev = dev->next) {
- if (!(dev->flags & IFF_UP))
- continue;
- if (dev->flags & IFF_POINTOPOINT)
- continue;
- if (dev->pa_mask & (addr ^ dev->pa_addr))
- continue;
- return dev;
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (strcmp(dev->name, name) == 0)
+ return(dev);
}
- return NULL;
+ return(NULL);
}
-/* Prepare an interface for use. */
-int
-dev_open(struct device *dev)
+/*
+ * Prepare an interface for use.
+ */
+
+int dev_open(struct device *dev)
{
- int ret = 0;
+ int ret = 0;
- if (dev->open)
- ret = dev->open(dev);
- if (ret == 0)
- dev->flags |= (IFF_UP | IFF_RUNNING);
+ /*
+ * Call device private open method
+ */
+ if (dev->open)
+ ret = dev->open(dev);
- return(ret);
+ /*
+ * If it went open OK then set the flags
+ */
+
+ if (ret == 0)
+ dev->flags |= (IFF_UP | IFF_RUNNING);
+
+ return(ret);
}
-/* Completely shutdown an interface. */
-int
-dev_close(struct device *dev)
+/*
+ * Completely shutdown an interface.
+ *
+ * WARNING: Both because of the way the upper layers work (that can be fixed)
+ * and because of races during a close (that can't be fixed any other way)
+ * a device may be given things to transmit EVEN WHEN IT IS DOWN. The driver
+ * MUST cope with this (eg by freeing and dumping the frame).
+ */
+
+int dev_close(struct device *dev)
{
- if (dev->flags != 0) {
- int ct=0;
- dev->flags = 0;
- if (dev->stop)
- dev->stop(dev);
- rt_flush(dev);
- dev->pa_addr = 0;
- dev->pa_dstaddr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- /* Purge any queued packets when we down the link */
- while(ct<DEV_NUMBUFFS)
+ /*
+ * Only close a device if it is up.
+ */
+
+ if (dev->flags != 0)
{
- struct sk_buff *skb;
- while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
- if(skb->free)
- kfree_skb(skb,FREE_WRITE);
- ct++;
+ int ct=0;
+ dev->flags = 0;
+ /*
+ * Call the device specific close. This cannot fail.
+ */
+ if (dev->stop)
+ dev->stop(dev);
+ /*
+ * Delete the route to the device.
+ */
+ ip_rt_flush(dev);
+ /*
+ * Blank the IP addresses
+ */
+ dev->pa_addr = 0;
+ dev->pa_dstaddr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ /*
+ * Purge any queued packets when we down the link
+ */
+ while(ct<DEV_NUMBUFFS)
+ {
+ struct sk_buff *skb;
+ while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
+ if(skb->free)
+ kfree_skb(skb,FREE_WRITE);
+ ct++;
+ }
}
- }
-
- return(0);
+ return(0);
}
-/* Send (or queue for sending) a packet. */
-void
-dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+/*
+ * Send (or queue for sending) a packet.
+ */
+
+void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
- int where = 0; /* used to say if the packet should go */
+ int where = 0; /* used to say if the packet should go */
/* at the front or the back of the */
/* queue. */
- DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
+ DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
skb, dev, pri));
- if (dev == NULL) {
- printk("dev.c: dev_queue_xmit: dev = NULL\n");
- return;
- }
+ if (dev == NULL)
+ {
+ printk("dev.c: dev_queue_xmit: dev = NULL\n");
+ return;
+ }
- IS_SKB(skb);
+ IS_SKB(skb);
- skb->dev = dev;
- if (skb->next != NULL) {
- /* Make sure we haven't missed an interrupt. */
- dev->hard_start_xmit(NULL, dev);
- return;
- }
+ skb->dev = dev;
+ start_bh_atomic();
+
+ /*
+ * This just eliminates some race conditions, but not all...
+ */
+
+ if (skb->next != NULL)
+ {
+ /*
+ * Make sure we haven't missed an interrupt.
+ */
+ printk("dev_queue_xmit: worked around a missed interrupt\n");
+ dev->hard_start_xmit(NULL, dev);
+ end_bh_atomic();
+ return;
+ }
- if (pri < 0) {
- pri = -pri-1;
- where = 1;
- }
+ /*
+ * Negative priority is used to flag a frame that is being pulled from the
+ * queue front as a retransmit attempt. It therefore goes back on the queue
+ * start on a failure.
+ */
+
+ if (pri < 0)
+ {
+ pri = -pri-1;
+ where = 1;
+ }
+
+ if (pri >= DEV_NUMBUFFS)
+ {
+ printk("bad priority in dev_queue_xmit.\n");
+ pri = 1;
+ }
- if (pri >= DEV_NUMBUFFS) {
- printk("bad priority in dev_queue_xmit.\n");
- pri = 1;
- }
+ /*
+ * If the address has not been resolved called the device header rebuilder.
+ * This can cover all protocols and technically no just ARP either.
+ */
+
+ if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
+ end_bh_atomic();
+ return;
+ }
- if (dev->hard_start_xmit(skb, dev) == 0) {
- return;
- }
-
- /* Put skb into a bidirectional circular linked list. */
- DPRINTF((DBG_DEV, "dev_queue_xmit dev->buffs[%d]=%X\n",
- pri, dev->buffs[pri]));
-
- /* Interrupts should already be cleared by hard_start_xmit. */
- cli();
- skb->magic = DEV_QUEUE_MAGIC;
- if(where)
- skb_queue_head(&dev->buffs[pri],skb);
- else
- skb_queue_tail(&dev->buffs[pri],skb);
- skb->magic = DEV_QUEUE_MAGIC;
- sti();
+ /*
+ * This is vitally important. We _MUST_ keep packets in order. While tcp/ip
+ * suffers only a slow down some IPX apps, and all the AX.25 code will break
+ * if it occurs out of order.
+ *
+ * This is commented out while I fix a few 'side effects'
+ */
+
+ if ((where==1 || skb_peek(&dev->buffs[pri])==NULL) && dev->hard_start_xmit(skb, dev) == 0)
+ {
+ end_bh_atomic();
+ return;
+ }
+
+ /*
+ * Transmission failed, put skb back into a list.
+ */
+
+ if(where)
+ skb_queue_head(&dev->buffs[pri],skb);
+ else
+ skb_queue_tail(&dev->buffs[pri],skb);
+ end_bh_atomic();
}
/*
- * Receive a packet from a device driver and queue it for the upper
- * (protocol) levels. It always succeeds.
+ * Receive a packet from a device driver and queue it for the upper
+ * (protocol) levels. It always succeeds. This is the recommended
+ * interface to use.
*/
-void
-netif_rx(struct sk_buff *skb)
+
+void netif_rx(struct sk_buff *skb)
{
- static int dropping = 0;
- /* Set any necessary flags. */
- skb->sk = NULL;
- skb->free = 1;
-
- /* check that we aren't oevrdoing things.. */
- if (!backlog_size)
- dropping = 0;
- else if (backlog_size > 100)
- dropping = 1;
- if (dropping) {
- kfree_skb(skb, FREE_READ);
- return;
- }
- /* and add it to the "backlog" queue. */
- IS_SKB(skb);
- skb_queue_tail(&backlog,skb);
- backlog_size++;
+ static int dropping = 0;
+ extern struct timeval xtime;
+
+ /*
+ * Any received buffers are un-owned and should be discarded
+ * when freed. These will be updated later as the frames get
+ * owners.
+ */
+ skb->sk = NULL;
+ skb->free = 1;
+ if(skb->stamp.tv_sec==0)
+ skb->stamp = xtime;
+
+ /*
+ * Check that we aren't oevrdoing things.
+ */
+
+ if (!backlog_size)
+ dropping = 0;
+ else if (backlog_size > 100)
+ dropping = 1;
+
+ if (dropping)
+ {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Add it to the "backlog" queue.
+ */
+
+ IS_SKB(skb);
+ skb_queue_tail(&backlog,skb);
+ backlog_size++;
- /* If any packet arrived, mark it for processing. */
- if (backlog != NULL) mark_bh(INET_BH);
+ /*
+ * If any packet arrived, mark it for processing after the
+ * hardware interrupt returns.
+ */
- return;
+ mark_bh(INET_BH);
+ return;
}
/*
- * The old interface to fetch a packet from a device driver.
- * This function is the base level entry point for all drivers that
- * want to send a packet to the upper (protocol) levels. It takes
- * care of de-multiplexing the packet to the various modules based
- * on their protocol ID.
+ * The old interface to fetch a packet from a device driver.
+ * This function is the base level entry point for all drivers that
+ * want to send a packet to the upper (protocol) levels. It takes
+ * care of de-multiplexing the packet to the various modules based
+ * on their protocol ID.
*
- * Return values: 1 <- exit I can't do any more
+ * Return values: 1 <- exit I can't do any more
* 0 <- feed me more (i.e. "done", "OK").
+ *
+ * This function is OBSOLETE and should not be used by any new
+ * device.
*/
-int
-dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
+
+int dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
{
- static int dropping = 0;
- struct sk_buff *skb = NULL;
- unsigned char *to;
- int amount, left;
- int len2;
-
- if (dev == NULL || buff == NULL || len <= 0) return(1);
- if (flags & IN_SKBUFF) {
- skb = (struct sk_buff *) buff;
- } else {
- if (dropping) {
- if (backlog != NULL)
- return(1);
- printk("INET: dev_rint: no longer dropping packets.\n");
- dropping = 0;
- }
+ static int dropping = 0;
+ struct sk_buff *skb = NULL;
+ unsigned char *to;
+ int amount, left;
+ int len2;
- skb = alloc_skb(sizeof(*skb) + len, GFP_ATOMIC);
- if (skb == NULL) {
- printk("dev_rint: packet dropped on %s (no memory) !\n",
- dev->name);
- dropping = 1;
+ if (dev == NULL || buff == NULL || len <= 0)
return(1);
+
+ if (flags & IN_SKBUFF)
+ {
+ skb = (struct sk_buff *) buff;
}
- skb->mem_len = sizeof(*skb) + len;
- skb->mem_addr = (struct sk_buff *) skb;
-
- /* First we copy the packet into a buffer, and save it for later. */
- to = skb->data;
- left = len;
- len2 = len;
- while (len2 > 0) {
- amount = min(len2, (unsigned long) dev->rmem_end -
+ else
+ {
+ if (dropping)
+ {
+ if (skb_peek(&backlog) != NULL)
+ return(1);
+ printk("INET: dev_rint: no longer dropping packets.\n");
+ dropping = 0;
+ }
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (skb == NULL)
+ {
+ printk("dev_rint: packet dropped on %s (no memory) !\n",
+ dev->name);
+ dropping = 1;
+ return(1);
+ }
+
+ /*
+ * First we copy the packet into a buffer, and save it for later. We
+ * in effect handle the incoming data as if it were from a circular buffer
+ */
+
+ to = skb->data;
+ left = len;
+
+ len2 = len;
+ while (len2 > 0)
+ {
+ amount = min(len2, (unsigned long) dev->rmem_end -
(unsigned long) buff);
- memcpy(to, buff, amount);
- len2 -= amount;
- left -= amount;
- buff += amount;
- to += amount;
- if ((unsigned long) buff == dev->rmem_end)
- buff = (unsigned char *) dev->rmem_start;
+ memcpy(to, buff, amount);
+ len2 -= amount;
+ left -= amount;
+ buff += amount;
+ to += amount;
+ if ((unsigned long) buff == dev->rmem_end)
+ buff = (unsigned char *) dev->rmem_start;
+ }
}
- }
- skb->len = len;
- skb->dev = dev;
- skb->free = 1;
-
- netif_rx(skb);
- /* OK, all done. */
- return(0);
+
+ /*
+ * Tag the frame and kick it to the proper receive routine
+ */
+
+ skb->len = len;
+ skb->dev = dev;
+ skb->free = 1;
+
+ netif_rx(skb);
+ /*
+ * OK, all done.
+ */
+ return(0);
}
-/* This routine causes all interfaces to try to send some data. */
-void
-dev_transmit(void)
+/*
+ * This routine causes all interfaces to try to send some data.
+ */
+
+void dev_transmit(void)
{
- struct device *dev;
+ struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (!dev->tbusy) {
- dev_tint(dev);
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (!dev->tbusy) {
+ /*
+ * Kick the device
+ */
+ dev_tint(dev);
+ }
}
- }
}
-static volatile char in_bh = 0;
+
+/**********************************************************************************
+
+ Receive Queue Processor
+
+***********************************************************************************/
+
+/*
+ * This is a single non-rentrant routine which takes the received packet
+ * queue and throws it at the networking layers in the hope that something
+ * useful will emerge.
+ */
+
+volatile char in_bh = 0; /* Non-rentrant remember */
int in_inet_bh() /* Used by timer.c */
{
}
/*
- * This function gets called periodically, to see if we can
- * process any data that came in from some interface.
- *
+ * When we are called the queue is ready to grab, the interrupts are
+ * on and hardware can interrupt and queue to the receive queue a we
+ * run with no problems.
+ * This is run as a bottom half after an interrupt handler that does
+ * mark_bh(INET_BH);
*/
-void
-inet_bh(void *tmp)
+
+void inet_bh(void *tmp)
{
- struct sk_buff *skb;
- struct packet_type *ptype;
- unsigned short type;
- unsigned char flag = 0;
- int nitcount;
-
- /* Atomically check and mark our BUSY state. */
- if (set_bit(1, (void*)&in_bh))
- return;
-
- /* Can we send anything now? */
- dev_transmit();
+ struct sk_buff *skb;
+ struct packet_type *ptype;
+ unsigned short type;
+ unsigned char flag = 0;
+ int nitcount;
+
+ /*
+ * Atomically check and mark our BUSY state.
+ */
+
+ if (set_bit(1, (void*)&in_bh))
+ return;
+
+ /*
+ * Can we send anything now? We want to clear the
+ * decks for any more sends that get done as we
+ * process the input.
+ */
+
+ dev_transmit();
- /* Any data left to process? */
- while((skb=skb_dequeue(&backlog))!=NULL)
- {
- backlog_size--;
- nitcount=dev_nit;
- flag=0;
- sti();
- /*
- * Bump the pointer to the next structure.
- * This assumes that the basic 'skb' pointer points to
- * the MAC header, if any (as indicated by its "length"
- * field). Take care now!
- */
- skb->h.raw = skb->data + skb->dev->hard_header_len;
- skb->len -= skb->dev->hard_header_len;
-
- /*
- * Fetch the packet protocol ID. This is also quite ugly, as
- * it depends on the protocol driver (the interface itself) to
- * know what the type is, or where to get it from. The Ethernet
- * interfaces fetch the ID from the two bytes in the Ethernet MAC
- * header (the h_proto field in struct ethhdr), but drivers like
- * SLIP and PLIP have no alternative but to force the type to be
- * IP or something like that. Sigh- FvK
- */
- type = skb->dev->type_trans(skb, skb->dev);
+ /*
+ * Any data left to process. This may occur because a
+ * mark_bh() is done after we empty the queue including
+ * that from the device which does a mark_bh() just after
+ */
+ cli();
+
/*
- * We got a packet ID. Now loop over the "known protocols"
- * table (which is actually a linked list, but this will
- * change soon if I get my way- FvK), and forward the packet
- * to anyone who wants it.
+ * While the queue is not empty
*/
- for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) {
- if (ptype->type == type || ptype->type == NET16(ETH_P_ALL)) {
- struct sk_buff *skb2;
-
- if (ptype->type==NET16(ETH_P_ALL))
- nitcount--;
- if (ptype->copy || nitcount) { /* copy if we need to */
- skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC);
- if (skb2 == NULL)
- continue;
- memcpy(skb2, (const void *) skb, skb->mem_len);
- skb2->mem_addr = skb2;
- skb2->h.raw = (unsigned char *)(
- (unsigned long) skb2 +
- (unsigned long) skb->h.raw -
- (unsigned long) skb
- );
- skb2->free = 1;
- } else {
- skb2 = skb;
- }
+
+ while((skb=skb_dequeue(&backlog))!=NULL)
+ {
+ /*
+ * We have a packet. Therefore the queue has shrunk
+ */
+ backlog_size--;
- /* This used to be in the 'else' part, but then
- * we don't have this flag set when we get a
- * protocol that *does* require copying... -FvK
- */
- flag = 1;
+ nitcount=dev_nit;
+ flag=0;
+ sti();
+
+ /*
+ * Bump the pointer to the next structure.
+ * This assumes that the basic 'skb' pointer points to
+ * the MAC header, if any (as indicated by its "length"
+ * field). Take care now!
+ */
+
+ skb->h.raw = skb->data + skb->dev->hard_header_len;
+ skb->len -= skb->dev->hard_header_len;
+
+ /*
+ * Fetch the packet protocol ID. This is also quite ugly, as
+ * it depends on the protocol driver (the interface itself) to
+ * know what the type is, or where to get it from. The Ethernet
+ * interfaces fetch the ID from the two bytes in the Ethernet MAC
+ * header (the h_proto field in struct ethhdr), but other drivers
+ * may either use the ethernet ID's or extra ones that do not
+ * clash (eg ETH_P_AX25). We could set this before we queue the
+ * frame. In fact I may change this when I have time.
+ */
+
+ type = skb->dev->type_trans(skb, skb->dev);
- /* Kick the protocol handler. */
- ptype->func(skb2, skb->dev, ptype);
+ /*
+ * We got a packet ID. Now loop over the "known protocols"
+ * table (which is actually a linked list, but this will
+ * change soon if I get my way- FvK), and forward the packet
+ * to anyone who wants it.
+ *
+ * [FvK didn't get his way but he is right this ought to be
+ * hashed so we typically get a single hit. The speed cost
+ * here is minimal but no doubt adds up at the 4,000+ pkts/second
+ * rate we can hit flat out]
+ */
+
+ for (ptype = ptype_base; ptype != NULL; ptype = ptype->next)
+ {
+ if (ptype->type == type || ptype->type == htons(ETH_P_ALL))
+ {
+ struct sk_buff *skb2;
+
+ if (ptype->type == htons(ETH_P_ALL))
+ nitcount--;
+ if (ptype->copy || nitcount)
+ {
+ /*
+ * copy if we need to
+ */
+#ifdef OLD
+ skb2 = alloc_skb(skb->len, GFP_ATOMIC);
+ if (skb2 == NULL)
+ continue;
+ memcpy(skb2, skb, skb2->mem_len);
+ skb2->mem_addr = skb2;
+ skb2->h.raw = (unsigned char *)(
+ (unsigned long) skb2 +
+ (unsigned long) skb->h.raw -
+ (unsigned long) skb
+ );
+ skb2->free = 1;
+#else
+ skb2=skb_clone(skb, GFP_ATOMIC);
+ if(skb2==NULL)
+ continue;
+#endif
+ }
+ else
+ {
+ skb2 = skb;
+ }
+
+ /*
+ * Protocol located.
+ */
+
+ flag = 1;
+
+ /*
+ * Kick the protocol handler. This should be fast
+ * and efficient code.
+ */
+
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ } /* End of protocol list loop */
+
+ /*
+ * Has an unknown packet has been received ?
+ */
+
+ if (!flag)
+ {
+ DPRINTF((DBG_DEV,"INET: unknown packet type 0x%04X (ignored)\n", type));
+ kfree_skb(skb, FREE_WRITE);
}
- }
+ /*
+ * Again, see if we can transmit anything now.
+ */
+
+ dev_transmit();
+ cli();
+ } /* End of queue loop */
+
+ /*
+ * We have emptied the queue
+ */
+
+ in_bh = 0;
+ sti();
+
/*
- * That's odd. We got an unknown packet. Who's using
- * stuff like Novell or Amoeba on this network??
+ * One last output flush.
*/
- if (!flag) {
- DPRINTF((DBG_DEV,
- "INET: unknown packet type 0x%04X (ignored)\n", type));
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- }
-
- /* Again, see if we can transmit anything now. */
+
dev_transmit();
- cli();
- }
- in_bh = 0;
- sti();
- dev_transmit();
}
/*
- * This routine is called when an device driver (i.e. an
- * interface) is * ready to transmit a packet.
+ * This routine is called when an device driver (i.e. an
+ * interface) is ready to transmit a packet.
*/
void dev_tint(struct device *dev)
int i;
struct sk_buff *skb;
- for(i = 0;i < DEV_NUMBUFFS; i++) {
+ /*
+ * Work the queues in priority order
+ */
+
+ for(i = 0;i < DEV_NUMBUFFS; i++)
+ {
+ /*
+ * Pull packets from the queue
+ */
+
while((skb=skb_dequeue(&dev->buffs[i]))!=NULL)
{
- skb->magic = 0;
- skb->next = NULL;
- skb->prev = NULL;
- dev->queue_xmit(skb,dev,-i - 1);
+ /*
+ * Feed them to the output stage and if it fails
+ * indicate they re-queue at the front.
+ */
+ dev_queue_xmit(skb,dev,-i - 1);
+ /*
+ * If we can take no more then stop here.
+ */
if (dev->tbusy)
return;
}
}
-/* Perform a SIOCGIFCONF call. */
-static int
-dev_ifconf(char *arg)
+/*
+ * Perform a SIOCGIFCONF call. This structure will change
+ * size shortly, and there is nothing I can do about it.
+ * Thus we will need a 'compatibility mode'.
+ */
+
+static int dev_ifconf(char *arg)
{
- struct ifconf ifc;
- struct ifreq ifr;
- struct device *dev;
- char *pos;
- int len;
- int err;
-
- /* Fetch the caller's info block. */
- err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
- if(err)
- return err;
- memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
- len = ifc.ifc_len;
- pos = ifc.ifc_buf;
- err=verify_area(VERIFY_WRITE, pos, len);
- if(err)
- return err;
-
- /* Loop over the interfaces, and write an info block for each. */
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if(!(dev->flags & IFF_UP))
- continue;
- memset(&ifr, 0, sizeof(struct ifreq));
- strcpy(ifr.ifr_name, dev->name);
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
-
- /* Write this block to the caller's space. */
- memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
- pos += sizeof(struct ifreq);
- len -= sizeof(struct ifreq);
- if (len < sizeof(struct ifreq)) break;
- }
-
- /* All done. Write the updated control block back to the caller. */
- ifc.ifc_len = (pos - ifc.ifc_buf);
- ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
- memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
- return(pos - arg);
+ struct ifconf ifc;
+ struct ifreq ifr;
+ struct device *dev;
+ char *pos;
+ int len;
+ int err;
+
+ /*
+ * Fetch the caller's info block.
+ */
+
+ err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
+ if(err)
+ return err;
+ memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
+ len = ifc.ifc_len;
+ pos = ifc.ifc_buf;
+
+ /*
+ * We now walk the device list filling each active device
+ * into the array.
+ */
+
+ err=verify_area(VERIFY_WRITE,pos,len);
+ if(err)
+ return err;
+
+ /*
+ * Loop over the interfaces, and write an info block for each.
+ */
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if(!(dev->flags & IFF_UP)) /* Downed devices don't count */
+ continue;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+
+ /*
+ * Write this block to the caller's space.
+ */
+
+ memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+ pos += sizeof(struct ifreq);
+ len -= sizeof(struct ifreq);
+
+ /*
+ * Have we run out of space here ?
+ */
+
+ if (len < sizeof(struct ifreq))
+ break;
+ }
+
+ /*
+ * All done. Write the updated control block back to the caller.
+ */
+
+ ifc.ifc_len = (pos - ifc.ifc_buf);
+ ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+ memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
+
+ /*
+ * Report how much was filled in
+ */
+
+ return(pos - arg);
}
-/* Print device statistics. */
-char *sprintf_stats(char *buffer, struct device *dev)
-{
- char *pos = buffer;
- struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
- if (stats)
- pos += sprintf(pos, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
+/*
+ * This is invoked by the /proc filesystem handler to display a device
+ * in detail.
+ */
+
+static int sprintf_stats(char *buffer, struct device *dev)
+{
+ struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+ int size;
+
+ if (stats)
+ size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
dev->name,
stats->rx_packets, stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors + stats->tx_aborted_errors
+ stats->tx_window_errors + stats->tx_heartbeat_errors);
- else
- pos += sprintf(pos, "%6s: No statistics available.\n", dev->name);
+ else
+ size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
- return pos;
+ return size;
}
-/* Called from the PROCfs module. */
-int
-dev_get_info(char *buffer)
+/*
+ * Called from the PROCfs module. This now uses the new arbitary sized /proc/net interface
+ * to create /proc/net/dev
+ */
+
+int dev_get_info(char *buffer, char **start, off_t offset, int length)
{
- char *pos = buffer;
- struct device *dev;
-
- pos +=
- sprintf(pos,
- "Inter-| Receive | Transmit\n"
- " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- pos = sprintf_stats(pos, dev);
- }
- return pos - buffer;
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size;
+
+ struct device *dev;
+
+
+ size = sprintf(buffer, "Inter-| Receive | Transmit\n"
+ " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
+
+ pos+=size;
+ len+=size;
+
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len=length; /* Ending slop */
+ return len;
}
+
+/*
+ * This checks bitmasks for the ioctl calls for devices.
+ */
+
static inline int bad_mask(unsigned long mask, unsigned long addr)
{
if (addr & (mask = ~mask))
return 0;
}
-
-/* Perform the SIOCxIFxxx calls. */
-static int
-dev_ifsioc(void *arg, unsigned int getset)
+/*
+ * Perform the SIOCxIFxxx calls.
+ *
+ * The socket layer has seen an ioctl the address family thinks is
+ * for the device. At this point we get invoked to make a decision
+ */
+
+static int dev_ifsioc(void *arg, unsigned int getset)
{
- struct ifreq ifr;
- struct device *dev;
- int ret;
-
- /* Fetch the caller's info block. */
- int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
- if(err)
- return err;
- memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
-
- /* See which interface the caller is talking about. */
- if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-EINVAL);
-
- switch(getset) {
- case SIOCGIFFLAGS:
- ifr.ifr_flags = dev->flags;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFFLAGS:
- {
- int old_flags = dev->flags;
- dev->flags = ifr.ifr_flags & (
- IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
- IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
- IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
-
- if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0))
- dev->set_multicast_list(dev,0,NULL);
- if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0))
- dev->set_multicast_list(dev,-1,NULL);
- if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) {
- ret = dev_close(dev);
- } else
- {
- ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP))
- ? dev_open(dev) : 0;
- if(ret<0)
- dev->flags&=~IFF_UP; /* Didnt open so down the if */
- }
- }
- break;
- case SIOCGIFADDR:
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_port = 0;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFADDR:
- dev->pa_addr = (*(struct sockaddr_in *)
+ struct ifreq ifr;
+ struct device *dev;
+ int ret;
+
+ /*
+ * Fetch the caller's info block into kernel space
+ */
+
+ int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+ if(err)
+ return err;
+
+ memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+
+ /*
+ * See which interface the caller is talking about.
+ */
+
+ if ((dev = dev_get(ifr.ifr_name)) == NULL)
+ return(-ENODEV);
+
+ switch(getset)
+ {
+ case SIOCGIFFLAGS: /* Get interface flags */
+ ifr.ifr_flags = dev->flags;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFFLAGS: /* Set interface flags */
+ {
+ int old_flags = dev->flags;
+ dev->flags = ifr.ifr_flags & (
+ IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
+ IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+ IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+
+ /*
+ * Has promiscuous mode been turned off
+ */
+ if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0))
+ dev->set_multicast_list(dev,0,NULL);
+
+ /*
+ * Has it been turned on
+ */
+
+ if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0))
+ dev->set_multicast_list(dev,-1,NULL);
+
+ /*
+ * Have we downed the interface
+ */
+
+ if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0))
+ {
+ ret = dev_close(dev);
+ }
+ else
+ {
+ /*
+ * Have we upped the interface
+ */
+
+ ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP))
+ ? dev_open(dev) : 0;
+ /*
+ * Check the flags.
+ */
+ if(ret<0)
+ dev->flags&=~IFF_UP; /* Didnt open so down the if */
+ }
+ }
+ break;
+
+ case SIOCGIFADDR: /* Get interface address (and family) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
+ dev->pa_addr = (*(struct sockaddr_in *)
&ifr.ifr_addr).sin_addr.s_addr;
- dev->family = ifr.ifr_addr.sa_family;
- dev->pa_mask = get_mask(dev->pa_addr);
- dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
- ret = 0;
- break;
- case SIOCGIFBRDADDR:
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_port = 0;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFBRDADDR:
- dev->pa_brdaddr = (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_addr.s_addr;
- ret = 0;
- break;
- case SIOCGIFDSTADDR:
- (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_port = 0;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFDSTADDR:
- dev->pa_dstaddr = (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_addr.s_addr;
- ret = 0;
- break;
- case SIOCGIFNETMASK:
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_port = 0;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFNETMASK: {
- unsigned long mask = (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_addr.s_addr;
- ret = -EINVAL;
- if (bad_mask(mask,0))
+ dev->family = ifr.ifr_addr.sa_family;
+ dev->pa_mask = ip_get_mask(dev->pa_addr);
+ dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+ ret = 0;
+ break;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ dev->pa_brdaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
+ dev->pa_dstaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ {
+ unsigned long mask = (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr;
+ ret = -EINVAL;
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(mask,0))
+ break;
+ dev->pa_mask = mask;
+ ret = 0;
+ }
+ break;
+
+ case SIOCGIFMETRIC: /* Get the metric on the inteface (currently unused) */
+
+ ifr.ifr_metric = dev->metric;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
+ dev->metric = ifr.ifr_metric;
+ ret = 0;
+ break;
+
+ case SIOCGIFMTU: /* Get the MTU of a device */
+ ifr.ifr_mtu = dev->mtu;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+
+ case SIOCSIFMTU: /* Set the MTU of a device */
+
+ /*
+ * MTU must be positive and under the page size problem
+ */
+
+ if(ifr.ifr_mtu<1 || ifr.ifr_mtu>3800)
+ return -EINVAL;
+ dev->mtu = ifr.ifr_mtu;
+ ret = 0;
+ break;
+
+ case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
+ do not support it */
+ printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
+ ret = -EINVAL;
+ break;
+
+ case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
+ printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
+ ret = -EINVAL;
+ break;
+
+ case OLD_SIOCGIFHWADDR: /* Get the hardware address. This will change and SIFHWADDR will be added */
+ memcpy(ifr.old_ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN);
+ memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+ ret=0;
break;
- dev->pa_mask = mask;
- ret = 0;
- break;
+
+ case SIOCGIFHWADDR:
+ memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family=dev->type;
+ memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+ ret=0;
+ break;
+
+ case SIOCSIFHWADDR:
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ if(ifr.ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ ret=dev->set_mac_address(dev,ifr.ifr_hwaddr.sa_data);
+ break;
+
+ /*
+ * Unknown ioctl
+ */
+
+ default:
+ ret = -EINVAL;
}
- case SIOCGIFMETRIC:
- ifr.ifr_metric = dev->metric;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFMETRIC:
- dev->metric = ifr.ifr_metric;
- ret = 0;
- break;
- case SIOCGIFMTU:
- ifr.ifr_mtu = dev->mtu;
- memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
- ret = 0;
- break;
- case SIOCSIFMTU:
- dev->mtu = ifr.ifr_mtu;
- ret = 0;
- break;
- case SIOCGIFMEM:
- printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
- ret = -EINVAL;
- break;
- case SIOCSIFMEM:
- printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
- ret = -EINVAL;
- break;
- case SIOCGIFHWADDR:
- memcpy(ifr.ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN);
- memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
- ret=0;
- break;
- default:
- ret = -EINVAL;
- }
- return(ret);
+ return(ret);
}
-/* This function handles all "interface"-type I/O control requests. */
-int
-dev_ioctl(unsigned int cmd, void *arg)
+/*
+ * This function handles all "interface"-type I/O control requests. The actual
+ * 'doing' part of this is dev_ifsioc above.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
{
- struct iflink iflink;
- struct ddi_device *dev;
-
- switch(cmd) {
- case IP_SET_DEV:
- printk("Your network configuration program needs upgrading.\n");
- return -EINVAL;
-
- case SIOCGIFCONF:
- (void) dev_ifconf((char *) arg);
- return 0;
-
- case SIOCGIFFLAGS:
- case SIOCGIFADDR:
- case SIOCGIFDSTADDR:
- case SIOCGIFBRDADDR:
- case SIOCGIFNETMASK:
- case SIOCGIFMETRIC:
- case SIOCGIFMTU:
- case SIOCGIFMEM:
- case SIOCGIFHWADDR:
- return dev_ifsioc(arg, cmd);
-
- case SIOCSIFFLAGS:
- case SIOCSIFADDR:
- case SIOCSIFDSTADDR:
- case SIOCSIFBRDADDR:
- case SIOCSIFNETMASK:
- case SIOCSIFMETRIC:
- case SIOCSIFMTU:
- case SIOCSIFMEM:
- if (!suser())
- return -EPERM;
- return dev_ifsioc(arg, cmd);
-
- case SIOCSIFLINK:
- if (!suser())
- return -EPERM;
- memcpy_fromfs(&iflink, arg, sizeof(iflink));
- dev = ddi_map(iflink.id);
- if (dev == NULL)
- return -EINVAL;
+ switch(cmd)
+ {
+ /*
+ * The old old setup ioctl. Even its name and this entry will soon be
+ * just so much ionization on a backup tape.
+ */
- /* Now allocate an interface and connect it. */
- printk("AF_INET: DDI \"%s\" linked to stream \"%s\"\n",
- dev->name, iflink.stream);
- return 0;
+ case SIOCGIFCONF:
+ (void) dev_ifconf((char *) arg);
+ return 0;
- default:
- return -EINVAL;
- }
+ /*
+ * Ioctl calls that can be done by all.
+ */
+
+ case SIOCGIFFLAGS:
+ case SIOCGIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFMEM:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case OLD_SIOCGIFHWADDR:
+ return dev_ifsioc(arg, cmd);
+
+ /*
+ * Ioctl calls requiring the power of a superuser
+ */
+
+ case SIOCSIFFLAGS:
+ case SIOCSIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCSIFNETMASK:
+ case SIOCSIFMETRIC:
+ case SIOCSIFMTU:
+ case SIOCSIFMEM:
+ if (!suser())
+ return -EPERM;
+ return dev_ifsioc(arg, cmd);
+
+ case SIOCSIFLINK:
+ return -EINVAL;
+
+ /*
+ * Unknown ioctl.
+ */
+
+ default:
+ return -EINVAL;
+ }
}
-/* Initialize the DEV module. */
-void
-dev_init(void)
+/*
+ * Initialize the DEV module. At boot time this walks the device list and
+ * unhooks any devices that fail to initialise (normally hardware not
+ * present) and leaves us with a valid list of present and active devices.
+ *
+ * The PCMICA code may need to change this a little, and add a pair
+ * of register_inet_device() unregister_inet_device() calls. This will be
+ * needed for ethernet as modules support.
+ */
+
+void dev_init(void)
{
- struct device *dev, *dev2;
+ struct device *dev, *dev2;
- /* Add the devices.
- * If the call to dev->init fails, the dev is removed
- * from the chain disconnecting the device until the
- * next reboot.
- */
- dev2 = NULL;
- for (dev = dev_base; dev != NULL; dev=dev->next) {
- if (dev->init && dev->init(dev)) {
- if (dev2 == NULL) dev_base = dev->next;
- else dev2->next = dev->next;
- } else {
- dev2 = dev;
+ /*
+ * Add the devices.
+ * If the call to dev->init fails, the dev is removed
+ * from the chain disconnecting the device until the
+ * next reboot.
+ */
+
+ dev2 = NULL;
+ for (dev = dev_base; dev != NULL; dev=dev->next)
+ {
+ if (dev->init && dev->init(dev))
+ {
+ /*
+ * It failed to come up. Unhook it.
+ */
+
+ if (dev2 == NULL)
+ dev_base = dev->next;
+ else
+ dev2->next = dev->next;
+ }
+ else
+ {
+ dev2 = dev;
+ }
}
- }
-
- /* Set up some IP addresses. */
- ip_bcast = in_aton("255.255.255.255");
}
--- /dev/null
+/*
+ * NET3 IP device support routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from the IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+
+/*
+ * Determine a default network mask, based on the IP address.
+ */
+
+unsigned long ip_get_mask(unsigned long addr)
+{
+ unsigned long dst;
+
+ if (addr == 0L)
+ return(0L); /* special case */
+
+ dst = ntohl(addr);
+ if (IN_CLASSA(dst))
+ return(htonl(IN_CLASSA_NET));
+ if (IN_CLASSB(dst))
+ return(htonl(IN_CLASSB_NET));
+ if (IN_CLASSC(dst))
+ return(htonl(IN_CLASSC_NET));
+
+ /*
+ * Something else, probably a multicast.
+ */
+
+ return(0);
+}
+
+/*
+ * Perform an IP address matching operation
+ */
+
+int ip_addr_match(unsigned long me, unsigned long him)
+{
+ int i;
+ unsigned long mask=0xFFFFFFFF;
+ DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
+ DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
+
+ /*
+ * Simple case
+ */
+ if (me == him)
+ return(1);
+
+ /*
+ * Look for a match ending in all 1's
+ */
+
+ for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8)
+ {
+ if ((me & 0xFF) != (him & 0xFF))
+ {
+ /*
+ * The only way this could be a match is for
+ * the rest of addr1 to be 0 or 255.
+ */
+ if (me != 0 && me != mask)
+ return(0);
+ return(1);
+ }
+ }
+ return(1);
+}
+
+
+/*
+ * Check the address for our address, broadcasts, etc.
+ *
+ * I intend to fix this to at the very least cache the last
+ * resolved entry.
+ */
+
+int ip_chk_addr(unsigned long addr)
+{
+ struct device *dev;
+ unsigned long mask;
+
+ /*
+ * Accept both `all ones' and `all zeros' as BROADCAST.
+ * (Support old BSD in other words). This old BSD
+ * support will go very soon as it messes other things
+ * up.
+ */
+
+ if (addr == INADDR_ANY || addr == INADDR_BROADCAST)
+ return IS_BROADCAST;
+
+ mask = ip_get_mask(addr);
+
+ /*
+ * Accept all of the `loopback' class A net.
+ */
+
+ if ((addr & mask) == htonl(0x7F000000L))
+ return IS_MYADDR;
+
+ /*
+ * OK, now check the interface addresses.
+ */
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (!(dev->flags & IFF_UP))
+ continue;
+ /*
+ * If the protocol address of the device is 0 this is special
+ * and means we are address hunting (eg bootp).
+ */
+
+ if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/)
+ return IS_MYADDR;
+ /*
+ * Is it the exact IP address?
+ */
+
+ if (addr == dev->pa_addr)
+ return IS_MYADDR;
+ /*
+ * Is it our broadcast address?
+ */
+
+ if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
+ return IS_BROADCAST;
+ /*
+ * Nope. Check for a subnetwork broadcast.
+ */
+
+ if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0)
+ {
+ if ((addr & ~dev->pa_mask) == 0)
+ return IS_BROADCAST;
+ if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
+ return IS_BROADCAST;
+ }
+
+ /*
+ * Nope. Check for Network broadcast.
+ */
+
+ if (((addr ^ dev->pa_addr) & mask) == 0)
+ {
+ if ((addr & ~mask) == 0)
+ return IS_BROADCAST;
+ if ((addr & ~mask) == ~mask)
+ return IS_BROADCAST;
+ }
+ }
+ return 0; /* no match at all */
+}
+
+
+/*
+ * Retrieve our own address.
+ *
+ * Because the loopback address (127.0.0.1) is already recognized
+ * automatically, we can use the loopback interface's address as
+ * our "primary" interface. This is the addressed used by IP et
+ * al when it doesn't know which address to use (i.e. it does not
+ * yet know from or to which interface to go...).
+ */
+
+unsigned long ip_my_addr(void)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->flags & IFF_LOOPBACK)
+ return(dev->pa_addr);
+ }
+ return(0);
+}
+
+/*
+ * Find an interface that can handle addresses for a certain address.
+ *
+ * This needs optimising, since its relatively trivial to collapse
+ * the two loops into one.
+ */
+
+struct device * ip_dev_check(unsigned long addr)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev; dev = dev->next)
+ {
+ if (!(dev->flags & IFF_UP))
+ continue;
+ if (!(dev->flags & IFF_POINTOPOINT))
+ continue;
+ if (addr != dev->pa_dstaddr)
+ continue;
+ return dev;
+ }
+ for (dev = dev_base; dev; dev = dev->next)
+ {
+ if (!(dev->flags & IFF_UP))
+ continue;
+ if (dev->flags & IFF_POINTOPOINT)
+ continue;
+ if (dev->pa_mask & (addr ^ dev->pa_addr))
+ continue;
+ return dev;
+ }
+ return NULL;
+}
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
*
* Fixes:
* Mr Linux : Arp problems
* Alan Cox : eth_rebuild_header missing an htons and
* minor other things.
* Tegge : Arp bug fixes.
+ * Florian : Removed many unnecessary functions, code cleanup
+ * and changes for new arp and skbuff.
+ * Alan Cox : Redid header building to reflect new format.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include <linux/errno.h>
#include "arp.h"
-
-/* Display an Ethernet address in readable format. */
-char *eth_print(unsigned char *ptr)
-{
- static char buff[64];
-
- if (ptr == NULL) return("[NONE]");
- sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
- (ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255),
- (ptr[3] & 255), (ptr[4] & 255), (ptr[5] & 255)
- );
- return(buff);
-}
-
void eth_setup(char *str, int *ints)
{
struct device *d = dev_base;
if (!str || !*str)
return;
- while (d) {
- if (!strcmp(str,d->name)) {
+ while (d)
+ {
+ if (!strcmp(str,d->name))
+ {
if (ints[0] > 0)
d->irq=ints[1];
if (ints[0] > 1)
}
}
-/* Display the contents of the Ethernet MAC header. */
-void
-eth_dump(struct ethhdr *eth)
-{
- if (inet_debug != DBG_ETH) return;
-
- printk("eth: SRC = %s ", eth_print(eth->h_source));
- printk("DST = %s ", eth_print(eth->h_dest));
- printk("TYPE = %04X\n", ntohs(eth->h_proto));
-}
-
-
-/* Create the Ethernet MAC header. */
-int
-eth_header(unsigned char *buff, struct device *dev, unsigned short type,
- unsigned long daddr, unsigned long saddr, unsigned len)
-{
- struct ethhdr *eth;
-
- DPRINTF((DBG_DEV, "ETH: header(%s, ", in_ntoa(saddr)));
- DPRINTF((DBG_DEV, "%s, 0x%X)\n", in_ntoa(daddr), type));
-
- /* Fill in the basic Ethernet MAC header. */
- eth = (struct ethhdr *) buff;
- eth->h_proto = htons(type);
-
- /* We don't ARP for the LOOPBACK device... */
- if (dev->flags & IFF_LOOPBACK) {
- DPRINTF((DBG_DEV, "ETH: No header for loopback\n"));
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- memset(eth->h_dest, 0, dev->addr_len);
- return(dev->hard_header_len);
- }
-
- /* Check if we can use the MAC BROADCAST address. */
- if (chk_addr(daddr) == IS_BROADCAST) {
- DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- memcpy(eth->h_dest, dev->broadcast, dev->addr_len);
- return(dev->hard_header_len);
- }
- cli();
- memcpy(eth->h_source, &saddr, 4);
- /* No. Ask ARP to resolve the Ethernet address. */
- if (arp_find(eth->h_dest, daddr, dev, dev->pa_addr))
- {
- sti();
- if(type!=ETH_P_IP)
- printk("Erk: protocol %X got into an arp request state!\n",type);
- return(-dev->hard_header_len);
- }
- else
- {
- memcpy(eth->h_source,dev->dev_addr,dev->addr_len); /* This was missing causing chaos if the
- header built correctly! */
- sti();
- return(dev->hard_header_len);
- }
-}
+/*
+ * Create the Ethernet MAC header for an arbitary protocol layer
+ *
+ * saddr=NULL means use device source address
+ * daddr=NULL means leave destination address (eg unresolved arp)
+ */
-/* Rebuild the Ethernet MAC header. */
-int
-eth_rebuild_header(void *buff, struct device *dev)
+int eth_header(unsigned char *buff, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len,
+ struct sk_buff *skb)
{
- struct ethhdr *eth;
- unsigned long src, dst;
-
- DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
- eth = (struct ethhdr *) buff;
- src = *(unsigned long *) eth->h_source;
- dst = *(unsigned long *) eth->h_dest;
- DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src)));
- DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst)));
- if(eth->h_proto!=htons(ETH_P_ARP)) /* This ntohs kind of helps a bit! */
- if (arp_find(eth->h_dest, dst, dev, dev->pa_addr /* src */)) return(1);
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- return(0);
+ struct ethhdr *eth = (struct ethhdr *)buff;
+
+ /*
+ * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+ * in here instead. It is up to the 802.2 layer to carry protocol information.
+ */
+
+ if(type!=ETH_P_802_3)
+ eth->h_proto = htons(type);
+ else
+ eth->h_proto = htons(len);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if(saddr)
+ memcpy(eth->h_source,saddr,dev->addr_len);
+ else
+ memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+ /*
+ * Anyway, the loopback-device should never use this function...
+ */
+
+ if (dev->flags & IFF_LOOPBACK)
+ {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ if(daddr)
+ {
+ memcpy(eth->h_dest,daddr,dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ return -dev->hard_header_len;
}
-/* Add an ARP entry for a host on this interface. */
-void
-eth_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+/*
+ * Rebuild the Ethernet MAC header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ */
+
+int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
{
- struct ethhdr *eth;
+ struct ethhdr *eth = (struct ethhdr *)buff;
+
+ /*
+ * Only ARP/IP is currently supported
+ */
+
+ if(eth->h_proto != htons(ETH_P_IP))
+ {
+ printk("eth_rebuild_header: Don't know how to resolve type %d addreses?\n",(int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
+ }
- eth = (struct ethhdr *) skb->data;
- arp_add(addr, eth->h_source, dev);
+ /*
+ * Try and get ARP to resolve the header.
+ */
+
+ return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
}
-/* Determine the packet's protocol ID. */
-unsigned short
-eth_type_trans(struct sk_buff *skb, struct device *dev)
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
{
- struct ethhdr *eth;
+ struct ethhdr *eth = (struct ethhdr *) skb->data;
- eth = (struct ethhdr *) skb->data;
+ if (ntohs(eth->h_proto) < 1536)
+ return htons(ETH_P_802_3);
- if(ntohs(eth->h_proto)<1536)
- return(htons(ETH_P_802_3));
- return(eth->h_proto);
+ return eth->h_proto;
}
+
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
*
* Fixes:
* Alan Cox : Generic queue usage.
* Gerhard Koerting: ICMP addressing corrected
* Alan Cox : Use tos/ttl settings
+ * Alan Cox : Protocol violations
+ * Alan Cox : SNMP Statistics
+ * Alan Cox : Routing errors
+ *
+ *
+ * FIXME:
+ * When 1.0.6 is out merge in the NET channel diffs for TIMESTAMP
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
+#include <linux/string.h>
* 2 of the License, or (at your option) any later version.
*/
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include <linux/string.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "icmp.h"
#include "tcp.h"
-#include "skbuff.h"
+#include "snmp.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include <linux/errno.h>
#include <linux/timer.h>
#define min(a,b) ((a)<(b)?(a):(b))
+/*
+ * Statistics
+ */
+
+struct icmp_mib icmp_statistics={0,};
+
+
/* An array of errno for error messages from dest unreach. */
struct icmp_err icmp_err_convert[] = {
{ ENETUNREACH, 1 }, /* ICMP_NET_UNREACH */
};
-/* Display the contents of an ICMP header. */
-static void
-print_icmp(struct icmphdr *icmph)
+/*
+ * Display the contents of an ICMP header.
+ */
+
+static void print_icmp(struct icmphdr *icmph)
{
- if (inet_debug != DBG_ICMP) return;
+ if (inet_debug != DBG_ICMP)
+ return;
- printk("ICMP: type = %d, code = %d, checksum = %X\n",
+ printk("ICMP: type = %d, code = %d, checksum = %X\n",
icmph->type, icmph->code, icmph->checksum);
- printk(" gateway = %s\n", in_ntoa(icmph->un.gateway));
+ printk(" gateway = %s\n", in_ntoa(icmph->un.gateway));
}
-/* Send an ICMP message. */
-void
-icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * Fixme: Fragment handling is wrong really.
+ */
+
+void icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
{
- struct sk_buff *skb;
- struct iphdr *iph;
- int offset;
- struct icmphdr *icmph;
- int len;
-
- DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ int offset;
+ struct icmphdr *icmph;
+ int len;
+ struct device *ndev=NULL; /* Make this =dev to force replies on the same interface */
+
+ DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
skb_in, type, code, dev));
- /* Get some memory for the reply. */
- len = sizeof(struct sk_buff) + dev->hard_header_len +
- sizeof(struct iphdr) + sizeof(struct icmphdr) +
- sizeof(struct iphdr) + 8; /* amount of header to return */
+ /*
+ * Find the original IP header.
+ */
+
+ iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);
+
+ /*
+ * We must NEVER NEVER send an ICMP error to an ICMP error message
+ */
+
+ if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)
+ {
+ if(iph->protocol==IPPROTO_ICMP)
+ return;
+
+ }
+ icmp_statistics.IcmpOutMsgs++;
+
+ /*
+ * This needs a tidy.
+ */
+
+ switch(type)
+ {
+ case ICMP_DEST_UNREACH:
+ icmp_statistics.IcmpOutDestUnreachs++;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ icmp_statistics.IcmpOutSrcQuenchs++;
+ break;
+ case ICMP_REDIRECT:
+ icmp_statistics.IcmpOutRedirects++;
+ break;
+ case ICMP_ECHO:
+ icmp_statistics.IcmpOutEchos++;
+ break;
+ case ICMP_ECHOREPLY:
+ icmp_statistics.IcmpOutEchoReps++;
+ break;
+ case ICMP_TIME_EXCEEDED:
+ icmp_statistics.IcmpOutTimeExcds++;
+ break;
+ case ICMP_PARAMETERPROB:
+ icmp_statistics.IcmpOutParmProbs++;
+ break;
+ case ICMP_TIMESTAMP:
+ icmp_statistics.IcmpOutTimestamps++;
+ break;
+ case ICMP_TIMESTAMPREPLY:
+ icmp_statistics.IcmpOutTimestampReps++;
+ break;
+ case ICMP_ADDRESS:
+ icmp_statistics.IcmpOutAddrMasks++;
+ break;
+ case ICMP_ADDRESSREPLY:
+ icmp_statistics.IcmpOutAddrMaskReps++;
+ break;
+ }
+ /*
+ * Get some memory for the reply.
+ */
+
+ len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) +
+ sizeof(struct iphdr) + 8; /* amount of header to return */
- skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- skb->sk = NULL;
- skb->mem_addr = skb;
- skb->mem_len = len;
- len -= sizeof(struct sk_buff);
-
- /* Find the IP header. */
- iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);
-
- /* Build Layer 2-3 headers for message back to source. */
- offset = ip_build_header(skb, dev->pa_addr, iph->saddr,
- &dev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255);
- if (offset < 0) {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- /* Re-adjust length according to actual IP header size. */
- skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
- icmph = (struct icmphdr *) (skb->data + offset);
- icmph->type = type;
- icmph->code = code;
- icmph->checksum = 0;
- icmph->un.gateway = 0;
- memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
-
- icmph->checksum = ip_compute_csum((unsigned char *)icmph,
- sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
+ skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
+ if (skb == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
- DPRINTF((DBG_ICMP, ">>\n"));
- print_icmp(icmph);
+ /*
+ * Build Layer 2-3 headers for message back to source.
+ */
- /* Send it and free it. */
- ip_queue_xmit(NULL, dev, skb, 1);
-}
+ offset = ip_build_header(skb, dev->pa_addr, iph->saddr,
+ &ndev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
-/* Handle ICMP_UNREACH and ICMP_QUENCH. */
-static void
-icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
-{
- struct inet_protocol *ipprot;
- struct iphdr *iph;
- unsigned char hash;
- int err;
-
- err = (icmph->type << 8) | icmph->code;
- iph = (struct iphdr *) (icmph + 1);
- switch(icmph->code & 7) {
- case ICMP_NET_UNREACH:
- DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
- in_ntoa(iph->daddr)));
- break;
- case ICMP_HOST_UNREACH:
- DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
- in_ntoa(iph->daddr)));
- break;
- case ICMP_PROT_UNREACH:
- printk("ICMP: %s:%d: protocol unreachable.\n",
- in_ntoa(iph->daddr), ntohs(iph->protocol));
- break;
- case ICMP_PORT_UNREACH:
- DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
- in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
- break;
- case ICMP_FRAG_NEEDED:
- printk("ICMP: %s: fragmentation needed and DF set.\n",
- in_ntoa(iph->daddr));
- break;
- case ICMP_SR_FAILED:
- printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
- break;
- default:
- DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
- (icmph->code & 7), in_ntoa(iph->daddr)));
- break;
- }
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
+
+ /*
+ * Fill in the frame
+ */
+
+ icmph = (struct icmphdr *) (skb->data + offset);
+ icmph->type = type;
+ icmph->code = code;
+ icmph->checksum = 0;
+ icmph->un.gateway = 0;
+ memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
+
+ icmph->checksum = ip_compute_csum((unsigned char *)icmph,
+ sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
- /* Get the protocol(s). */
- hash = iph->protocol & (MAX_INET_PROTOS -1);
+ DPRINTF((DBG_ICMP, ">>\n"));
+ print_icmp(icmph);
- /* This can change while we are doing it. */
- ipprot = (struct inet_protocol *) inet_protos[hash];
- while(ipprot != NULL) {
- struct inet_protocol *nextip;
+ /*
+ * Send it and free it once sent.
+ */
+ ip_queue_xmit(NULL, dev, skb, 1);
+}
- nextip = (struct inet_protocol *) ipprot->next;
- /* Pass it off to everyone who wants it. */
- if (iph->protocol == ipprot->protocol && ipprot->err_handler) {
- ipprot->err_handler(err, (unsigned char *)(icmph + 1),
- iph->daddr, iph->saddr, ipprot);
+/*
+ * Handle ICMP_UNREACH and ICMP_QUENCH.
+ */
+
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
+{
+ struct inet_protocol *ipprot;
+ struct iphdr *iph;
+ unsigned char hash;
+ int err;
+
+ err = (icmph->type << 8) | icmph->code;
+ iph = (struct iphdr *) (icmph + 1);
+
+ switch(icmph->code & 7)
+ {
+ case ICMP_NET_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
+ in_ntoa(iph->daddr)));
+ break;
+ case ICMP_HOST_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
+ in_ntoa(iph->daddr)));
+ break;
+ case ICMP_PROT_UNREACH:
+ printk("ICMP: %s:%d: protocol unreachable.\n",
+ in_ntoa(iph->daddr), ntohs(iph->protocol));
+ break;
+ case ICMP_PORT_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
+ in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
+ break;
+ case ICMP_FRAG_NEEDED:
+ printk("ICMP: %s: fragmentation needed and DF set.\n",
+ in_ntoa(iph->daddr));
+ break;
+ case ICMP_SR_FAILED:
+ printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+ break;
+ default:
+ DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
+ (icmph->code & 7), in_ntoa(iph->daddr)));
+ break;
}
- ipprot = nextip;
- }
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ /*
+ * Get the protocol(s).
+ */
+
+ hash = iph->protocol & (MAX_INET_PROTOS -1);
+
+ /*
+ * This can't change while we are doing it.
+ */
+
+ ipprot = (struct inet_protocol *) inet_protos[hash];
+ while(ipprot != NULL)
+ {
+ struct inet_protocol *nextip;
+
+ nextip = (struct inet_protocol *) ipprot->next;
+
+ /*
+ * Pass it off to everyone who wants it.
+ */
+ if (iph->protocol == ipprot->protocol && ipprot->err_handler)
+ {
+ ipprot->err_handler(err, (unsigned char *)(icmph + 1),
+ iph->daddr, iph->saddr, ipprot);
+ }
+
+ ipprot = nextip;
+ }
+ kfree_skb(skb, FREE_READ);
}
-/* Handle ICMP_REDIRECT. */
-static void
-icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
-{
- struct iphdr *iph;
- unsigned long ip;
+/*
+ * Handle ICMP_REDIRECT.
+ */
- iph = (struct iphdr *) (icmph + 1);
- ip = iph->daddr;
- switch(icmph->code & 7) {
- case ICMP_REDIR_NET:
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
+{
+ struct iphdr *iph;
+ unsigned long ip;
+
+ /*
+ * Get the copied header of the packet that caused the redirect
+ */
+
+ iph = (struct iphdr *) (icmph + 1);
+ ip = iph->daddr;
+
+ switch(icmph->code & 7)
+ {
+ case ICMP_REDIR_NET:
+ /*
+ * This causes a problem with subnetted networks. What we should do
+ * is use ICMP_ADDRESS to get the subnet mask of the problem route
+ * and set both. But we don't..
+ */
#ifdef not_a_good_idea
- rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
- ip, 0, icmph->un.gateway, dev);
- break;
+ ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
+ ip, 0, icmph->un.gateway, dev);
+ break;
#endif
- case ICMP_REDIR_HOST:
- rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
- ip, 0, icmph->un.gateway, dev);
- break;
- case ICMP_REDIR_NETTOS:
- case ICMP_REDIR_HOSTTOS:
- printk("ICMP: cannot handle TOS redirects yet!\n");
- break;
- default:
- DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
+ case ICMP_REDIR_HOST:
+ /*
+ * Add better route to host
+ */
+ ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
+ ip, 0, icmph->un.gateway, dev);
+ break;
+ case ICMP_REDIR_NETTOS:
+ case ICMP_REDIR_HOSTTOS:
+ printk("ICMP: cannot handle TOS redirects yet!\n");
+ break;
+ default:
+ DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
(icmph->code & 7)));
break;
- }
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ }
+
+ /*
+ * Discard the original packet
+ */
+
+ kfree_skb(skb, FREE_READ);
}
-/* Handle ICMP_ECHO ("ping") requests. */
-static void
-icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ * Handle ICMP_ECHO ("ping") requests.
+ */
+
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
unsigned long saddr, unsigned long daddr, int len,
struct options *opt)
{
- struct icmphdr *icmphr;
- struct sk_buff *skb2;
- int size, offset;
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ struct device *ndev=NULL;
+ int size, offset;
+
+ icmp_statistics.IcmpOutEchoReps++;
+ icmp_statistics.IcmpOutMsgs++;
+
+ size = dev->hard_header_len + 64 + len;
+ skb2 = alloc_skb(size, GFP_ATOMIC);
+
+ if (skb2 == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->free = 1;
+
+ /* Build Layer 2-3 headers for message back to source */
+ offset = ip_build_header(skb2, daddr, saddr, &ndev,
+ IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
+ kfree_skb(skb2,FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
- size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
- skb2 = alloc_skb(size, GFP_ATOMIC);
- if (skb2 == NULL) {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
- skb2->sk = NULL;
- skb2->mem_addr = skb2;
- skb2->mem_len = size;
- skb2->free = 1;
-
- /* Build Layer 2-3 headers for message back to source */
- offset = ip_build_header(skb2, daddr, saddr, &dev,
- IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
- if (offset < 0) {
- printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
- kfree_skb(skb2,FREE_WRITE);
- skb->sk = NULL;
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb2->len = offset + len;
+
+ /*
+ * Build ICMP_ECHO Response message.
+ */
+ icmphr = (struct icmphdr *) (skb2->data + offset);
+ memcpy((char *) icmphr, (char *) icmph, len);
+ icmphr->type = ICMP_ECHOREPLY;
+ icmphr->code = 0;
+ icmphr->checksum = 0;
+ icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+
+ /*
+ * Ship it out - free it when done
+ */
+ ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+
+ /*
+ * Free the received frame
+ */
+
kfree_skb(skb, FREE_READ);
- return;
- }
-
- /* Re-adjust length according to actual IP header size. */
- skb2->len = offset + len;
-
- /* Build ICMP_ECHO Response message. */
- icmphr = (struct icmphdr *) (skb2->data + offset);
- memcpy((char *) icmphr, (char *) icmph, len);
- icmphr->type = ICMP_ECHOREPLY;
- icmphr->code = 0;
- icmphr->checksum = 0;
- icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
-
- /* Ship it out - free it when done */
- ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
-
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
}
-
-/* Handle ICMP Timestamp requests. */
-static void
-icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ * Handle ICMP Timestamp requests.
+ */
+
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
unsigned long saddr, unsigned long daddr, int len,
struct options *opt)
{
- struct icmphdr *icmphr;
- struct sk_buff *skb2;
- int size, offset;
- unsigned long *timeptr, midtime;
- extern struct timeval xtime; /* kernel/time.c */
-
- size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
- if (! (skb2 = alloc_skb(size, GFP_ATOMIC))) {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
- skb2->sk = NULL;
- skb2->mem_addr = skb2;
- skb2->mem_len = size;
- skb2->free = 1;
-
- /* Build Layer 2-3 headers for message back to source */
- offset = ip_build_header(skb2, daddr, saddr, &dev, IPPROTO_ICMP, opt, len,
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+ unsigned long *timeptr, midtime;
+ extern struct timeval xtime; /* kernel/time.c */
+ struct device *ndev=NULL;
+
+ size = dev->hard_header_len + 64 + len;
+
+ if (! (skb2 = alloc_skb(size, GFP_ATOMIC)))
+ {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
+ skb2->free = 1;
+
+/*
+ * Build Layer 2-3 headers for message back to source
+ */
+
+ offset = ip_build_header(skb2, daddr, saddr, &ndev, IPPROTO_ICMP, opt, len,
skb->ip_hdr->tos, 255);
- if (offset < 0) {
- printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n");
- kfree_skb(skb2, FREE_WRITE);
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- /* Re-adjust length according to actual IP header size. */
- skb2->len = offset + len;
-
- /* Build ICMP_TIMESTAMP Response message. */
- icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
- memcpy((char *) icmphr, (char *) icmph, len);
- icmphr->type = ICMP_TIMESTAMPREPLY;
- icmphr->code = icmphr->checksum = 0;
-
- /* fill in the current time as ms since midnight UT: */
- midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
- timeptr = (unsigned long *) (icmphr + 1);
- /* the originate timestamp (timeptr [0]) is still in the copy: */
- timeptr [1] = timeptr [2] = htonl(midtime);
-
- icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, len);
-
- /* Ship it out - free it when done */
- ip_queue_xmit((struct sock *) NULL, dev, skb2, 1);
-
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ if (offset < 0)
+ {
+ printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n");
+ kfree_skb(skb2, FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ icmp_statistics.IcmpOutErrors++;
+ return;
+ }
+
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+ skb2->len = offset + len;
+
+ /*
+ * Build ICMP_TIMESTAMP Response message.
+ */
+
+ icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
+ memcpy((char *) icmphr, (char *) icmph, len);
+ icmphr->type = ICMP_TIMESTAMPREPLY;
+ icmphr->code = icmphr->checksum = 0;
+
+ /* fill in the current time as ms since midnight UT: */
+ midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
+ timeptr = (unsigned long *) (icmphr + 1);
+ /*
+ * the originate timestamp (timeptr [0]) is still in the copy:
+ */
+ timeptr [1] = timeptr [2] = htonl(midtime);
+
+ icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, len);
+
+ /*
+ * Ship it out - free it when done
+ */
+
+ ip_queue_xmit((struct sock *) NULL, dev, skb2, 1);
+ icmp_statistics.IcmpOutTimestampReps++;
+ kfree_skb(skb, FREE_READ);
}
+
+
-/* Handle the ICMP INFORMATION REQUEST. */
-static void
-icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ * Handle the ICMP INFORMATION REQUEST.
+ */
+
+static void icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
unsigned long saddr, unsigned long daddr, int len,
struct options *opt)
{
- /* NOT YET */
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ /* Obsolete */
+ kfree_skb(skb, FREE_READ);
}
-/* Handle ICMP_ADRESS_MASK requests. */
-static void
-icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ * Handle ICMP_ADRESS_MASK requests.
+ */
+
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
unsigned long saddr, unsigned long daddr, int len,
struct options *opt)
{
- struct icmphdr *icmphr;
- struct sk_buff *skb2;
- int size, offset;
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+ struct device *ndev=NULL;
+
+ icmp_statistics.IcmpOutMsgs++;
+ icmp_statistics.IcmpOutAddrMaskReps++;
+
+ size = dev->hard_header_len + 64 + len;
+ skb2 = alloc_skb(size, GFP_ATOMIC);
+ if (skb2 == NULL)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->free = 1;
+
+ /*
+ * Build Layer 2-3 headers for message back to source
+ */
+
+ offset = ip_build_header(skb2, daddr, saddr, &ndev,
+ IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+ if (offset < 0)
+ {
+ icmp_statistics.IcmpOutErrors++;
+ printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
+ kfree_skb(skb2,FREE_WRITE);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
- size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
- skb2 = alloc_skb(size, GFP_ATOMIC);
- if (skb2 == NULL) {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
- skb2->sk = NULL;
- skb2->mem_addr = skb2;
- skb2->mem_len = size;
- skb2->free = 1;
-
- /* Build Layer 2-3 headers for message back to source */
- offset = ip_build_header(skb2, daddr, saddr, &dev,
- IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
- if (offset < 0) {
- printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
- kfree_skb(skb2,FREE_WRITE);
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- return;
- }
+ /*
+ * Re-adjust length according to actual IP header size.
+ */
+
+ skb2->len = offset + len;
- /* Re-adjust length according to actual IP header size. */
- skb2->len = offset + len;
+ /*
+ * Build ICMP ADDRESS MASK Response message.
+ */
- /* Build ICMP ADDRESS MASK Response message. */
- icmphr = (struct icmphdr *) (skb2->data + offset);
- icmphr->type = ICMP_ADDRESSREPLY;
- icmphr->code = 0;
- icmphr->checksum = 0;
- icmphr->un.echo.id = icmph->un.echo.id;
- icmphr->un.echo.sequence = icmph->un.echo.sequence;
- memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
+ icmphr = (struct icmphdr *) (skb2->data + offset);
+ icmphr->type = ICMP_ADDRESSREPLY;
+ icmphr->code = 0;
+ icmphr->checksum = 0;
+ icmphr->un.echo.id = icmph->un.echo.id;
+ icmphr->un.echo.sequence = icmph->un.echo.sequence;
+ memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
- icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+ icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
- /* Ship it out - free it when done */
- ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+ /* Ship it out - free it when done */
+ ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
}
-/* Deal with incoming ICMP packets. */
-int
-icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
+/*
+ * Deal with incoming ICMP packets.
+ */
+
+int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
unsigned long daddr, unsigned short len,
unsigned long saddr, int redo, struct inet_protocol *protocol)
{
- struct icmphdr *icmph;
- unsigned char *buff;
-
- /* Drop broadcast packets. */
- if (chk_addr(daddr) == IS_BROADCAST) {
- DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
+ struct icmphdr *icmph;
+ unsigned char *buff;
+
+ /*
+ * Drop broadcast packets. IP has done a broadcast check and ought one day
+ * to pass on that information.
+ */
+
+ icmp_statistics.IcmpInMsgs++;
+
+ if (ip_chk_addr(daddr) == IS_BROADCAST)
+ {
+ DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
in_ntoa(saddr)));
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(0);
- }
-
- buff = skb1->h.raw;
- icmph = (struct icmphdr *) buff;
-
- /* Validate the packet first */
- if (ip_compute_csum((unsigned char *) icmph, len)) {
- /* Failed checksum! */
- printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(0);
- }
- print_icmp(icmph);
-
- /* Parse the ICMP message */
- switch(icmph->type) {
- case ICMP_TIME_EXCEEDED:
- case ICMP_DEST_UNREACH:
- case ICMP_SOURCE_QUENCH:
- icmp_unreach(icmph, skb1);
- return(0);
- case ICMP_REDIRECT:
- icmp_redirect(icmph, skb1, dev);
- return(0);
- case ICMP_ECHO:
- icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
- return 0;
- case ICMP_ECHOREPLY:
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(0);
- case ICMP_TIMESTAMP:
- icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);
- return 0;
- case ICMP_TIMESTAMPREPLY:
- skb1->sk = NULL;
+ icmp_statistics.IcmpInErrors++;
kfree_skb(skb1, FREE_READ);
return(0);
- case ICMP_INFO_REQUEST:
- icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
- return 0;
- case ICMP_INFO_REPLY:
- skb1->sk = NULL;
+ }
+
+ /*
+ * Grab the packet as an icmp object
+ */
+
+ buff = skb1->h.raw;
+ icmph = (struct icmphdr *) buff;
+
+ /*
+ * Validate the packet first
+ */
+
+ if (ip_compute_csum((unsigned char *) icmph, len))
+ {
+ /* Failed checksum! */
+ icmp_statistics.IcmpInErrors++;
+ printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
kfree_skb(skb1, FREE_READ);
return(0);
- case ICMP_ADDRESS:
- icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
- return 0;
- case ICMP_ADDRESSREPLY:
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(0);
- default:
- DPRINTF((DBG_ICMP,
- "ICMP: Unsupported ICMP from %s, type = 0x%X\n",
- in_ntoa(saddr), icmph->type));
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(0);
- }
+ }
+ print_icmp(icmph);
+
+ /*
+ * Parse the ICMP message
+ */
+
+ switch(icmph->type)
+ {
+ case ICMP_TIME_EXCEEDED:
+ icmp_statistics.IcmpInTimeExcds++;
+ icmp_unreach(icmph, skb1);
+ return 0;
+ case ICMP_DEST_UNREACH:
+ icmp_statistics.IcmpInDestUnreachs++;
+ icmp_unreach(icmph, skb1);
+ return 0;
+ case ICMP_SOURCE_QUENCH:
+ icmp_statistics.IcmpInSrcQuenchs++;
+ icmp_unreach(icmph, skb1);
+ return(0);
+ case ICMP_REDIRECT:
+ icmp_statistics.IcmpInRedirects++;
+ icmp_redirect(icmph, skb1, dev);
+ return(0);
+ case ICMP_ECHO:
+ icmp_statistics.IcmpInEchos++;
+ icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ECHOREPLY:
+ icmp_statistics.IcmpInEchoReps++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_TIMESTAMP:
+ icmp_statistics.IcmpInTimestamps++;
+ icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_TIMESTAMPREPLY:
+ icmp_statistics.IcmpInTimestampReps++;
+ kfree_skb(skb1,FREE_READ);
+ return 0;
+ /* INFO is obsolete and doesn't even feature in the SNMP stats */
+ case ICMP_INFO_REQUEST:
+ icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_INFO_REPLY:
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_ADDRESS:
+ icmp_statistics.IcmpInAddrMasks++;
+ icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ADDRESSREPLY:
+ /*
+ * We ought to set our netmask on receiving this, but
+ * experience shows its a waste of effort.
+ */
+ icmp_statistics.IcmpInAddrMaskReps++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ default:
+ icmp_statistics.IcmpInErrors++;
+ DPRINTF((DBG_ICMP,
+ "ICMP: Unsupported ICMP from %s, type = 0x%X\n",
+ in_ntoa(saddr), icmph->type));
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
/*NOTREACHED*/
- skb1->sk = NULL;
- kfree_skb(skb1, FREE_READ);
- return(-1);
+ kfree_skb(skb1, FREE_READ);
+ return(-1);
}
-/* Perform any ICMP-related I/O control requests. */
-int
-icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+/*
+ * Perform any ICMP-related I/O control requests.
+ * [to vanish soon]
+ */
+
+int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- switch(cmd) {
- case DDIOCSDBG:
- return(dbg_ioctl((void *) arg, DBG_ICMP));
- default:
- return(-EINVAL);
- }
- return(0);
+ switch(cmd)
+ {
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_ICMP));
+ default:
+ return(-EINVAL);
+ }
+ return(0);
}
extern struct icmp_err icmp_err_convert[];
+extern struct icmp_mib icmp_statistics;
+
extern void icmp_send(struct sk_buff *skb_in, int type, int code,
struct device *dev);
* Alan Cox : handling.
* Gerhard Koerting: Forwarding uses IP priority hints
* Teemu Rantanen : Fragment problems.
+ * Alan Cox : General cleanup, comments and reformat
+ * Alan Cox : SNMP statistics
+ * Alan Cox : BSD address rule semantics. Also see
+ * UDP as there is a nasty checksum issue
+ * if you do things the wrong way.
*
* To Fix:
* IP option processing is mostly not needed. ip_forward needs to know about routing rules
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "snmp.h"
#include "ip.h"
#include "protocol.h"
#include "route.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "arp.h"
#include "icmp.h"
#define min(a,b) ((a)<(b)?(a):(b))
-void
-ip_print(struct iphdr *ip)
+/*
+ * SNMP management statistics
+ */
+
+struct ip_mib ip_statistics={1,64,}; /* Forwarding=Yes, Default TTL=64 */
+
+/*
+ * Print an IP packet for debugging purposes.
+ *
+ * This function is exported for the IP
+ * upper layers to use also.
+ */
+
+void ip_print(const struct iphdr *ip)
{
- unsigned char buff[32];
- unsigned char *ptr;
- int addr, len, i;
-
- if (inet_debug != DBG_IP) return;
-
- /* Dump the IP header. */
- printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
- ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len));
- printk(" id=%X, ttl=%d, prot=%d, check=%X\n",
- ip->id, ip->ttl, ip->protocol, ip->check);
- printk(" frag_off=%d\n", ip->frag_off);
- printk(" soucre=%s ", in_ntoa(ip->saddr));
- printk("dest=%s\n", in_ntoa(ip->daddr));
- printk(" ----\n");
-
- /* Dump the data. */
- ptr = (unsigned char *)(ip + 1);
- addr = 0;
- len = ntohs(ip->tot_len) - (4 * ip->ihl);
- while (len > 0) {
- printk(" %04X: ", addr);
- for(i = 0; i < 16; i++) {
- if (len > 0) {
- printk("%02X ", (*ptr & 0xFF));
- buff[i] = *ptr++;
- if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
- } else {
- printk(" ");
- buff[i] = ' ';
- }
- addr++;
- len--;
- };
- buff[i] = '\0';
- printk(" \"%s\"\n", buff);
- }
- printk(" ----\n\n");
+ unsigned char buff[32];
+ unsigned char *ptr;
+ int addr;
+ int len;
+ int i;
+
+ /* Are we debugging IP frames */
+
+ if (inet_debug != DBG_IP)
+ return;
+
+ /* Dump the IP header. */
+ printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
+ ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len));
+ printk(" id=%X, ttl=%d, prot=%d, check=%X\n",
+ ip->id, ip->ttl, ip->protocol, ip->check);
+ printk(" frag_off=%d\n", ip->frag_off);
+ printk(" soucre=%s ", in_ntoa(ip->saddr));
+ printk("dest=%s\n", in_ntoa(ip->daddr));
+ printk(" ----\n");
+
+ /* Dump the data. */
+ ptr = (unsigned char *)(ip + 1);
+ addr = 0;
+ len = ntohs(ip->tot_len) - (4 * ip->ihl);
+
+ while (len > 0)
+ {
+ printk(" %04X: ", addr);
+ for(i = 0; i < 16; i++)
+ {
+ if (len > 0)
+ {
+ printk("%02X ", (*ptr & 0xFF));
+ buff[i] = *ptr++;
+ if (buff[i] < 32 || buff[i] > 126)
+ buff[i] = '.';
+ }
+ else
+ {
+ printk(" ");
+ buff[i] = ' ';
+ }
+ addr++;
+ len--;
+ };
+ buff[i] = '\0';
+ printk(" \"%s\"\n", buff);
+ }
+ printk(" ----\n\n");
}
+/*
+ * Handle the issuing of an ioctl() request
+ * for the ip device. This is scheduled to
+ * disappear
+ */
-int
-ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- switch(cmd) {
- case DDIOCSDBG:
- return(dbg_ioctl((void *) arg, DBG_IP));
- default:
- return(-EINVAL);
- }
+ switch(cmd)
+ {
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_IP));
+ default:
+ return(-EINVAL);
+ }
}
-/* these two routines will do routining. */
+/* these two routines will do routing. */
+
static void
strict_route(struct iphdr *iph, struct options *opt)
{
#endif
-/* Take an skb, and fill in the MAC header. */
-static int
-ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev,
- unsigned long saddr)
+/*
+ * Take an skb, and fill in the MAC header.
+ */
+
+static int ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, unsigned long saddr)
{
- unsigned char *ptr;
- int mac;
+ int mac = 0;
- ptr = skb->data;
- mac = 0;
- skb->arp = 1;
- if (dev->hard_header) {
- mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len);
- }
- if (mac < 0) {
- mac = -mac;
- skb->arp = 0;
- }
- skb->dev = dev;
- return(mac);
+ skb->dev = dev;
+ skb->arp = 1;
+ if (dev->hard_header)
+ {
+ /*
+ * Build a hardware header. Source address is our mac, destination unknown
+ * (rebuild header will sort this out)
+ */
+ mac = dev->hard_header(skb->data, dev, ETH_P_IP, NULL, NULL, len, skb);
+ if (mac < 0)
+ {
+ mac = -mac;
+ skb->arp = 0;
+ skb->raddr = daddr; /* next routing address */
+ }
+ }
+ return mac;
}
* protocol knows what it's doing, otherwise it uses the
* routing/ARP tables to select a device struct.
*/
-int
-ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
+int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
{
- static struct options optmem;
- struct iphdr *iph;
- struct rtable *rt;
- unsigned char *buff;
- unsigned long raddr;
- static int count = 0;
- int tmp;
+ static struct options optmem;
+ struct iphdr *iph;
+ struct rtable *rt;
+ unsigned char *buff;
+ unsigned long raddr;
+ static int count = 0;
+ int tmp;
+ unsigned long src;
- if (saddr == 0)
- saddr = my_addr();
-
- DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
- " type=%d, opt=%X, len = %d)\n",
- skb, saddr, daddr, *dev, type, opt, len));
+ /*
+ * If there is no 'from' address as yet, then make it our loopback
+ */
+
+ if (saddr == 0)
+ saddr = ip_my_addr();
+
+ DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
+ " type=%d, opt=%X, len = %d)\n",
+ skb, saddr, daddr, *dev, type, opt, len));
- buff = skb->data;
-
- /* See if we need to look up the device. */
- if (*dev == NULL) {
- rt = rt_route(daddr, &optmem);
- if (rt == NULL)
- return(-ENETUNREACH);
-
- *dev = rt->rt_dev;
- if (saddr == 0x0100007FL && daddr != 0x0100007FL)
- saddr = rt->rt_dev->pa_addr;
- raddr = rt->rt_gateway;
-
- DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
- opt = &optmem;
- } else {
- /* We still need the address of the first hop. */
- rt = rt_route(daddr, &optmem);
- raddr = (rt == NULL) ? 0 : rt->rt_gateway;
- }
- if (raddr == 0)
- raddr = daddr;
-
- /* Now build the MAC header. */
- tmp = ip_send(skb, raddr, len, *dev, saddr);
- buff += tmp;
- len -= tmp;
+ buff = skb->data;
- skb->dev = *dev;
- skb->saddr = saddr;
- if (skb->sk) skb->sk->saddr = saddr;
-
- /* Now build the IP header. */
+ /*
+ * See if we need to look up the device.
+ */
+
+ if (*dev == NULL)
+ {
+ rt = ip_rt_route(daddr, &optmem, &src);
+ if (rt == NULL)
+ {
+ ip_statistics.IpOutNoRoutes++;
+ return(-ENETUNREACH);
+ }
+
+ *dev = rt->rt_dev;
+ /*
+ * If the frame is from us and going off machine it MUST MUST MUST
+ * have the output device ip address and never the loopback
+ */
+ if (saddr == 0x0100007FL && daddr != 0x0100007FL)
+ saddr = src;/*rt->rt_dev->pa_addr;*/
+ raddr = rt->rt_gateway;
+
+ DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
+ opt = &optmem;
+ }
+ else
+ {
+ /*
+ * We still need the address of the first hop.
+ */
+ rt = ip_rt_route(daddr, &optmem, &src);
+ /*
+ * If the frame is from us and going off machine it MUST MUST MUST
+ * have the output device ip address and never the loopback
+ */
+ if (saddr == 0x0100007FL && daddr != 0x0100007FL)
+ saddr = src;/*rt->rt_dev->pa_addr;*/
+
+ raddr = (rt == NULL) ? 0 : rt->rt_gateway;
+ }
+
+ /*
+ * No gateway so aim at the real destination
+ */
+ if (raddr == 0)
+ raddr = daddr;
+
+ /*
+ * Now build the MAC header.
+ */
+
+ tmp = ip_send(skb, raddr, len, *dev, saddr);
+ buff += tmp;
+ len -= tmp;
- /* If we are using IPPROTO_RAW, then we don't need an IP header, since
- one is being supplied to us by the user */
+ /*
+ * Book keeping
+ */
- if(type == IPPROTO_RAW) return (tmp);
+ skb->dev = *dev;
+ skb->saddr = saddr;
+ if (skb->sk)
+ skb->sk->saddr = saddr;
- iph = (struct iphdr *)buff;
- iph->version = 4;
- iph->tos = tos;
- iph->frag_off = 0;
- iph->ttl = ttl;
- iph->daddr = daddr;
- iph->saddr = saddr;
- iph->protocol = type;
- iph->ihl = 5;
- iph->id = htons(count++);
+ /*
+ * Now build the IP header.
+ */
- /* Setup the IP options. */
+ /*
+ * If we are using IPPROTO_RAW, then we don't need an IP header, since
+ * one is being supplied to us by the user
+ */
+
+ if(type == IPPROTO_RAW)
+ return (tmp);
+
+ iph = (struct iphdr *)buff;
+ iph->version = 4;
+ iph->tos = tos;
+ iph->frag_off = 0;
+ iph->ttl = ttl;
+ iph->daddr = daddr;
+ iph->saddr = saddr;
+ iph->protocol = type;
+ iph->ihl = 5;
+ iph->id = htons(count++);
+
+ /* Setup the IP options. */
#ifdef Not_Yet_Avail
- build_options(iph, opt);
+ build_options(iph, opt);
#endif
- return(20 + tmp); /* IP header plus MAC header size */
+ return(20 + tmp); /* IP header plus MAC header size */
}
return(0);
}
-/* This is a version of ip_compute_csum() optimized for IP headers, which
- always checksum on 4 octet boundaries. */
-static inline unsigned short
-ip_fast_csum(unsigned char * buff, int wlen)
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers, which
+ * always checksum on 4 octet boundaries.
+ */
+
+static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
{
- unsigned long sum = 0;
+ unsigned long sum = 0;
- if (wlen) {
+ if (wlen)
+ {
unsigned long bogus;
__asm__("clc\n"
"1:\t"
"adcw $0, %w0"
: "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus)
: "0" (sum), "1" (buff), "2" (wlen));
- }
- return (~sum) & 0xffff;
+ }
+ return (~sum) & 0xffff;
}
/*
* This routine does all the checksum computations that don't
* require anything special (like copying or special headers).
*/
-unsigned short
-ip_compute_csum(unsigned char * buff, int len)
+
+unsigned short ip_compute_csum(unsigned char * buff, int len)
{
- unsigned long sum = 0;
+ unsigned long sum = 0;
- /* Do the first multiple of 4 bytes and convert to 16 bits. */
- if (len > 3) {
- __asm__("clc\n"
+ /* Do the first multiple of 4 bytes and convert to 16 bits. */
+ if (len > 3)
+ {
+ __asm__("clc\n"
"1:\t"
"lodsl\n\t"
"adcl %%eax, %%ebx\n\t"
: "=b" (sum) , "=S" (buff)
: "0" (sum), "c" (len >> 2) ,"1" (buff)
: "ax", "cx", "si", "bx" );
- }
- if (len & 2) {
- __asm__("lodsw\n\t"
+ }
+ if (len & 2)
+ {
+ __asm__("lodsw\n\t"
"addw %%ax, %%bx\n\t"
"adcw $0, %%bx"
: "=b" (sum), "=S" (buff)
: "0" (sum), "1" (buff)
: "bx", "ax", "si");
- }
- if (len & 1) {
- __asm__("lodsb\n\t"
+ }
+ if (len & 1)
+ {
+ __asm__("lodsb\n\t"
"movb $0, %%ah\n\t"
"addw %%ax, %%bx\n\t"
"adcw $0, %%bx"
: "=b" (sum), "=S" (buff)
: "0" (sum), "1" (buff)
: "bx", "ax", "si");
- }
- sum =~sum;
- return(sum & 0xffff);
+ }
+ sum =~sum;
+ return(sum & 0xffff);
}
-/* Check the header of an incoming IP datagram. This version is still used in slhc.c. */
-int
-ip_csum(struct iphdr *iph)
+/*
+ * Check the header of an incoming IP datagram. This version is still used in slhc.c.
+ */
+
+int ip_csum(struct iphdr *iph)
{
- return ip_fast_csum((unsigned char *)iph, iph->ihl);
+ return ip_fast_csum((unsigned char *)iph, iph->ihl);
}
-/* Generate a checksym for an outgoing IP datagram. */
-static void
-ip_send_check(struct iphdr *iph)
+/*
+ * Generate a checksym for an outgoing IP datagram.
+ */
+
+static void ip_send_check(struct iphdr *iph)
{
- iph->check = 0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
/************************ Fragment Handlers From NET2E not yet with tweaks to beat 4K **********************************/
+
+/*
+ * This fragment handler is a bit of a heap. On the other hand it works quite
+ * happily and handles things quite well.
+ */
+
static struct ipq *ipqueue = NULL; /* IP fragment queue */
- /* Create a new fragment entry. */
+
+/*
+ * Create a new fragment entry.
+ */
+
static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
{
struct ipfrag *fp;
/*
- * Find the correct entry in the "incomplete datagrams" queue for
- * this IP datagram, and return the queue entry address if found.
+ * Find the correct entry in the "incomplete datagrams" queue for
+ * this IP datagram, and return the queue entry address if found.
*/
+
static struct ipq *ip_find(struct iphdr *iph)
{
struct ipq *qp;
if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol)
{
- del_timer(&qp->timer); /* So it doesnt vanish on us. The timer will be reset anyway */
+ del_timer(&qp->timer); /* So it doesn't vanish on us. The timer will be reset anyway */
sti();
return(qp);
}
/*
- * Remove an entry from the "incomplete datagrams" queue, either
- * because we completed, reassembled and processed it, or because
- * it timed out.
+ * Remove an entry from the "incomplete datagrams" queue, either
+ * because we completed, reassembled and processed it, or because
+ * it timed out.
*/
static void ip_free(struct ipq *qp)
struct ipfrag *fp;
struct ipfrag *xp;
- /* Stop the timer for this entry. */
-/* printk("ip_free\n");*/
+ /*
+ * Stop the timer for this entry.
+ */
+
del_timer(&qp->timer);
/* Remove this entry from the "incomplete datagrams" queue. */
}
/* Release all fragment data. */
-/* printk("ip_free: kill frag data\n");*/
+
fp = qp->fragments;
while (fp != NULL)
{
fp = xp;
}
-/* printk("ip_free: cleanup\n");*/
-
/* Release the MAC header. */
kfree_s(qp->mac, qp->maclen);
}
- /* Oops- a fragment queue timed out. Kill it and send an ICMP reply. */
+/*
+ * Oops- a fragment queue timed out. Kill it and send an ICMP reply.
+ */
static void ip_expire(unsigned long arg)
{
qp = (struct ipq *)arg;
DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp));
- /* Send an ICMP "Fragment Reassembly Timeout" message. */
-#if 0
- icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED,
- ICMP_EXC_FRAGTIME, qp->iph);
-#endif
+ /*
+ * Send an ICMP "Fragment Reassembly Timeout" message.
+ */
+
+ ip_statistics.IpReasmTimeout++;
+ ip_statistics.IpReasmFails++;
+ /* This if is always true... shrug */
if(qp->fragments!=NULL)
icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
ICMP_EXC_FRAGTIME, qp->dev);
- /* Nuke the fragment queue. */
+ /*
+ * Nuke the fragment queue.
+ */
ip_free(qp);
}
/*
- * Add an entry to the 'ipq' queue for a newly received IP datagram.
- * We will (hopefully :-) receive all other fragments of this datagram
- * in time, so we just create a queue for this datagram, in which we
- * will insert the received fragments at their respective positions.
+ * Add an entry to the 'ipq' queue for a newly received IP datagram.
+ * We will (hopefully :-) receive all other fragments of this datagram
+ * in time, so we just create a queue for this datagram, in which we
+ * will insert the received fragments at their respective positions.
*/
static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
{
printk("IP: create: no memory left !\n");
return(NULL);
+ skb->dev = qp->dev;
}
memset(qp, 0, sizeof(struct ipq));
- /* Allocate memory for the MAC header. */
+ /*
+ * Allocate memory for the MAC header.
+ *
+ * FIXME: We have a maximum MAC address size limit and define
+ * elsewhere. We should use it here and avoid the 3 kmalloc() calls
+ */
+
maclen = ((unsigned long) iph) - ((unsigned long) skb->data);
qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
if (qp->mac == NULL)
return(NULL);
}
- /* Allocate memory for the IP header (plus 8 octects for ICMP). */
+ /*
+ * Allocate memory for the IP header (plus 8 octects for ICMP).
+ */
+
ihlen = (iph->ihl * sizeof(unsigned long));
qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
if (qp->iph == NULL)
qp->maclen = maclen;
qp->fragments = NULL;
qp->dev = dev;
-/* printk("Protocol = %d\n",qp->iph->protocol);*/
/* Start a timer for this entry. */
qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */
}
- /* See if a fragment queue is complete. */
+/*
+ * See if a fragment queue is complete.
+ */
+
static int ip_done(struct ipq *qp)
{
struct ipfrag *fp;
}
-/* Build a new IP datagram from all its fragments. */
+/*
+ * Build a new IP datagram from all its fragments.
+ *
+ * FIXME: We copy here because we lack an effective way of handling lists
+ * of bits on input. Until the new skb data handling is in I'm not going
+ * to touch this with a bargepole. This also causes a 4Kish limit on
+ * packet sizes.
+ */
+
static struct sk_buff *ip_glue(struct ipq *qp)
{
struct sk_buff *skb;
unsigned char *ptr;
int count, len;
- /* Allocate a new buffer for the datagram. */
- len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len;
+ /*
+ * Allocate a new buffer for the datagram.
+ */
+
+ len = qp->maclen + qp->ihlen + qp->len;
+
if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL)
{
+ ip_statistics.IpReasmFails++;
printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
ip_free(qp);
return(NULL);
skb->len = (len - qp->maclen);
skb->h.raw = skb->data;
skb->free = 1;
- skb->dev = qp->dev;
/* Copy the original MAC and IP headers into the new buffer. */
ptr = (unsigned char *) skb->h.raw;
memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
-/* printk("Copied %d bytes of mac header.\n",qp->maclen);*/
ptr += qp->maclen;
memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
-/* printk("Copied %d byte of ip header.\n",qp->ihlen);*/
ptr += qp->ihlen;
skb->h.raw += qp->maclen;
-/* printk("Protocol = %d\n",skb->h.iph->protocol);*/
count = 0;
/* Copy the data portions of all fragments into the new buffer. */
printk("Invalid fragment list: Fragment over size.\n");
ip_free(qp);
kfree_skb(skb,FREE_WRITE);
+ ip_statistics.IpReasmFails++;
return NULL;
}
-/* printk("Fragment %d size %d\n",fp->offset,fp->len);*/
memcpy((ptr + fp->offset), fp->ptr, fp->len);
count += fp->len;
fp = fp->next;
iph->frag_off = 0;
iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
skb->ip_hdr = iph;
+
+ ip_statistics.IpReasmOKs++;
return(skb);
}
-/* Process an incoming IP datagram fragment. */
+/*
+ * Process an incoming IP datagram fragment.
+ */
+
static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
{
struct ipfrag *prev, *next;
int flags, offset;
int i, ihl, end;
+ ip_statistics.IpReasmReqds++;
+
/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
qp = ip_find(iph);
offset = ntohs(iph->frag_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
- if (((flags & IP_MF) == 0) && (offset == 0)) {
+ if (((flags & IP_MF) == 0) && (offset == 0))
+ {
if (qp != NULL)
ip_free(qp); /* Huh? How could this exist?? */
return(skb);
}
+
offset <<= 3; /* offset is in 8-byte chunks */
/*
* as we still are receiving fragments. Otherwise, create a fresh
* queue entry.
*/
- if (qp != NULL) {
+
+ if (qp != NULL)
+ {
del_timer(&qp->timer);
qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */
qp->timer.data = (unsigned long) qp; /* pointer to queue */
qp->timer.function = ip_expire; /* expire function */
add_timer(&qp->timer);
- } else {
- if ((qp = ip_create(skb, iph, dev)) == NULL) {
+ }
+ else
+ {
+ /*
+ * If we failed to create it, then discard the frame
+ */
+ if ((qp = ip_create(skb, iph, dev)) == NULL)
+ {
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
+ ip_statistics.IpReasmFails++;
return NULL;
}
}
- /* Determine the position of this fragment. */
+ /*
+ * Determine the position of this fragment.
+ */
+
ihl = (iph->ihl * sizeof(unsigned long));
end = offset + ntohs(iph->tot_len) - ihl;
- /* Point into the IP datagram 'data' part. */
+ /*
+ * Point into the IP datagram 'data' part.
+ */
+
ptr = skb->data + dev->hard_header_len + ihl;
- /* Is this the final fragment? */
+ /*
+ * Is this the final fragment?
+ */
+
if ((flags & IP_MF) == 0)
qp->len = end;
/*
- * Find out which fragments are in front and at the back of us
- * in the chain of fragments so far. We must know where to put
- * this fragment, right?
+ * Find out which fragments are in front and at the back of us
+ * in the chain of fragments so far. We must know where to put
+ * this fragment, right?
*/
+
prev = NULL;
for(next = qp->fragments; next != NULL; next = next->next)
{
}
/*
- * We found where to put this one.
- * Check for overlap with preceeding fragment, and, if needed,
- * align things so that any overlaps are eliminated.
+ * We found where to put this one.
+ * Check for overlap with preceeding fragment, and, if needed,
+ * align things so that any overlaps are eliminated.
*/
if (prev != NULL && offset < prev->end)
{
next->offset += i; /* next fragment */
next->ptr += i;
- /* If we get a frag size of <= 0, remove it. */
+ /*
+ * If we get a frag size of <= 0, remove it and the packet
+ * that it goes with.
+ */
if (next->len <= 0)
{
DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n",
if (tfp->next != NULL)
next->next->prev = next->prev;
-
- kfree_skb(next->skb, FREE_READ);
+
+ kfree_skb(next->skb,FREE_READ);
kfree_s(next, sizeof(struct ipfrag));
}
DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i));
}
- /* Insert this fragment in the chain of fragments. */
+ /*
+ * Insert this fragment in the chain of fragments.
+ */
+
tfp = NULL;
tfp = ip_frag_create(offset, end, skb, ptr);
- if (!tfp) {
+
+ /*
+ * No memory to save the fragment - so throw the lot
+ */
+
+ if (!tfp)
+ {
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
return NULL;
next->prev = tfp;
/*
- * OK, so we inserted this new fragment into the chain.
- * Check if we now have a full IP datagram which we can
- * bump up to the IP layer...
+ * OK, so we inserted this new fragment into the chain.
+ * Check if we now have a full IP datagram which we can
+ * bump up to the IP layer...
*/
if (ip_done(qp))
/*
- * This IP datagram is too large to be sent in one piece. Break it up into
- * smaller pieces (each of size equal to the MAC header plus IP header plus
- * a block of the data of the original IP data part) that will yet fit in a
- * single device frame, and queue such a frame for sending by calling the
- * ip_queue_xmit(). Note that this is recursion, and bad things will happen
- * if this function causes a loop...
+ * This IP datagram is too large to be sent in one piece. Break it up into
+ * smaller pieces (each of size equal to the MAC header plus IP header plus
+ * a block of the data of the original IP data part) that will yet fit in a
+ * single device frame, and queue such a frame for sending by calling the
+ * ip_queue_xmit(). Note that this is recursion, and bad things will happen
+ * if this function causes a loop...
+ *
+ * Yes this is inefficient, feel free to submit a quicker one.
+ *
+ * **Protocol Violation**
+ * We copy all the options to each fragment. !FIXME!
*/
+
void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
{
struct iphdr *iph;
int left, mtu, hlen, len;
int offset;
- /* Point into the IP datagram header. */
+ /*
+ * Point into the IP datagram header.
+ */
+
raw = skb->data;
iph = (struct iphdr *) (raw + dev->hard_header_len);
skb->ip_hdr = iph;
- /* Setup starting values. */
+ /*
+ * Setup starting values.
+ */
+
hlen = (iph->ihl * sizeof(unsigned long));
- left = ntohs(iph->tot_len) - hlen;
- hlen += dev->hard_header_len;
- mtu = (dev->mtu - hlen);
- ptr = (raw + hlen);
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ hlen += dev->hard_header_len; /* Total header size */
+ mtu = (dev->mtu - hlen); /* Size of data space */
+ ptr = (raw + hlen); /* Where to start from */
DPRINTF((DBG_IP, "IP: Fragmentation Desired\n"));
DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s",
dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
-
- if (mtu < 8)
- return;
- /* Check for any "DF" flag. */
+
+ /*
+ * Check for any "DF" flag. [DF means do not fragment]
+ */
+
if (ntohs(iph->frag_off) & IP_DF)
{
DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n"));
dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
+ ip_statistics.IpFragFails++;
icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev);
return;
}
- /* Fragment the datagram. */
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ */
+
+ if(mtu<8)
+ {
+ /* It's wrong but its better than nothing */
+ icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev);
+ ip_statistics.IpFragFails++;
+ return;
+ }
+
+ /*
+ * Fragment the datagram.
+ */
+
+ /*
+ * The initial offset is 0 for a complete frame. When
+ * fragmenting fragments its wherever this one starts.
+ */
+
if (is_frag & 2)
- offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
+ offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
else
- offset = 0;
+ offset = 0;
+
+
+ /*
+ * Keep copying data until we run out.
+ */
+
while(left > 0)
{
len = left;
DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n",
len, len + hlen));
- /* Allocate buffer. */
- if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_ATOMIC)) == NULL)
+ /*
+ * Allocate buffer.
+ */
+
+ if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)
{
printk("IP: frag: no memory for new fragment!\n");
+ ip_statistics.IpFragFails++;
return;
}
+
+ /*
+ * Set up data on packet
+ */
+
skb2->arp = skb->arp;
skb2->free = skb->free;
skb2->len = len + hlen;
skb2->h.raw=(char *) skb2->data;
+ /*
+ * Charge the memory for the fragment to any owner
+ * it might posess
+ */
+
if (sk)
sk->wmem_alloc += skb2->mem_len;
- /* Copy the packet header into the new buffer. */
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
memcpy(skb2->h.raw, raw, hlen);
- /* Copy a block of the IP datagram. */
+ /*
+ * Copy a block of the IP datagram.
+ */
memcpy(skb2->h.raw + hlen, ptr, len);
left -= len;
skb2->h.raw+=dev->hard_header_len;
- /* Fill in the new header fields. */
+
+ /*
+ * Fill in the new header fields.
+ */
iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
iph->frag_off = htons((offset >> 3));
- /* Added AC : If we are fragmenting a fragment thats not the
- last fragment then keep MF on each bit */
+ /*
+ * Added AC : If we are fragmenting a fragment thats not the
+ * last fragment then keep MF on each bit
+ */
if (left > 0 || (is_frag & 1))
iph->frag_off |= htons(IP_MF);
ptr += len;
offset += len;
-/* printk("Queue frag\n");*/
- /* Put this fragment into the sending queue. */
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ ip_statistics.IpFragCreates++;
+
ip_queue_xmit(sk, dev, skb2, 1);
-/* printk("Queued\n");*/
}
- }
+ ip_statistics.IpFragOKs++;
+}
#ifdef CONFIG_IP_FORWARD
-/* Forward an IP datagram to its next destination. */
-static void
-ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
+/*
+ * Forward an IP datagram to its next destination.
+ */
+
+static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
{
- struct device *dev2;
- struct iphdr *iph;
- struct sk_buff *skb2;
- struct rtable *rt;
- unsigned char *ptr;
- unsigned long raddr;
+ struct device *dev2; /* Output device */
+ struct iphdr *iph; /* Our header */
+ struct sk_buff *skb2; /* Output packet */
+ struct rtable *rt; /* Route we use */
+ unsigned char *ptr; /* Data pointer */
+ unsigned long raddr; /* Router IP address */
- /*
- * Only forward packets that were fired at us when we are in promiscuous
- * mode. In standard mode we rely on the driver to filter for us.
- */
+ /*
+ * Only forward packets that were fired at us when we are in promiscuous
+ * mode. In standard mode we rely on the driver to filter for us.
+ */
- if(dev->flags&IFF_PROMISC)
- {
- if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
- return;
- }
-
- /*
- * According to the RFC, we must first decrease the TTL field. If
- * that reaches zero, we must reply an ICMP control message telling
- * that the packet's lifetime expired.
- */
- iph = skb->h.iph;
- iph->ttl--;
- if (iph->ttl <= 0) {
- DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
- DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
- DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
-
- /* Tell the sender its packet died... */
- icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
- return;
- }
+ if(dev->flags&IFF_PROMISC)
+ {
+ if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
+ return;
+ }
+
- /* Re-compute the IP header checksum. */
- ip_send_check(iph);
+
+ /*
+ * According to the RFC, we must first decrease the TTL field. If
+ * that reaches zero, we must reply an ICMP control message telling
+ * that the packet's lifetime expired.
+ *
+ * Exception:
+ * We may not generate an ICMP for an ICMP. icmp_send does the
+ * enforcement of this so we can forget it here. It is however
+ * sometimes VERY important.
+ */
+
+ iph = skb->h.iph;
+ iph->ttl--;
+ if (iph->ttl <= 0)
+ {
+ DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
+ DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+
+ /* Tell the sender its packet died... */
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
+ return;
+ }
- /*
- * OK, the packet is still valid. Fetch its destination address,
- * and give it to the IP sender for further processing.
- */
- rt = rt_route(iph->daddr, NULL);
- if (rt == NULL) {
- DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
+ /*
+ * Re-compute the IP header checksum.
+ * This is inefficient. We know what has happened to the header
+ * and could thus adjust the checksum as Phil Karn does in KA9Q
+ */
+
+ ip_send_check(iph);
- /* Tell the sender its packet cannot be delivered... */
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
- return;
- }
+ /*
+ * OK, the packet is still valid. Fetch its destination address,
+ * and give it to the IP sender for further processing.
+ */
+ rt = ip_rt_route(iph->daddr, NULL, NULL);
+ if (rt == NULL)
+ {
+ DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
- /*
- * Gosh. Not only is the packet valid; we even know how to
- * forward it onto its final destination. Can we say this
- * is being plain lucky?
- * If the router told us that there is no GW, use the dest.
- * IP address itself- we seem to be connected directly...
- */
- raddr = rt->rt_gateway;
- if (raddr != 0) {
- rt = rt_route(raddr, NULL);
- if (rt == NULL) {
- DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
-
- /* Tell the sender its packet cannot be delivered... */
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+ /*
+ * Tell the sender its packet cannot be delivered. Again
+ * ICMP is screened later.
+ */
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
return;
}
- if (rt->rt_gateway != 0) raddr = rt->rt_gateway;
- } else raddr = iph->daddr;
- dev2 = rt->rt_dev;
-
-
- if (dev == dev2)
- return;
- /*
- * We now allocate a new buffer, and copy the datagram into it.
- * If the indicated interface is up and running, kick it.
- */
- DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
- DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
- in_ntoa(raddr), dev2->name, skb->len));
- if (dev2->flags & IFF_UP) {
- skb2 = (struct sk_buff *) alloc_skb(sizeof(struct sk_buff) +
- dev2->hard_header_len + skb->len, GFP_ATOMIC);
- if (skb2 == NULL) {
- printk("\nIP: No memory available for IP forward\n");
- return;
- }
- ptr = skb2->data;
- skb2->sk = NULL;
- skb2->free = 1;
- skb2->len = skb->len + dev2->hard_header_len;
- skb2->mem_addr = skb2;
- skb2->mem_len = sizeof(struct sk_buff) + skb2->len;
- skb2->next = NULL;
- skb2->h.raw = ptr;
-
- /* Copy the packet data into the new buffer. */
- memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
-
- /* Now build the MAC header. */
- (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
- if(skb2->len > dev2->mtu)
+ /*
+ * Gosh. Not only is the packet valid; we even know how to
+ * forward it onto its final destination. Can we say this
+ * is being plain lucky?
+ * If the router told us that there is no GW, use the dest.
+ * IP address itself- we seem to be connected directly...
+ */
+
+ raddr = rt->rt_gateway;
+
+ if (raddr != 0)
{
- ip_fragment(NULL,skb2,dev2, is_frag);
- kfree_skb(skb2,FREE_WRITE);
- }
- else
+ /*
+ * There is a gateway so find the correct route for it.
+ * Gateways cannot in turn be gatewayed.
+ */
+ rt = ip_rt_route(raddr, NULL, NULL);
+ if (rt == NULL)
+ {
+ DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
+
+ /*
+ * Tell the sender its packet cannot be delivered...
+ */
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+ return;
+ }
+ if (rt->rt_gateway != 0)
+ raddr = rt->rt_gateway;
+ }
+ else
+ raddr = iph->daddr;
+
+ /*
+ * Having picked a route we can now send the frame out.
+ */
+
+ dev2 = rt->rt_dev;
+
+ /*
+ * In IP you never forward a frame on the interface that it arrived
+ * upon. We should generate an ICMP HOST REDIRECT giving the route
+ * we calculated.
+ * For now just dropping the packet is an acceptable compromise.
+ */
+
+ if (dev == dev2)
+ return;
+
+ /*
+ * We now allocate a new buffer, and copy the datagram into it.
+ * If the indicated interface is up and running, kick it.
+ */
+
+ DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
+ in_ntoa(raddr), dev2->name, skb->len));
+
+ if (dev2->flags & IFF_UP)
{
- if(iph->tos & IPTOS_LOWDELAY)
- dev2->queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
- else if(iph->tos & IPTOS_THROUGHPUT)
- dev2->queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
+
+ /*
+ * Current design decrees we copy the packet. For identical header
+ * lengths we could avoid it. The new skb code will let us push
+ * data so the problem goes away then.
+ */
+
+ skb2 = alloc_skb(dev2->hard_header_len + skb->len, GFP_ATOMIC);
+ /*
+ * This is rare and since IP is tolerant of network failures
+ * quite harmless.
+ */
+ if (skb2 == NULL)
+ {
+ printk("\nIP: No memory available for IP forward\n");
+ return;
+ }
+ ptr = skb2->data;
+ skb2->free = 1;
+ skb2->len = skb->len + dev2->hard_header_len;
+ skb2->h.raw = ptr;
+
+ /*
+ * Copy the packet data into the new buffer.
+ */
+ memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
+
+ /* Now build the MAC header. */
+ (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
+
+ ip_statistics.IpForwDatagrams++;
+
+ /*
+ * See if it needs fragmenting. Note in ip_rcv we tagged
+ * the fragment type. This must be right so that
+ * the fragmenter does the right thing.
+ */
+
+ if(skb2->len > dev2->mtu)
+ {
+ ip_fragment(NULL,skb2,dev2, is_frag);
+ kfree_skb(skb2,FREE_WRITE);
+ }
else
- dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL);
+ {
+ /*
+ * Map service types to priority. We lie about
+ * throughput being low priority, but its a good
+ * choice to help improve general usage.
+ */
+ if(iph->tos & IPTOS_LOWDELAY)
+ dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
+ else if(iph->tos & IPTOS_THROUGHPUT)
+ dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
+ else
+ dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
+ }
}
- }
}
#endif
-/* This function receives all incoming IP datagrams. */
-int
-ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+/*
+ * This function receives all incoming IP datagrams.
+ */
+
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
- struct iphdr *iph = skb->h.iph;
- unsigned char hash;
- unsigned char flag = 0;
- unsigned char opts_p = 0; /* Set iff the packet has options. */
- struct inet_protocol *ipprot;
- static struct options opt; /* since we don't use these yet, and they
+ struct iphdr *iph = skb->h.iph;
+ unsigned char hash;
+ unsigned char flag = 0;
+ unsigned char opts_p = 0; /* Set iff the packet has options. */
+ struct inet_protocol *ipprot;
+ static struct options opt; /* since we don't use these yet, and they
take up stack space. */
- int brd;
- int is_frag=0;
-
- DPRINTF((DBG_IP, "<<\n"));
-
- skb->ip_hdr = iph; /* Fragments can cause ICMP errors too! */
- /* Is the datagram acceptable? */
- if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) {
- DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
- DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
- DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
-
- if (iph->ihl != 5) { /* Fast path for the typical optionless IP packet. */
- ip_print(iph); /* Bogus, only for debugging. */
- memset((char *) &opt, 0, sizeof(opt));
- if (do_options(iph, &opt) != 0)
- return 0;
- opts_p = 1;
- }
+ int brd;
+ int is_frag=0;
+
+
+ ip_statistics.IpInReceives++;
+
+ DPRINTF((DBG_IP, "<<\n"));
+
+ /*
+ * Tag the ip header of this packet so we can find it
+ */
+
+ skb->ip_hdr = iph;
+
+ /*
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+ * (4. We ought to check for IP multicast addresses and undefined types.. does this matter ?)
+ */
+
+ if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0)
+ {
+ ip_statistics.IpInHdrErrors++;
+ DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
+ DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /*
+ * Our transport medium may have padded the buffer out. Now we know it
+ * is IP we can trim to the true length of the frame.
+ */
+
+ skb->len=ntohs(iph->tot_len);
+
+ /*
+ * Next anaylse the packet for options. Studies show under one packet in
+ * a thousand have options....
+ */
+
+ if (iph->ihl != 5)
+ { /* Fast path for the typical optionless IP packet. */
+ ip_print(iph); /* Bogus, only for debugging. */
+ memset((char *) &opt, 0, sizeof(opt));
+ if (do_options(iph, &opt) != 0)
+ return 0;
+ opts_p = 1;
+ }
+
+ /*
+ * Remember if the frame is fragmented.
+ */
- if (iph->frag_off & 0x0020)
- is_frag|=1;
- if (ntohs(iph->frag_off) & 0x1fff)
- is_frag|=2;
+ if (iph->frag_off & 0x0020)
+ is_frag|=1;
+
+ /*
+ * Last fragment ?
+ */
+
+ if (ntohs(iph->frag_off) & 0x1fff)
+ is_frag|=2;
- /* Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. */
- if ((brd = chk_addr(iph->daddr)) == 0) {
+ /*
+ * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday.
+ *
+ * This is inefficient. While finding out if it is for us we could also compute
+ * the routing table entry. This is where the great unified cache theory comes
+ * in as and when someone impliments it
+ */
+
+ if ((brd = ip_chk_addr(iph->daddr)) == 0)
+ {
+
+ /*
+ * The packet is for another target. Forward the frame
+ */
+
#ifdef CONFIG_IP_FORWARD
- ip_forward(skb, dev, is_frag);
+ ip_forward(skb, dev, is_frag);
#else
- printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
+ printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
iph->saddr,iph->daddr);
+ ip_statistics.IpInAddrErrors++;
#endif
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
+ /*
+ * The forwarder is inefficient and copies the packet. We
+ * free the original now.
+ */
+
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
- /*
- * Reassemble IP fragments.
- */
+ /*
+ * Reassemble IP fragments.
+ */
- if(is_frag)
- {
+ if(is_frag)
+ {
#ifdef CONFIG_IP_DEFRAG
- skb=ip_defrag(iph,skb,dev);
- if(skb==NULL)
- {
- return 0;
- }
- iph=skb->h.iph;
+ /* Defragment. Obtain the complete packet if there is one */
+ skb=ip_defrag(iph,skb,dev);
+ if(skb==NULL)
+ return 0;
+ iph=skb->h.iph;
#else
- printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
- printk(" SRC = %s ", in_ntoa(iph->saddr));
- printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr));
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- return(0);
+ printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
+ printk(" SRC = %s ", in_ntoa(iph->saddr));
+ printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr));
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
#endif
- }
-
-
+ }
- if(brd==IS_INVBCAST)
- {
-/* printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n",
- iph->saddr,iph->daddr);*/
- skb->sk=NULL;
- kfree_skb(skb,FREE_WRITE);
- return(0);
- }
-
- /* Point into the IP datagram, just past the header. */
+ /*
+ * Point into the IP datagram, just past the header.
+ */
- skb->ip_hdr = iph;
- skb->h.raw += iph->ihl*4;
- hash = iph->protocol & (MAX_INET_PROTOS -1);
- for (ipprot = (struct inet_protocol *)inet_protos[hash];
- ipprot != NULL;
- ipprot=(struct inet_protocol *)ipprot->next)
- {
- struct sk_buff *skb2;
+ skb->ip_hdr = iph;
+ skb->h.raw += iph->ihl*4;
+
+ /*
+ * skb->h.raw now points at the protocol beyond the IP header.
+ */
+
+ hash = iph->protocol & (MAX_INET_PROTOS -1);
+ for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
+ {
+ struct sk_buff *skb2;
- if (ipprot->protocol != iph->protocol) continue;
- DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
- print_ipprot(ipprot);
+ if (ipprot->protocol != iph->protocol)
+ continue;
+ DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
+ print_ipprot(ipprot);
/*
- * See if we need to make a copy of it. This will
- * only be set if more than one protocol wants it.
- * and then not for the last one.
+ * See if we need to make a copy of it. This will
+ * only be set if more than one protocol wants it.
+ * and then not for the last one.
+ *
+ * This is an artifact of poor upper protocol design.
+ * Because the upper protocols damage the actual packet
+ * we must do copying. In actual fact it's even worse
+ * than this as TCP may hold on to the buffer.
*/
- if (ipprot->copy) {
- skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC);
- if (skb2 == NULL)
- continue;
- memcpy(skb2, skb, skb->mem_len);
- skb2->mem_addr = skb2;
- skb2->ip_hdr = (struct iphdr *)(
- (unsigned long)skb2 +
- (unsigned long) skb->ip_hdr -
- (unsigned long)skb);
- skb2->h.raw = (unsigned char *)(
- (unsigned long)skb2 +
- (unsigned long) skb->h.raw -
- (unsigned long)skb);
- skb2->free=1;
- } else {
- skb2 = skb;
- }
- flag = 1;
+ if (ipprot->copy)
+ {
+#if 0
+ skb2 = alloc_skb(skb->mem_len-sizeof(struct sk_buff), GFP_ATOMIC);
+ if (skb2 == NULL)
+ continue;
+ memcpy(skb2, skb, skb2->mem_len);
+ skb2->ip_hdr = (struct iphdr *)(
+ (unsigned long)skb2 +
+ (unsigned long) skb->ip_hdr -
+ (unsigned long)skb);
+ skb2->h.raw = (unsigned char *)(
+ (unsigned long)skb2 +
+ (unsigned long) skb->h.raw -
+ (unsigned long)skb);
+ skb2->free=1;
+#else
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if(skb2==NULL)
+ continue;
+#endif
+ }
+ else
+ {
+ skb2 = skb;
+ }
+ flag = 1;
- /*
- * Pass on the datagram to each protocol that wants it,
- * based on the datagram protocol. We should really
- * check the protocol handler's return values here...
- */
- ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
- (ntohs(iph->tot_len) - (iph->ihl * 4)),
- iph->saddr, 0, ipprot);
+ /*
+ * Pass on the datagram to each protocol that wants it,
+ * based on the datagram protocol. We should really
+ * check the protocol handler's return values here...
+ */
+ ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
+ (ntohs(iph->tot_len) - (iph->ihl * 4)),
+ iph->saddr, 0, ipprot);
- }
+ }
- /*
- * All protocols checked.
- * If this packet was a broadcast, we may *not* reply to it, since that
- * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
- * ICMP reply messages get queued up for transmission...)
- */
- if (!flag) {
- if (brd != IS_BROADCAST)
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- }
+ /*
+ * All protocols checked.
+ * If this packet was a broadcast, we may *not* reply to it, since that
+ * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+ * ICMP reply messages get queued up for transmission...)
+ */
- return(0);
+ if (!flag)
+ {
+ if (brd != IS_BROADCAST)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+ kfree_skb(skb, FREE_WRITE);
+ }
+
+ return(0);
}
* Queues a packet to be sent, and starts the transmitter
* if necessary. if free = 1 then we free the block after
* transmit, otherwise we don't.
- * This routine also needs to put in the total length, and
- * compute the checksum.
+ * This routine also needs to put in the total length,
+ * and compute the checksum
*/
-void
-ip_queue_xmit(struct sock *sk, struct device *dev,
+
+void ip_queue_xmit(struct sock *sk, struct device *dev,
struct sk_buff *skb, int free)
{
- struct iphdr *iph;
- unsigned char *ptr;
+ struct iphdr *iph;
+ unsigned char *ptr;
- if (sk == NULL) free = 1;
- if (dev == NULL) {
- printk("IP: ip_queue_xmit dev = NULL\n");
- return;
- }
- IS_SKB(skb);
- skb->free = free;
- skb->dev = dev;
- skb->when = jiffies;
+ /* All buffers without an owner socket get freed */
+ if (sk == NULL)
+ free = 1;
+
+ /* Sanity check */
+ if (dev == NULL)
+ {
+ printk("IP: ip_queue_xmit dev = NULL\n");
+ return;
+ }
- DPRINTF((DBG_IP, ">>\n"));
- ptr = skb->data;
- ptr += dev->hard_header_len;
- iph = (struct iphdr *)ptr;
- skb->ip_hdr = iph;
- iph->tot_len = ntohs(skb->len-dev->hard_header_len);
-
- if(skb->len > dev->mtu)
- {
-/* printk("Fragment!\n");*/
- ip_fragment(sk,skb,dev,0);
IS_SKB(skb);
- kfree_skb(skb,FREE_WRITE);
- return;
- }
+
+ /*
+ * Do some book-keeping in the packet for later
+ */
+
+ skb->free = free;
+ skb->dev = dev;
+ skb->when = jiffies;
- ip_send_check(iph);
- ip_print(iph);
- skb->next = NULL;
-
- /* See if this is the one trashing our queue. Ross? */
- skb->magic = 1;
- if (!free) {
- skb->link3 = NULL;
- sk->packets_out++;
- cli();
- if (sk->send_head == NULL) {
- sk->send_tail = skb;
- sk->send_head = skb;
- } else {
- /* See if we've got a problem. */
- if (sk->send_tail == NULL) {
- printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n");
- sort_send(sk);
- } else {
+ DPRINTF((DBG_IP, ">>\n"));
+
+ /*
+ * Find the IP header and set the length. This is bad
+ * but once we get the skb data handling code in the
+ * hardware will push its header sensibly and we will
+ * set skb->ip_hdr to avoid this mess and the fixed
+ * header length problem
+ */
+
+ ptr = skb->data;
+ ptr += dev->hard_header_len;
+ iph = (struct iphdr *)ptr;
+ skb->ip_hdr = iph;
+ iph->tot_len = ntohs(skb->len-dev->hard_header_len);
+
+ /*
+ * Do we need to fragment. Again this is inefficient.
+ * We need to somehow lock the original buffer and use
+ * bits of it.
+ */
+
+ if(skb->len > dev->mtu)
+ {
+ ip_fragment(sk,skb,dev,0);
+ IS_SKB(skb);
+ kfree_skb(skb,FREE_WRITE);
+ return;
+ }
+
+ /*
+ * Add an IP checksum
+ */
+
+ ip_send_check(iph);
+
+ /*
+ * Print the frame when debugging
+ */
+ ip_print(iph);
+
+ /*
+ * More debugging. You cannot queue a packet already on a list
+ * Spot this and moan loudly.
+ */
+ if (skb->next != NULL)
+ {
+ printk("ip_queue_xmit: next != NULL\n");
+ skb_unlink(skb);
+ }
+
+ /*
+ * If a sender wishes the packet to remain unfreed
+ * we add it to his send queue. This arguably belongs
+ * in the TCP level since nobody elses uses it. BUT
+ * remember IPng might change all the rules.
+ */
+
+ if (!free)
+ {
+ unsigned long flags;
+ /* The socket now has more outstanding blocks */
+
+ sk->packets_out++;
+
+ /* Protect the list for a moment */
+ save_flags(flags);
+ cli();
+
+ if (skb->link3 != NULL)
+ {
+ printk("ip.c: link3 != NULL\n");
+ skb->link3 = NULL;
+ }
+ if (sk->send_head == NULL)
+ {
+ sk->send_tail = skb;
+ sk->send_head = skb;
+ }
+ else
+ {
sk->send_tail->link3 = skb;
sk->send_tail = skb;
}
- }
- sti();
- reset_timer(sk, TIME_WRITE, sk->rto);
- } else {
- skb->sk = sk;
- }
+ /* skb->link3 is NULL */
+
+ /* Interrupt restore */
+ restore_flags(flags);
+ /* Set the IP write timeout to the round trip time for the packet.
+ If an acknowledge has not arrived by then we may wish to act */
+ reset_timer(sk, TIME_WRITE, sk->rto);
+ }
+ else
+ /* Remember who owns the buffer */
+ skb->sk = sk;
- /* If the indicated interface is up and running, kick it. */
- if (dev->flags & IFF_UP) {
- if (sk != NULL) {
- dev->queue_xmit(skb, dev, sk->priority);
+ /*
+ * If the indicated interface is up and running, send the packet.
+ */
+ ip_statistics.IpOutRequests++;
+
+ if (dev->flags & IFF_UP)
+ {
+ /*
+ * If we have an owner use its priority setting,
+ * otherwise use NORMAL
+ */
+
+ if (sk != NULL)
+ {
+ dev_queue_xmit(skb, dev, sk->priority);
+ }
+ else
+ {
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ }
}
- else {
- dev->queue_xmit(skb, dev, SOPRI_NORMAL);
+ else
+ {
+ ip_statistics.IpOutDiscards++;
+ if (free)
+ kfree_skb(skb, FREE_WRITE);
}
- } else {
- if (free) kfree_skb(skb, FREE_WRITE);
- }
}
-void
-ip_do_retransmit(struct sock *sk, int all)
+/*
+ * A socket has timed out on its send queue and wants to do a
+ * little retransmitting. Currently this means TCP.
+ */
+
+void ip_do_retransmit(struct sock *sk, int all)
{
- struct sk_buff * skb;
- struct proto *prot;
- struct device *dev;
- int retransmits;
-
- prot = sk->prot;
- skb = sk->send_head;
- retransmits = sk->retransmits;
- while (skb != NULL) {
- dev = skb->dev;
- /* I know this can't happen but as it does.. */
- if(dev==NULL)
+ struct sk_buff * skb;
+ struct proto *prot;
+ struct device *dev;
+ int retransmits;
+
+ prot = sk->prot;
+ skb = sk->send_head;
+ retransmits = sk->retransmits;
+
+ while (skb != NULL)
{
- printk("ip_retransmit: NULL device bug!\n");
- goto oops;
- }
+ dev = skb->dev;
+ IS_SKB(skb);
+#if 0
+ /********** THIS IS NOW DONE BY THE DEVICE LAYER **********/
+ /*
+ * The rebuild_header function sees if the ARP is done.
+ * If not it sends a new ARP request, and if so it builds
+ * the header. It isn't really needed here, and with the
+ * new ARP pretty much will not happen.
+ */
+
+ if (!skb->arp)
+ {
+ if (dev->rebuild_header(skb->data, dev, skb->raddr, NULL))
+ {
+ if (!all)
+ break;
+ skb = skb->link3;
+ continue;
+ }
+ }
+#endif
+ skb->when = jiffies;
- IS_SKB(skb);
-
- /*
- * The rebuild_header function sees if the ARP is done.
- * If not it sends a new ARP request, and if so it builds
- * the header.
- */
- cli(); /* We might get interrupted by an arp reply here and fill
- the frame in twice. Because of the technique used this
- would be a little sad */
- if (!skb->arp) {
- if (dev->rebuild_header(skb->data, dev)) {
- sti(); /* Failed to rebuild - next */
- if (!all) break;
- skb = (struct sk_buff *)skb->link3;
- continue;
+ /*
+ * If the interface is (still) up and running, kick it.
+ */
+
+ if (dev->flags & IFF_UP)
+ {
+ /*
+ * If the packet is still being sent by the device/protocol
+ * below then don't retransmit. This is both needed, and good -
+ * especially with connected mode AX.25 where it stops resends
+ * occuring of an as yet unsent anyway frame!
+ * We still add up the counts as the round trip time wants
+ * adjusting.
+ */
+ if (sk && !skb_device_locked(skb))
+ {
+ /* Remove it from any existing driver queue first! */
+ skb_unlink(skb);
+ /* Now queue it */
+ ip_statistics.IpOutRequests++;
+ dev_queue_xmit(skb, dev, sk->priority);
+ }
}
- }
- skb->arp = 1;
- sti();
- skb->when = jiffies;
+
+ /*
+ * Count retransmissions
+ */
+ retransmits++;
+ sk->prot->retransmits ++;
+
+ /*
+ * Only one retransmit requested.
+ */
+ if (!all)
+ break;
- /* If the interface is (still) up and running, kick it. */
- if (dev->flags & IFF_UP) {
- if (sk && !skb_device_locked(skb))
- dev->queue_xmit(skb, dev, sk->priority);
- /* else dev->queue_xmit(skb, dev, SOPRI_NORMAL ); CANNOT HAVE SK=NULL HERE */
+ /*
+ * This should cut it off before we send too many packets.
+ */
+ if (sk->retransmits > sk->cong_window)
+ break;
+ skb = skb->link3;
}
-
-oops: retransmits++;
- sk->prot->retransmits ++;
- if (!all) break;
-
- /* This should cut it off before we send too many packets. */
- if (sk->retransmits > sk->cong_window) break;
- skb = (struct sk_buff *)skb->link3;
- }
}
/*
- * This is the normal code called for timeouts. It does the retransmission
- * and then does backoff. ip_do_retransmit is separated out because
- * tcp_ack needs to send stuff from the retransmit queue without
- * initiating a backoff.
+ * This is the normal code called for timeouts. It does the retransmission
+ * and then does backoff. ip_do_retransmit is separated out because
+ * tcp_ack needs to send stuff from the retransmit queue without
+ * initiating a backoff.
*/
-void
-ip_retransmit(struct sock *sk, int all)
+void ip_retransmit(struct sock *sk, int all)
{
- ip_do_retransmit(sk, all);
-
- /*
- * Increase the timeout each time we retransmit. Note that
- * we do not increase the rtt estimate. rto is initialized
- * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
- * that doubling rto each time is the least we can get away with.
- * In KA9Q, Karns uses this for the first few times, and then
- * goes to quadratic. netBSD doubles, but only goes up to *64,
- * and clamps at 1 to 64 sec afterwards. Note that 120 sec is
- * defined in the protocol as the maximum possible RTT. I guess
- * we'll have to use something other than TCP to talk to the
- * University of Mars.
- */
-
- sk->retransmits++;
- sk->backoff++;
- sk->rto = min(sk->rto << 1, 120*HZ);
- reset_timer(sk, TIME_WRITE, sk->rto);
+ ip_do_retransmit(sk, all);
+
+ /*
+ * Increase the timeout each time we retransmit. Note that
+ * we do not increase the rtt estimate. rto is initialized
+ * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
+ * that doubling rto each time is the least we can get away with.
+ * In KA9Q, Karn uses this for the first few times, and then
+ * goes to quadratic. netBSD doubles, but only goes up to *64,
+ * and clamps at 1 to 64 sec afterwards. Note that 120 sec is
+ * defined in the protocol as the maximum possible RTT. I guess
+ * we'll have to use something other than TCP to talk to the
+ * University of Mars.
+ */
+
+ sk->retransmits++;
+ sk->backoff++;
+ sk->rto = min(sk->rto << 1, 120*HZ);
+ reset_timer(sk, TIME_WRITE, sk->rto);
}
/*
* Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
* an IP socket.
+ *
+ * We impliment IP_TOS (type of service), IP_TTL (time to live).
+ *
+ * Next release we will sort out IP_OPTIONS since for some people are kind of important.
*/
int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
}
}
+/*
+ * Get the options. Note for future reference. The GET of IP options gets the
+ * _received_ ones. The set sets the _sent_ ones.
+ */
+
int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
{
int val,err;
return(0);
}
+
+/*
+ * IP protocol layer initialiser
+ */
+
+static struct packet_type ip_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_IP),*/
+ 0, /* copy */
+ ip_rcv,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * IP registers the packet type and then calls the subprotocol initialisers
+ */
+
+void ip_init(void)
+{
+ ip_packet_type.type=htons(ETH_P_IP);
+ dev_add_pack(&ip_packet_type);
+/* ip_raw_init();
+ ip_packet_init();
+ ip_tcp_init();
+ ip_udp_init();*/
+}
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <linux/ip.h>
+#ifndef _SNMP_H
+#include "snmp.h"
+#endif
#include "sock.h" /* struct sock */
extern int backoff(int n);
-extern void ip_print(struct iphdr *ip);
+extern void ip_print(const struct iphdr *ip);
extern int ip_ioctl(struct sock *sk, int cmd,
unsigned long arg);
extern void ip_route_check(unsigned long daddr);
extern void ip_do_retransmit(struct sock *sk, int all);
extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+extern void ip_init(void);
+extern struct ip_mib ip_statistics;
#endif /* _IP_H */
--- /dev/null
+/*
+ * Implements an IPX socket layer (badly - but I'm working on it).
+ *
+ * This code is derived from work by
+ * Ross Biro : Writing the original IP stack
+ * Fred Van Kempen : Tidying up the TCP/IP
+ *
+ * Many thanks go to Keith Baker, Institute For Industrial Information
+ * Technology Ltd, Swansea University for allowing me to work on this
+ * in my own time even though it was in some ways related to commercial
+ * work I am currently employed to do there.
+ *
+ * All the material in this file is subject to the Gnu license version 2.
+ * Neither Alan Cox nor the Swansea University Computer Society admit liability
+ * nor provide warranty for any of this software. This material is provided
+ * as is and at no charge.
+ *
+ * Revision 0.21: Uses the new generic socket option code.
+ * Revision 0.22: Gcc clean ups and drop out device registration. Use the
+ * new multi-protocol edition of hard_header
+ * Revision 0.23: IPX /proc by Mark Evans.
+ * Adding a route will overwrite any existing route to the same
+ * network.
+ * Revision 0.24: Supports new /proc with no 4K limit
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/ipx.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#ifdef CONFIG_IPX
+/***********************************************************************************************************************\
+* *
+* Handlers for the socket list. *
+* *
+\***********************************************************************************************************************/
+
+static ipx_socket *volatile ipx_socket_list=NULL;
+
+/*
+ * Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ * handler using this technique. They can be added although we do not
+ * use this facility.
+ */
+
+static void ipx_remove_socket(ipx_socket *sk)
+{
+ ipx_socket *s;
+
+ cli();
+ s=ipx_socket_list;
+ if(s==sk)
+ {
+ ipx_socket_list=s->next;
+ sti();
+ return;
+ }
+ while(s && s->next)
+ {
+ if(s->next==sk)
+ {
+ s->next=sk->next;
+ sti();
+ return;
+ }
+ s=s->next;
+ }
+ sti();
+}
+
+static void ipx_insert_socket(ipx_socket *sk)
+{
+ cli();
+ sk->next=ipx_socket_list;
+ ipx_socket_list=sk;
+ sti();
+}
+
+static ipx_socket *ipx_find_socket(int port)
+{
+ ipx_socket *s;
+ s=ipx_socket_list;
+ while(s)
+ {
+ if(s->ipx_source_addr.sock==port)
+ {
+ return(s);
+ }
+ s=s->next;
+ }
+ return(NULL);
+}
+
+/*
+ * This is only called from user mode. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+
+static void ipx_destroy_socket(ipx_socket *sk)
+{
+ struct sk_buff *skb;
+ ipx_remove_socket(sk);
+
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+ {
+ kfree_skb(skb,FREE_READ);
+ }
+
+ kfree_s(sk,sizeof(*sk));
+}
+
+
+/* Called from proc fs */
+int ipx_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ ipx_socket *s;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ /* Theory.. Keep printing in the same place until we pass offset */
+
+ len += sprintf (buffer,"Type local_address rem_address tx_queue rx_queue st uid\n");
+ for (s = ipx_socket_list; s != NULL; s = s->next)
+ {
+ len += sprintf (buffer+len,"%02X ", s->ipx_type);
+ len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_source_addr.net),
+ s->ipx_source_addr.node[0], s->ipx_source_addr.node[1], s->ipx_source_addr.node[2],
+ s->ipx_source_addr.node[3], s->ipx_source_addr.node[4], s->ipx_source_addr.node[5],
+ htons(s->ipx_source_addr.sock));
+ len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_dest_addr.net),
+ s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], s->ipx_dest_addr.node[2],
+ s->ipx_dest_addr.node[3], s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
+ htons(s->ipx_dest_addr.sock));
+ len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc);
+ len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
+
+ /* Are we still dumping unwanted data then discard the record */
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0; /* Keep dumping into the buffer start */
+ begin=pos;
+ }
+ if(pos>offset+length) /* We have dumped enough */
+ break;
+ }
+
+ /* The data in question runs from begin to begin+len */
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Remove unwanted header data from lenth */
+ if(len>length)
+ len=length; /* Remove unwanted tail data from length */
+
+ return len;
+}
+
+/*******************************************************************************************************************\
+* *
+* Routing tables for the IPX socket layer *
+* *
+\*******************************************************************************************************************/
+
+
+static ipx_route *ipx_router_list=NULL;
+
+static ipx_route *ipxrtr_get_dev(long net)
+{
+ ipx_route *r;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ r=ipx_router_list;
+ while(r!=NULL)
+ {
+ if(r->net==net)
+ {
+ restore_flags(flags);
+ return r;
+ }
+ r=r->next;
+ }
+ restore_flags(flags);
+ return NULL;
+}
+
+static int ipxrtr_create(struct ipx_route_def *r)
+{
+ ipx_route *rt=ipxrtr_get_dev(r->ipx_network);
+ struct device *dev;
+
+ if(r->ipx_router_network!=0)
+ {
+ /* Adding an indirect route */
+ ipx_route *rt1=ipxrtr_get_dev(r->ipx_router_network);
+ if(rt1==NULL)
+ return -ENETUNREACH;
+ if(rt1->flags&IPX_RT_ROUTED)
+ return -EMULTIHOP;
+ if (rt==NULL)
+ {
+ rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC); /* Because we are brave and don't lock the table! */
+ if(rt==NULL)
+ return -EAGAIN;
+ rt->next=ipx_router_list;
+ ipx_router_list=rt;
+ }
+ rt->net=r->ipx_network;
+ rt->router_net=r->ipx_router_network;
+ memcpy(rt->router_node,r->ipx_router_node,sizeof(rt->router_node));
+ rt->flags=(rt1->flags&IPX_RT_BLUEBOOK)|IPX_RT_ROUTED;
+ rt->dev=rt1->dev;
+ return 0;
+ }
+ /* Add a direct route */
+ dev=dev_get(r->ipx_device);
+ if(dev==NULL)
+ return -ENODEV;
+ /* Check addresses are suitable */
+ if(dev->addr_len>6)
+ return -EINVAL;
+ if(dev->addr_len<2)
+ return -EINVAL;
+ /* Ok now create */
+ if (rt==NULL)
+ {
+ rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC); /* Because we are brave and don't lock the table! */
+ if(rt==NULL)
+ return -EAGAIN;
+ rt->next=ipx_router_list;
+ ipx_router_list=rt;
+ }
+ rt->router_net=0;
+ memset(rt->router_node,0,sizeof(rt->router_node));
+ rt->dev=dev;
+ rt->net=r->ipx_network;
+ rt->flags=r->ipx_flags&IPX_RT_BLUEBOOK;
+ return 0;
+}
+
+static int ipxrtr_delete(long net)
+{
+ ipx_route *r=ipx_router_list;
+ if(r->net==net)
+ {
+ ipx_router_list=r->next;
+ return 0;
+ }
+ while(r->next!=NULL)
+ {
+ if(r->next->net==net)
+ {
+ ipx_route *d=r->next;
+ r->next=d->next;
+ kfree_s(d,sizeof(ipx_route));
+ return 0;
+ }
+ r=r->next;
+ }
+ return -ENOENT;
+}
+
+static int ipxrtr_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ switch(cmd)
+ {
+ case SIOCDELRT:
+ err=verify_area(VERIFY_READ,arg,sizeof(long));
+ if(err)
+ return err;
+ return ipxrtr_delete(get_fs_long(arg));
+ case SIOCADDRT:
+ {
+ struct ipx_route_def f;
+ err=verify_area(VERIFY_READ,arg,sizeof(f));
+ if(err)
+ return err;
+ memcpy_fromfs(&f,arg,sizeof(f));
+ return ipxrtr_create(&f);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Called from proc fs */
+int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ ipx_route *rt;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
+ len += sprintf (buffer,"Net Router Flags Dev\n");
+ for (rt = ipx_router_list; rt != NULL; rt = rt->next)
+ {
+ len += sprintf (buffer+len,"%08lX %08lX:%02X%02X%02X%02X%02X%02X %02X %s\n", ntohl(rt->net),
+ ntohl(rt->router_net), rt->router_node[0], rt->router_node[1], rt->router_node[2],
+ rt->router_node[3], rt->router_node[4], rt->router_node[5], rt->flags, rt->dev->name);
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
+/*******************************************************************************************************************\
+* *
+* Handling for system calls applied via the various interfaces to an IPX socket object *
+* *
+\*******************************************************************************************************************/
+
+static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ ipx_socket *sk;
+
+ sk=(ipx_socket *)sock->data;
+
+ if(sk==NULL)
+ {
+ printk("IPX:fcntl:passed sock->data=NULL\n");
+ return(0);
+ }
+
+ switch(cmd)
+ {
+ default:
+ return(-EINVAL);
+ }
+}
+
+static int ipx_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ ipx_socket *sk;
+ int err,opt;
+
+ sk=(ipx_socket *)sock->data;
+
+ if(sk==NULL)
+ {
+ printk("IPX:setsockopt:passed sock->data=NULL\n");
+ return 0;
+ }
+
+ if(optval==NULL)
+ return(-EINVAL);
+ err=verify_area(VERIFY_READ,optval,sizeof(int));
+ if(err)
+ return err;
+ opt=get_fs_long((unsigned long *)optval);
+
+ switch(level)
+ {
+ case SOL_IPX:
+ switch(optname)
+ {
+ case IPX_TYPE:
+ if(!suser())
+ return(-EPERM);
+ sk->ipx_type=opt;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ case SOL_SOCKET:
+ return sock_setsockopt(sk,level,optname,optval,optlen);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ipx_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ ipx_socket *sk;
+ int val=0;
+ int err;
+
+ sk=(ipx_socket *)sock->data;
+ if(sk==NULL)
+ {
+ printk("IPX:getsockopt:passed NULL sock->data.\n");
+ return 0;
+ }
+
+ switch(level)
+ {
+
+ case SOL_IPX:
+ switch(optname)
+ {
+ case IPX_TYPE:
+ val=sk->ipx_type;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+ break;
+
+ case SOL_SOCKET:
+ return sock_getsockopt(sk,level,optname,optval,optlen);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
+ if(err)
+ return err;
+ put_fs_long(sizeof(int),(unsigned long *)optlen);
+ err=verify_area(VERIFY_WRITE,optval,sizeof(int));
+ put_fs_long(val,(unsigned long *)optval);
+ return(0);
+}
+
+static int ipx_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+static void def_callback1(struct sock *sk)
+{
+ if(!sk->dead)
+ wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+ if(!sk->dead)
+ wake_up_interruptible(sk->sleep);
+}
+
+static int ipx_create(struct socket *sock, int protocol)
+{
+ ipx_socket *sk;
+ sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+ if(sk==NULL)
+ return(-ENOMEM);
+ switch(sock->type)
+ {
+ case SOCK_DGRAM:
+ break;
+ default:
+ kfree_s((void *)sk,sizeof(*sk));
+ return(-ESOCKTNOSUPPORT);
+ }
+ sk->stamp.tv_sec=0;
+ sk->rmem_alloc=0;
+ sk->dead=0;
+ sk->next=NULL;
+ sk->broadcast=0;
+ sk->rcvbuf=SK_RMEM_MAX;
+ sk->sndbuf=SK_WMEM_MAX;
+ sk->wmem_alloc=0;
+ sk->rmem_alloc=0;
+ sk->inuse=0;
+ sk->dead=0;
+ sk->prot=NULL; /* So we use default free mechanisms */
+ sk->broadcast=0;
+ sk->err=0;
+ skb_queue_head_init(&sk->receive_queue);
+ skb_queue_head_init(&sk->write_queue);
+ sk->send_head=NULL;
+ skb_queue_head_init(&sk->back_log);
+ sk->mtu=512;
+ sk->state=TCP_CLOSE;
+ sk->socket=sock;
+ sk->type=sock->type;
+ sk->ipx_type=0; /* General user level IPX */
+ sk->debug=0;
+
+ memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
+ memset(&sk->ipx_source_addr,'\0',sizeof(sk->ipx_source_addr));
+ sk->mtu=IPX_MTU;
+
+ if(sock!=NULL)
+ {
+ sock->data=(void *)sk;
+ sk->sleep=sock->wait;
+ }
+
+ sk->state_change=def_callback1;
+ sk->data_ready=def_callback2;
+ sk->write_space=def_callback1;
+ sk->error_report=def_callback1;
+
+ sk->zapped=1;
+ return(0);
+}
+
+static int ipx_dup(struct socket *newsock,struct socket *oldsock)
+{
+ return(ipx_create(newsock,SOCK_DGRAM));
+}
+
+static int ipx_release(struct socket *sock, struct socket *peer)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ if(sk==NULL)
+ return(0);
+ if(!sk->dead)
+ sk->state_change(sk);
+ sk->dead=1;
+ sock->data=NULL;
+ ipx_destroy_socket(sk);
+ return(0);
+}
+
+static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
+{
+ ipx_socket *sk;
+ int err;
+ struct sockaddr_ipx addr;
+ struct ipx_route *rt;
+
+ sk=(ipx_socket *)sock->data;
+ if(sk==NULL)
+ {
+ printk("IPX:bind:sock->data=NULL\n");
+ return 0;
+ }
+
+ if(sk->zapped==0)
+ return(-EIO);
+
+ err=verify_area(VERIFY_READ,uaddr,addr_len);
+ if(err)
+ return err;
+ if(addr_len!=sizeof(addr))
+ return -EINVAL;
+ memcpy_fromfs(&addr,uaddr,addr_len);
+
+ if(ntohs(addr.sipx_port)<0x4000 && !suser())
+ return(-EPERM); /* protect IPX system stuff like routing/sap */
+
+ /* Source addresses are easy. It must be our network:node pair for
+ an interface routed to IPX with the ipx routing ioctl() */
+
+ if(ipx_find_socket(addr.sipx_port)!=NULL)
+ {
+ if(sk->debug)
+ printk("IPX: bind failed because port %X in use.\n",
+ (int)addr.sipx_port);
+ return(-EADDRINUSE);
+ }
+ sk->ipx_source_addr.sock=addr.sipx_port;
+ memcpy(sk->ipx_source_addr.node,addr.sipx_node,sizeof(sk->ipx_source_addr.node));
+ sk->ipx_source_addr.net=addr.sipx_network;
+ if((rt=ipxrtr_get_dev(sk->ipx_source_addr.net))==NULL)
+ {
+ if(sk->debug)
+ printk("IPX: bind failed (no device for net %lX)\n",
+ sk->ipx_source_addr.net);
+ return(-EADDRNOTAVAIL);
+ }
+ memset(sk->ipx_source_addr.node,'\0',6);
+ memcpy(sk->ipx_source_addr.node,rt->dev->dev_addr,rt->dev->addr_len);
+ ipx_insert_socket(sk);
+ sk->zapped=0;
+ if(sk->debug)
+ printk("IPX: socket is bound.\n");
+ return(0);
+}
+
+static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx addr;
+ int err;
+
+ if(sk==NULL)
+ {
+ printk("IPX:connect:sock->data=NULL!\n");
+ return 0;
+ }
+
+ sk->state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if(addr_len!=sizeof(addr))
+ return(-EINVAL);
+ err=verify_area(VERIFY_READ,uaddr,addr_len);
+ if(err)
+ return err;
+ memcpy_fromfs(&addr,uaddr,sizeof(addr));
+
+ if(ntohs(addr.sipx_port)<0x4000 && !suser())
+ return -EPERM;
+ if(sk->ipx_source_addr.net==0) /* Must bind first - no autobinding in this */
+ return -EINVAL;
+
+
+ sk->ipx_dest_addr.net=addr.sipx_network;
+ sk->ipx_dest_addr.sock=addr.sipx_port;
+ memcpy(sk->ipx_dest_addr.node,addr.sipx_node,sizeof(sk->ipx_source_addr.node));
+ if(ipxrtr_get_dev(sk->ipx_dest_addr.net)==NULL)
+ return -ENETUNREACH;
+ sock->state = SS_CONNECTED;
+ sk->state=TCP_ESTABLISHED;
+ return(0);
+}
+
+static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return(-EOPNOTSUPP);
+}
+
+static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ if(newsock->data)
+ kfree_s(newsock->data,sizeof(ipx_socket));
+ return -EOPNOTSUPP;
+}
+
+static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ ipx_address *addr;
+ struct sockaddr_ipx sipx;
+ ipx_socket *sk;
+ int len;
+ int err;
+
+ sk=(ipx_socket *)sock->data;
+
+ err = verify_area(VERIFY_WRITE,uaddr_len,sizeof(long));
+ if(err)
+ return err;
+
+ len = get_fs_long(uaddr_len);
+
+ err = verify_area(VERIFY_WRITE, uaddr, len);
+ if(err)
+ return err;
+
+ if(len<sizeof(struct sockaddr_ipx))
+ return -EINVAL;
+
+ if(peer)
+ {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ addr=&sk->ipx_dest_addr;
+ }
+ else
+ addr=&sk->ipx_source_addr;
+
+ sipx.sipx_family = AF_IPX;
+ sipx.sipx_port = addr->sock;
+ sipx.sipx_network = addr->net;
+ memcpy(sipx.sipx_node,addr->node,sizeof(sipx.sipx_node));
+ memcpy_tofs(uaddr,&sipx,sizeof(sipx));
+ put_fs_long(len,uaddr_len);
+ return(0);
+}
+
+
+int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ /* NULL here for pt means the packet was looped back */
+ ipx_socket *sock;
+ unsigned char *buff;
+ ipx_packet *ipx;
+ ipx_route *rt;
+
+ buff=skb->data;
+ buff+=dev->hard_header_len;
+ ipx=(ipx_packet *)buff;
+
+ if(ipx->ipx_checksum!=IPX_NO_CHECKSUM)
+ {
+ /* We don't do checksum options. We can't really. Novell don't seem to have documented them.
+ If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
+ the same... */
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+
+ /* Too small */
+ if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)+dev->hard_header_len)
+ {
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+
+ /* Too many hops */
+ if(ipx->ipx_tctrl>16)
+ {
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+
+ /* Not us/broadcast */
+ if(memcmp(dev->dev_addr,ipx->ipx_dest.node,dev->addr_len)!=0
+ && memcmp(ipx_broadcast_node,ipx->ipx_dest.node,dev->addr_len)!=0)
+ {
+ /**********************************************************************************************
+
+ IPX router. Roughly as per the Novell spec. This doesn't handle netbios flood fill
+ broadcast frames. See the Novell IPX router specification for more details
+ (for ftp from ftp.novell.com)
+
+ ***********************************************************************************************/
+
+ struct sk_buff *skb2;
+ int free_it=0;
+
+ /* Rule: Don't forward packets that have exceeded the hop limit. This is fixed at 16 in IPX */
+ if(ipx->ipx_tctrl==16)
+ {
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+
+ /* Don't forward if we don't have a route. We ought to go off and start hunting out routes but
+ if someone needs this _THEY_ can add it */
+ rt=ipxrtr_get_dev(ipx->ipx_dest.net);
+ if(rt==NULL) /* Unlike IP we can send on the interface we received. Eg doing DIX/802.3 conversion */
+ {
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+ if(rt->dev->hard_header_len!=dev->hard_header_len)
+ {
+ /* A different header length causes a copy. Awkward to avoid with the current
+ sk_buff stuff. */
+ skb2=alloc_skb(skb->len,GFP_ATOMIC);
+ if(skb2==NULL)
+ {
+ kfree_skb(skb,FREE_READ);
+ return 0;
+ }
+ free_it=1;
+ skb2->free=1;
+ skb2->len=skb->len;
+ memcpy((char *)(skb2+1),(char *)(skb+1),skb->len);
+ buff=(char *)(skb2+1);
+ ipx=(ipx_packet *)(buff+dev->hard_header_len);
+ }
+ else
+ {
+ skb2=skb;
+ buff=(char *)(skb+1);
+ }
+ /* Now operate on the buffer */
+ /* Increase hop count */
+ ipx->ipx_tctrl++;
+ /* If the route is a gateway then forward to it */
+
+ dev->hard_header(buff, dev,
+ (rt->flags&IPX_RT_BLUEBOOK)?ntohs(ETH_P_IPX):ntohs(skb2->len),
+ (rt->flags&IPX_RT_ROUTED)?rt->router_node:ipx->ipx_dest.node,
+ NULL, /* Our source */
+ skb2->len,
+ skb2);
+
+ dev_queue_xmit(skb2,dev,SOPRI_NORMAL);
+
+ if(free_it)
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+ /************ End of router: Now sanity check stuff for us ***************/
+
+ /* Ok its for us ! */
+
+ sock=ipx_find_socket(ipx->ipx_dest.sock);
+ if(sock==NULL) /* But not one of our sockets */
+ {
+ kfree_skb(skb,FREE_READ);
+ return(0);
+ }
+
+ if(sock->rmem_alloc>=sock->rcvbuf)
+ {
+ kfree_skb(skb,FREE_READ); /* Socket is full */
+ return(0);
+ }
+
+ sock->rmem_alloc+=skb->mem_len;
+ skb_queue_tail(&sock->receive_queue,skb);
+ if(!sock->dead)
+ sock->data_ready(sock,skb->len);
+ return(0);
+}
+
+
+
+
+static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
+ unsigned flags, struct sockaddr *usip, int addr_len)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
+ int err;
+ struct sockaddr_ipx sipx;
+ struct sk_buff *skb;
+ struct device *dev;
+ struct ipx_packet *ipx;
+ int size;
+ ipx_route *rt;
+
+ if(flags)
+ return -EINVAL;
+ if(len<0)
+ return -EINVAL;
+ if(len == 0)
+ return 0;
+
+ if(usipx)
+ {
+ if(addr_len <sizeof(sipx))
+ return(-EINVAL);
+ err=verify_area(VERIFY_READ,usipx,sizeof(sipx));
+ if(err)
+ return(err);
+ memcpy_fromfs(&sipx,usipx,sizeof(sipx));
+ if(sipx.sipx_family != AF_IPX)
+ return -EINVAL;
+ if(htons(sipx.sipx_port)<0x4000 && !suser())
+ return -EPERM;
+ }
+ else
+ {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ sipx.sipx_family=AF_IPX;
+ sipx.sipx_port=sk->ipx_dest_addr.sock;
+ sipx.sipx_network=sk->ipx_dest_addr.net;
+ memcpy(sipx.sipx_node,sk->ipx_dest_addr.node,sizeof(sipx.sipx_node));
+ }
+
+ if(sk->debug)
+ printk("IPX: sendto: Addresses built.\n");
+ if(!sk->broadcast && memcmp(&sipx.sipx_node,&ipx_broadcast_node,6)==0)
+ return -ENETUNREACH;
+ /* Build a packet */
+
+ if(sk->debug)
+ printk("IPX: sendto: building packet.\n");
+ err=verify_area(VERIFY_READ,ubuf,len);
+ if(err)
+ return err;
+
+ size=sizeof(ipx_packet)+len; /* For mac headers */
+
+ /* Find out where this has to go */
+ rt=ipxrtr_get_dev(sipx.sipx_network);
+ if(rt==NULL)
+ {
+ return -ENETUNREACH;
+ }
+
+ dev=rt->dev;
+
+ size+=dev->hard_header_len;
+
+ if(sk->debug)
+ printk("IPX: sendto: allocating buffer (%d)\n",size-sizeof(struct sk_buff));
+
+ if(size+sk->wmem_alloc>sk->sndbuf)
+ return -EAGAIN;
+
+ skb=alloc_skb(size,GFP_KERNEL);
+ if(skb==NULL)
+ return -ENOMEM;
+ if(skb->mem_len+sk->wmem_alloc>sk->sndbuf)
+ {
+ kfree_skb(skb,FREE_WRITE);
+ return -EAGAIN;
+ }
+
+ sk->wmem_alloc+=skb->mem_len;
+ skb->sk=sk;
+ skb->free=1;
+ skb->arp=1;
+ skb->len=size-sizeof(struct sk_buff);
+
+ if(sk->debug)
+ printk("Building MAC header.\n");
+ skb->dev=rt->dev;
+
+ dev->hard_header(skb->data,skb->dev,
+ (rt->flags&IPX_RT_BLUEBOOK)?ntohs(ETH_P_IPX):ntohs(len+sizeof(ipx_packet)),
+ (rt->flags&IPX_RT_ROUTED)?rt->router_node:sipx.sipx_node,
+ NULL,
+ len+sizeof(ipx_packet),
+ skb);
+
+ /* Now the IPX */
+ if(sk->debug)
+ printk("Building IPX Header.\n");
+ ipx=(ipx_packet *)skb->data+skb->dev->hard_header_len;
+ ipx->ipx_checksum=0xFFFF;
+ ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
+ ipx->ipx_tctrl=0;
+ ipx->ipx_type=sk->ipx_type;
+ memcpy(&ipx->ipx_source,&sk->ipx_source_addr,sizeof(ipx->ipx_source));
+ ipx->ipx_dest.net=sipx.sipx_network;
+ memcpy(ipx->ipx_dest.node,sipx.sipx_node,sizeof(ipx->ipx_dest.node));
+ ipx->ipx_dest.sock=sipx.sipx_port;
+ if(sk->debug)
+ printk("IPX: Appending user data.\n");
+ /* User data follows immediately after the IPX data */
+ memcpy_fromfs((char *)(ipx+1),ubuf,len);
+ if(sk->debug)
+ printk("IPX: Transmitting buffer\n");
+ if(dev->flags&IFF_LOOPBACK)
+ /* loop back */
+ ipx_rcv(skb,dev,NULL);
+ else
+ dev_queue_xmit(skb,dev,SOPRI_NORMAL);
+ return len;
+}
+
+static int ipx_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags)
+{
+ return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
+}
+
+static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ return ipx_send(sock,ubuf,size,noblock,0);
+}
+
+static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags, struct sockaddr *sip, int *addr_len)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)sip;
+ /* FILL ME IN */
+ int copied = 0;
+ struct sk_buff *skb;
+ int er;
+
+ if(sk->err)
+ {
+ er= -sk->err;
+ sk->err=0;
+ return er;
+ }
+
+ if(size==0)
+ return 0;
+ if(size<0)
+ return -EINVAL;
+ if(addr_len)
+ {
+ er=verify_area(VERIFY_WRITE,addr_len,sizeof(*addr_len));
+ if(er)
+ return er;
+ put_fs_long(sizeof(*sipx),addr_len);
+ }
+ if(sipx)
+ {
+ er=verify_area(VERIFY_WRITE,sipx,sizeof(*sipx));
+ if(er)
+ return er;
+ }
+ er=verify_area(VERIFY_WRITE,ubuf,size);
+ if(er)
+ return er;
+ skb=skb_recv_datagram(sk,flags,noblock,&er);
+ if(skb==NULL)
+ return er;
+ copied=(size<skb->len)?size:skb->len;
+ skb_copy_datagram(skb,sizeof(struct ipx_packet),ubuf,copied);
+ sk->stamp=skb->stamp;
+
+ if(sipx)
+ {
+ struct sockaddr_ipx addr;
+
+ addr.sipx_family=AF_IPX;
+ addr.sipx_port=((ipx_packet*)skb->h.raw)->ipx_source.sock;
+ memcpy(addr.sipx_node,((ipx_packet*)skb->h.raw)->ipx_source.node,sizeof(addr.sipx_node));
+ addr.sipx_network=((ipx_packet*)skb->h.raw)->ipx_source.net;
+ memcpy_tofs(sipx,&addr,sizeof(*sipx));
+ }
+ skb_free_datagram(skb);
+ return(copied);
+}
+
+static int ipx_recv(struct socket *sock, void *ubuf, int size , int noblock,
+ unsigned flags)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+ if(sk->zapped)
+ return -ENOTCONN;
+ return ipx_recvfrom(sock,ubuf,size,noblock,flags,NULL, NULL);
+}
+
+static int ipx_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ return ipx_recv(sock,ubuf,size,noblock,0);
+}
+
+
+static int ipx_shutdown(struct socket *sk,int how)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ipx_select(struct socket *sock , int sel_type, select_table *wait)
+{
+ ipx_socket *sk=(ipx_socket *)sock->data;
+
+ return datagram_select(sk,sel_type,wait);
+}
+
+static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+
+ switch(cmd)
+ {
+ case SIOCADDRT:
+ case SIOCDELRT:
+ if(!suser())
+ return -EPERM;
+ return(ipxrtr_ioctl(cmd,(void *)arg));
+ default:
+ return -EINVAL;
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+static struct proto_ops ipx_proto_ops = {
+ AF_IPX,
+
+ ipx_create,
+ ipx_dup,
+ ipx_release,
+ ipx_bind,
+ ipx_connect,
+ ipx_socketpair,
+ ipx_accept,
+ ipx_getname,
+ ipx_read,
+ ipx_write,
+ ipx_select,
+ ipx_ioctl,
+ ipx_listen,
+ ipx_send,
+ ipx_recv,
+ ipx_sendto,
+ ipx_recvfrom,
+ ipx_shutdown,
+ ipx_setsockopt,
+ ipx_getsockopt,
+ ipx_fcntl,
+};
+
+/* Called by ddi.c on kernel start up */
+
+static struct packet_type ipx_8023_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_8023),*/
+ 0, /* copy */
+ ipx_rcv,
+ NULL,
+ NULL,
+};
+
+static struct packet_type ipx_dix_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_IPX),*/
+ 0, /* copy */
+ ipx_rcv,
+ NULL,
+ NULL,
+};
+
+
+void ipx_proto_init(struct ddi_proto *pro)
+{
+ (void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+ ipx_dix_packet_type.type=htons(ETH_P_IPX);
+ dev_add_pack(&ipx_dix_packet_type);
+ ipx_8023_packet_type.type=htons(ETH_P_802_3);
+ dev_add_pack(&ipx_8023_packet_type);
+
+ printk("Swansea University Computer Society IPX 0.24 BETA for NET3 ALPHA.008\n");
+
+}
+
+#endif
--- /dev/null
+
+/*
+ * The following information is in its entirety obtained from:
+ *
+ * Novell 'IPX Router Specification' Version 1.10
+ * Part No. 107-000029-001
+ *
+ * Which is available from ftp.novell.com
+ */
+
+#ifndef _IPX_H
+#define _IPX_H
+
+#include <linux/ddi.h>
+
+typedef struct
+{
+ unsigned long net;
+ unsigned char node[6];
+ unsigned short sock;
+} ipx_address;
+
+#define ipx_broadcast_node "\377\377\377\377\377\377"
+
+typedef struct ipx_packet
+{
+ unsigned short ipx_checksum;
+#define IPX_NO_CHECKSUM 0xFFFF
+ unsigned short ipx_pktsize;
+ unsigned char ipx_tctrl;
+ unsigned char ipx_type;
+#define IPX_TYPE_UNKNOWN 0x00
+#define IPX_TYPE_RIP 0x01 /* may also be 0 */
+#define IPX_TYPE_SAP 0x04 /* may also be 0 */
+#define IPX_TYPE_SPX 0x05 /* Not yet implemented */
+#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */
+#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */
+ ipx_address ipx_dest __attribute__ ((packed));
+ ipx_address ipx_source __attribute__ ((packed));
+} ipx_packet;
+
+
+typedef struct ipx_route
+{
+ unsigned long net;
+ unsigned char router_node[6];
+ unsigned long router_net;
+ unsigned short flags;
+ struct device *dev;
+#define IPX_RT_ROUTED 1 /* This isn't a direct route. Send via this if to node router_node */
+#define IPX_RT_BLUEBOOK 2 /* Use DIX 8137 frames not IEE802.3 */
+ struct ipx_route *next;
+} ipx_route;
+
+
+typedef struct sock ipx_socket;
+
+
+#include "ipxcall.h"
+extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+
+
+
+#endif
--- /dev/null
+/* Seperate to keep compilation of Space.c simpler */
+extern void ipx_proto_init(struct ddi_proto *pro);
+++ /dev/null
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * Pseudo-driver for the loopback interface.
- *
- * Version: @(#)loopback.c 1.0.4b 08/16/93
- *
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- * Donald Becker, <becker@super.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
-#include <linux/in.h>
-#include <linux/if_ether.h> /* For the statistics structure. */
-
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/io.h>
-
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
-
-
-static int
-loopback_xmit(struct sk_buff *skb, struct device *dev)
-{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
- int done;
-
- DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
- if (skb == NULL || dev == NULL) return(0);
-
- cli();
- if (dev->tbusy != 0) {
- sti();
- stats->tx_errors++;
- return(1);
- }
- dev->tbusy = 1;
- sti();
-
- done = dev_rint(skb->data, skb->len, 0, dev);
- if (skb->free) kfree_skb(skb, FREE_WRITE);
-
- while (done != 1) {
- done = dev_rint(NULL, 0, 0, dev);
- }
- stats->tx_packets++;
-
- dev->tbusy = 0;
-
-#if 1
- __asm__("cmpl $0,_intr_count\n\t"
- "jne 1f\n\t"
- "movl _bh_active,%%eax\n\t"
- "testl _bh_mask,%%eax\n\t"
- "je 1f\n\t"
- "incl _intr_count\n\t"
- "call _do_bottom_half\n\t"
- "decl _intr_count\n"
- "1:"
- :
- :
- : "ax", "dx", "cx");
-#endif
-
- return(0);
-}
-
-static struct enet_statistics *
-get_stats(struct device *dev)
-{
- return (struct enet_statistics *)dev->priv;
-}
-
-/* Initialize the rest of the LOOPBACK device. */
-int
-loopback_init(struct device *dev)
-{
- dev->mtu = 2000; /* MTU */
- dev->tbusy = 0;
- dev->hard_start_xmit = loopback_xmit;
- dev->open = NULL;
-#if 1
- dev->hard_header = eth_header;
- dev->add_arp = NULL;
- dev->hard_header_len = ETH_HLEN; /* 14 */
- dev->addr_len = ETH_ALEN; /* 6 */
- dev->type = ARPHRD_ETHER; /* 0x0001 */
- dev->type_trans = eth_type_trans;
- dev->rebuild_header = eth_rebuild_header;
-#else
- dev->hard_header_length = 0;
- dev->add_arp = NULL;
- dev->addr_len = 0;
- dev->type = 0; /* loopback_type (0) */
- dev->hard_header = NULL;
- dev->type_trans = NULL;
- dev->rebuild_header = NULL;
-#endif
- dev->queue_xmit = dev_queue_xmit;
-
- /* New-style flags. */
- dev->flags = IFF_LOOPBACK;
- dev->family = AF_INET;
- dev->pa_addr = in_aton("127.0.0.1");
- dev->pa_brdaddr = in_aton("127.255.255.255");
- dev->pa_mask = in_aton("255.0.0.0");
- dev->pa_alen = sizeof(unsigned long);
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct enet_statistics));
- dev->get_stats = get_stats;
-
- return(0);
-};
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
*
* Fixes:
* Alan Cox : verify_area() now used correctly
* from all old Linux datagram code.
* Alan Cox : Uses the improved datagram code.
* Alan Cox : Added NULL's for socket options.
+ * Alan Cox : Re-commented the code.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
*/
+
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/segment.h>
-#include "udp.h"
-#include "raw.h"
+/*
+ * We really ought to have a single public _inline_ min function!
+ */
-static unsigned long
-min(unsigned long a, unsigned long b)
+static unsigned long min(unsigned long a, unsigned long b)
{
- if (a < b) return(a);
- return(b);
+ if (a < b)
+ return(a);
+ return(b);
}
-/* This should be the easiest of all, all we do is copy it into a buffer. */
-int
-packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+/*
+ * This should be the easiest of all, all we do is copy it into a buffer.
+ */
+
+int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
- struct sock *sk;
-
- sk = (struct sock *) pt->data;
- skb->dev = dev;
- skb->len += dev->hard_header_len;
-
- skb->sk = sk;
-
- /* Charge it too the socket. */
- if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ struct sock *sk;
+
+ /*
+ * When we registered the protcol we saved the socket in the data
+ * field for just this event.
+ */
+
+ sk = (struct sock *) pt->data;
+
+ /*
+ * The SOCK_PACKET socket receives _all_ frames, and as such
+ * therefore needs to put the header back onto the buffer.
+ * (it was removed by inet_bh()).
+ */
+
+ skb->dev = dev;
+ skb->len += dev->hard_header_len;
+
+ skb->sk = sk;
+
+ /*
+ * Charge the memory to the socket. This is done specificially
+ * to prevent sockets using all the memory up.
+ */
+
+ if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
+ {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+ sk->rmem_alloc += skb->mem_len;
+
+ /*
+ * Queue the packet up, and wake anyone waiting for it.
+ */
+
+ skb_queue_tail(&sk->receive_queue,skb);
+ wake_up_interruptible(sk->sleep);
+
+ /*
+ * Processing complete.
+ */
+
+ release_sock(sk); /* This is now effectively surplus in this layer */
return(0);
- }
- sk->rmem_alloc += skb->mem_len;
- skb_queue_tail(&sk->rqueue,skb);
- wake_up_interruptible(sk->sleep);
- release_sock(sk);
- return(0);
}
-/* This will do terrible things if len + ipheader + devheader > dev->mtu */
-static int
-packet_sendto(struct sock *sk, unsigned char *from, int len,
+/*
+ * Output a raw packet to a device layer. This bypasses all the other
+ * protocol layers and you must therefore supply it with a complete frame
+ */
+
+static int packet_sendto(struct sock *sk, unsigned char *from, int len,
int noblock, unsigned flags, struct sockaddr_in *usin,
int addr_len)
{
- struct sk_buff *skb;
- struct device *dev;
- struct sockaddr saddr;
- int err;
-
- /* Check the flags. */
- if (flags) return(-EINVAL);
- if (len < 0) return(-EINVAL);
-
- /* Get and verify the address. */
- if (usin) {
- if (addr_len < sizeof(saddr)) return(-EINVAL);
- err=verify_area(VERIFY_READ, usin, sizeof(saddr));
+ struct sk_buff *skb;
+ struct device *dev;
+ struct sockaddr saddr;
+ int err;
+
+ /*
+ * Check the flags.
+ */
+
+ if (flags)
+ return(-EINVAL);
+ if (len < 0)
+ return(-EINVAL);
+
+ /*
+ * Get and verify the address.
+ */
+
+ if (usin)
+ {
+ if (addr_len < sizeof(saddr))
+ return(-EINVAL);
+ err=verify_area(VERIFY_READ, usin, sizeof(saddr));
+ if(err)
+ return err;
+ memcpy_fromfs(&saddr, usin, sizeof(saddr));
+ }
+ else
+ return(-EINVAL); /* SOCK_PACKET must be sent giving an address */
+
+
+ /*
+ * Check the buffer is readable.
+ */
+
+ err=verify_area(VERIFY_READ,from,len);
if(err)
- return err;
- memcpy_fromfs(&saddr, usin, sizeof(saddr));
- } else
- return(-EINVAL);
+ return(err);
+
+ /*
+ * Find the device first to size check it
+ */
+
+ saddr.sa_data[13] = 0;
+ dev = dev_get(saddr.sa_data);
+ if (dev == NULL)
+ {
+ return(-ENXIO);
+ }
- err=verify_area(VERIFY_READ,from,len);
- if(err)
- return(err);
-/* Find the device first to size check it */
-
- saddr.sa_data[13] = 0;
- dev = dev_get(saddr.sa_data);
- if (dev == NULL) {
- return(-ENXIO);
- }
- if(len>dev->mtu)
- return -EMSGSIZE;
-
-/* Now allocate the buffer, knowing 4K pagelimits wont break this line */
- skb = sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL);
-
- /* This shouldn't happen, but it could. */
- if (skb == NULL) {
- DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
- return(-ENOMEM);
- }
- /* Fill it in */
- skb->mem_addr = skb;
- skb->mem_len = len + sizeof(*skb);
- skb->sk = sk;
- skb->free = 1;
- memcpy_fromfs(skb->data, from, len);
- skb->len = len;
- skb->next = NULL;
- skb->arp = 1;
- if (dev->flags & IFF_UP) dev->queue_xmit(skb, dev, sk->priority);
- else kfree_skb(skb, FREE_WRITE);
- return(len);
+ /*
+ * You may not queue a frame bigger than the mtu. This is the lowest level
+ * raw protocol and you must do your own fragmentation at this level.
+ */
+
+ if(len>dev->mtu)
+ return -EMSGSIZE;
+
+ /*
+ * Now allocate the buffer, knowing 4K pagelimits wont break this line.
+ */
+
+ skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL);
+
+ /*
+ * If the write buffer is full, then tough. At this level the user gets to
+ * deal with the problem.
+ */
+
+ if (skb == NULL)
+ {
+ DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
+ return(-ENOMEM);
+ }
+
+ /*
+ * Fill it in
+ */
+
+ skb->sk = sk;
+ skb->free = 1;
+ memcpy_fromfs(skb->data, from, len);
+ skb->len = len;
+ skb->arp = 1; /* No ARP needs doing on this (complete) frame */
+
+ /*
+ * Now send it
+ */
+
+ if (dev->flags & IFF_UP)
+ dev_queue_xmit(skb, dev, sk->priority);
+ else
+ kfree_skb(skb, FREE_WRITE);
+ return(len);
}
+/*
+ * A write to a SOCK_PACKET can't actually do anything useful and will
+ * always fail but we include it for completeness and future expansion.
+ */
-static int
-packet_write(struct sock *sk, unsigned char *buff,
+static int packet_write(struct sock *sk, unsigned char *buff,
int len, int noblock, unsigned flags)
{
- return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0));
+ return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0));
}
+/*
+ * Close a SOCK_PACKET socket. This is fairly simple. We immediately go
+ * to 'closed' state and remove our protocol entry in the device list.
+ * The release_sock() will destroy the socket if a user has closed the
+ * file side of the object.
+ */
-static void
-packet_close(struct sock *sk, int timeout)
+static void packet_close(struct sock *sk, int timeout)
{
- sk->inuse = 1;
- sk->state = TCP_CLOSE;
- dev_remove_pack((struct packet_type *)sk->pair);
- kfree_s((void *)sk->pair, sizeof(struct packet_type));
- sk->pair = NULL;
- release_sock(sk);
+ sk->inuse = 1;
+ sk->state = TCP_CLOSE;
+ dev_remove_pack((struct packet_type *)sk->pair);
+ kfree_s((void *)sk->pair, sizeof(struct packet_type));
+ sk->pair = NULL;
+ release_sock(sk);
}
+/*
+ * Create a packet of type SOCK_PACKET. We do one slightly irregular
+ * thing here that wants tidying up. We borrow the 'pair' pointer in
+ * the socket object so we can find the packet_type entry in the
+ * device list. The reverse is easy as we use the data field of the
+ * packet type to point to our socket.
+ */
-static int
-packet_init(struct sock *sk)
+static int packet_init(struct sock *sk)
{
- struct packet_type *p;
+ struct packet_type *p;
- p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
- if (p == NULL) return(-ENOMEM);
+ p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return(-ENOMEM);
- p->func = packet_rcv;
- p->type = sk->num;
- p->data = (void *)sk;
- dev_add_pack(p);
+ p->func = packet_rcv;
+ p->type = sk->num;
+ p->data = (void *)sk;
+ dev_add_pack(p);
- /* We need to remember this somewhere. */
- sk->pair = (struct sock *)p;
+ /*
+ * We need to remember this somewhere.
+ */
+
+ sk->pair = (struct sock *)p;
- return(0);
+ return(0);
}
/*
- * This should be easy, if there is something there
- * we return it, otherwise we block.
+ * Pull a packet from our receive queue and hand it to the user.
+ * If neccessary we block.
*/
-int
-packet_recvfrom(struct sock *sk, unsigned char *to, int len,
+
+int packet_recvfrom(struct sock *sk, unsigned char *to, int len,
int noblock, unsigned flags, struct sockaddr_in *sin,
int *addr_len)
{
- int copied=0;
- struct sk_buff *skb;
- struct sockaddr *saddr;
- int err;
-
- saddr = (struct sockaddr *)sin;
- if (len == 0) return(0);
- if (len < 0) return(-EINVAL);
-
- if (sk->shutdown & RCV_SHUTDOWN) return(0);
- if (addr_len) {
- err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
- if(err)
- return err;
- put_fs_long(sizeof(*saddr), addr_len);
- }
-
- err=verify_area(VERIFY_WRITE,to,len);
- if(err)
- return err;
- skb=skb_recv_datagram(sk,flags,noblock,&err);
- if(skb==NULL)
- return err;
- copied = min(len, skb->len);
-
- memcpy_tofs(to, skb->data, copied); /* Don't use skb_copy_datagram here: We can't get frag chains */
-
- /* Copy the address. */
- if (saddr) {
- struct sockaddr addr;
-
- addr.sa_family = skb->dev->type;
- memcpy(addr.sa_data,skb->dev->name, 14);
- verify_area(VERIFY_WRITE, saddr, sizeof(*saddr));
- memcpy_tofs(saddr, &addr, sizeof(*saddr));
- }
-
- skb_free_datagram(skb); /* Its either been used up, or its a peek_copy anyway */
-
- release_sock(sk);
- return(copied);
+ int copied=0;
+ struct sk_buff *skb;
+ struct sockaddr *saddr;
+ int err;
+
+ saddr = (struct sockaddr *)sin;
+ if (len == 0)
+ return(0);
+ if (len < 0)
+ return(-EINVAL);
+
+ if (sk->shutdown & RCV_SHUTDOWN)
+ return(0);
+
+ /*
+ * If the address length field is there to be filled in, we fill
+ * it in now.
+ */
+
+ if (addr_len)
+ {
+ err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+ if(err)
+ return err;
+ put_fs_long(sizeof(*saddr), addr_len);
+ }
+
+ if(saddr)
+ {
+ err=verify_area(VERIFY_WRITE, saddr, sizeof(*saddr));
+ if(err)
+ return err;
+ }
+
+ /*
+ * Check the user given area can be written to.
+ */
+
+ err=verify_area(VERIFY_WRITE,to,len);
+ if(err)
+ return err;
+
+ /*
+ * Call the generic datagram receiver. This handles all sorts
+ * of horrible races and re-entrancy so we can forget about it
+ * in the protocol layers.
+ */
+
+ skb=skb_recv_datagram(sk,flags,noblock,&err);
+
+ /*
+ * An error occured so return it. Because skb_recv_datagram()
+ * handles the blocking we don't see and worry about blocking
+ * retries.
+ */
+
+ if(skb==NULL)
+ return err;
+
+ /*
+ * You lose any data beyond the buffer you gave. If it worries a
+ * user program they can ask the device for its MTU anyway.
+ */
+
+ copied = min(len, skb->len);
+
+ memcpy_tofs(to, skb->data, copied); /* We can't use skb_copy_datagram here */
+
+ /*
+ * Copy the address.
+ */
+
+ if (saddr)
+ {
+ struct sockaddr addr;
+
+ addr.sa_family = skb->dev->type;
+ memcpy(addr.sa_data,skb->dev->name, 14);
+ memcpy_tofs(saddr, &addr, sizeof(*saddr));
+ }
+
+ /*
+ * Free or return the buffer as appropriate. Again this hides all the
+ * races and re-entrancy issues from us.
+ */
+
+ skb_free_datagram(skb);
+
+ /*
+ * We are done.
+ */
+
+ release_sock(sk);
+ return(copied);
}
-int
-packet_read(struct sock *sk, unsigned char *buff,
+/*
+ * A packet read can succeed and is just the same as a recvfrom but without the
+ * addresses being recorded.
+ */
+
+int packet_read(struct sock *sk, unsigned char *buff,
int len, int noblock, unsigned flags)
{
- return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+ return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
}
-struct proto packet_prot = {
- sock_wmalloc,
- sock_rmalloc,
- sock_wfree,
- sock_rfree,
- sock_rspace,
- sock_wspace,
- packet_close,
- packet_read,
- packet_write,
- packet_sendto,
- packet_recvfrom,
- ip_build_header,
- udp_connect,
- NULL,
- ip_queue_xmit,
- ip_retransmit,
- NULL,
- NULL,
- NULL,
- datagram_select,
- NULL,
- packet_init,
- NULL,
- NULL, /* No set/get socket options */
- NULL,
- 128,
- 0,
- {NULL,},
- "PACKET"
+/*
+ * This structure declares to the lower layer socket subsystem currently
+ * incorrectly embedded in the IP code how to behave. This interface needs
+ * a lot of work and will change.
+ */
+
+struct proto packet_prot =
+{
+ sock_wmalloc,
+ sock_rmalloc,
+ sock_wfree,
+ sock_rfree,
+ sock_rspace,
+ sock_wspace,
+ packet_close,
+ packet_read,
+ packet_write,
+ packet_sendto,
+ packet_recvfrom,
+ ip_build_header, /* Not actually used */
+ NULL,
+ NULL,
+ ip_queue_xmit, /* These two are not actually used */
+ ip_retransmit,
+ NULL,
+ NULL,
+ NULL,
+ datagram_select,
+ NULL,
+ packet_init,
+ NULL,
+ NULL, /* No set/get socket options */
+ NULL,
+ 128,
+ 0,
+ {NULL,},
+ "PACKET"
};
#include <linux/un.h>
#include <linux/in.h>
#include <linux/param.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
#include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "raw.h"
* happens, get__netinfo returns only part of the available infos.
*/
static int
-get__netinfo(struct proto *pro, char *buffer, int format)
+get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
{
struct sock **s_array;
struct sock *sp;
- char *pos=buffer;
int i;
int timer_active;
unsigned long dest, src;
unsigned short destp, srcp;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+
s_array = pro->sock_array;
- pos+=sprintf(pos, "sl local_address rem_address st tx_queue rx_queue tr tm->when uid\n");
+ len+=sprintf(buffer, "sl local_address rem_address st tx_queue rx_queue tr tm->when uid\n");
/*
* This was very pretty but didn't work when a socket is destroyed at the wrong moment
* (eg a syn recv socket getting a reset), or a memory timer destroy. Instead of playing
timer_active = del_timer(&sp->timer);
if (!timer_active)
sp->timer.expires = 0;
- pos+=sprintf(pos, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
+ len+=sprintf(buffer+len, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
i, src, srcp, dest, destp, sp->state,
format==0?sp->write_seq-sp->rcv_ack_seq:sp->rmem_alloc,
format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc,
SOCK_INODE(sp->socket)->i_uid);
if (timer_active)
add_timer(&sp->timer);
- /* Is place in buffer too rare? then abort. */
- if (pos > buffer+PAGE_SIZE-90) {
- printk("oops, too many %s sockets for netinfo.\n",
- pro->name);
- return(strlen(buffer));
- }
-
/*
* All sockets with (port mod SOCK_ARRAY_SIZE) = i
* are kept in sock_array[i], so we must follow the
* 'next' link to get them all.
*/
sp = sp->next;
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
}
sti(); /* We only turn interrupts back on for a moment, but because the interrupt queues anything built up
before this will clear before we jump back and cli, so its not as bad as it looks */
+ if(pos>offset+length)
+ break;
}
- return(strlen(buffer));
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
-int tcp_get_info(char *buffer)
+int tcp_get_info(char *buffer, char **start, off_t offset, int length)
{
- return get__netinfo(&tcp_prot, buffer,0);
+ return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
}
-int udp_get_info(char *buffer)
+int udp_get_info(char *buffer, char **start, off_t offset, int length)
{
- return get__netinfo(&udp_prot, buffer,1);
+ return get__netinfo(&udp_prot, buffer,1, start, offset, length);
}
-int raw_get_info(char *buffer)
+int raw_get_info(char *buffer, char **start, off_t offset, int length)
{
- return get__netinfo(&raw_prot, buffer,1);
+ return get__netinfo(&raw_prot, buffer,1, start, offset, length);
}
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "icmp.h"
#include "udp.h"
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
+#if 0
#include "tcp.h"
-#include "skbuff.h"
+#endif
+#include <linux/skbuff.h>
#include "sock.h"
#include "icmp.h"
#include "udp.h"
" len=%d, saddr=%X, redo=%d, protocol=%X)\n",
skb, dev, opt, daddr, len, saddr, redo, protocol));
- if (skb == NULL) return(0);
- if (protocol == NULL) {
+ if (skb == NULL)
+ return(0);
+
+ if (protocol == NULL)
+ {
kfree_skb(skb, FREE_READ);
return(0);
}
+
sk = (struct sock *) protocol->data;
- if (sk == NULL) {
+ if (sk == NULL)
+ {
kfree_skb(skb, FREE_READ);
return(0);
}
/* Charge it too the socket. */
if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
- skb->sk = NULL;
+ ip_statistics.IpInDiscards++;
+ skb->sk=NULL;
kfree_skb(skb, FREE_READ);
return(0);
}
sk->rmem_alloc += skb->mem_len;
- skb_queue_tail(&sk->rqueue,skb);
+ ip_statistics.IpInDelivers++;
+ skb_queue_tail(&sk->receive_queue,skb);
sk->data_ready(sk,skb->len);
release_sock(sk);
return(0);
}
if (sin.sin_port == 0) sin.sin_port = sk->protocol;
- if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+ if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
return -EACCES;
sk->inuse = 1;
}
skb = sk->prot->wmalloc(sk,
- len+sizeof(*skb) + sk->prot->max_header,
+ len + sk->prot->max_header,
0, GFP_KERNEL);
if (skb == NULL) {
int tmp;
sti();
}
}
- skb->mem_addr = skb;
- skb->mem_len = len + sizeof(*skb) +sk->prot->max_header;
skb->sk = sk;
- skb->free = 1; /* these two should be unecessary. */
- skb->arp = 0;
+ skb->free = 1;
tmp = sk->prot->build_header(skb, sk->saddr,
sin.sin_addr.s_addr, &dev,
}
skb->len = tmp + len;
+
sk->prot->queue_xmit(sk, dev, skb, 1);
release_sock(sk);
return(len);
copied = min(len, skb->len);
skb_copy_datagram(skb, 0, to, copied);
+ sk->stamp=skb->stamp;
/* Copy the address. */
if (sin) {
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Linus Torvalds, <Linus.Torvalds@helsinki.fi>
*
* Fixes:
* Alan Cox : Verify area fixes.
* Rui Oliveira : ICMP routing table updates
* (rco@di.uminho.pt) Routing table insertion and update
* Linus Torvalds : Rewrote bits to be sensible
+ * Alan Cox : Added BSD route gw semantics
+ * Alan Cox : Super /proc >4K
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "route.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
-#include "arp.h"
#include "icmp.h"
+/*
+ * The routing table list
+ */
static struct rtable *rt_base = NULL;
+
+/*
+ * Pointer to the loopback route
+ */
+
static struct rtable *rt_loopback = NULL;
-/* Dump the contents of a routing table entry. */
-static void
-rt_print(struct rtable *rt)
+/*
+ * Dump the contents of a routing table entry.
+ */
+
+static void rt_print(struct rtable *rt)
{
- if (rt == NULL || inet_debug != DBG_RT) return;
- printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
+ if (rt == NULL || inet_debug != DBG_RT)
+ return;
+
+ printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
(long) rt, (long) rt->rt_next, rt->rt_flags);
- printk(" TARGET=%s ", in_ntoa(rt->rt_dst));
- printk("GW=%s ", in_ntoa(rt->rt_gateway));
- printk(" DEV=%s USE=%ld REF=%d\n",
- (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
- rt->rt_use, rt->rt_refcnt);
+ printk(" TARGET=%s ", in_ntoa(rt->rt_dst));
+ printk("GW=%s ", in_ntoa(rt->rt_gateway));
+ printk(" DEV=%s USE=%ld REF=%d\n",
+ (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
+ rt->rt_use, rt->rt_refcnt);
}
/*
- * Remove a routing table entry.
+ * Remove a routing table entry.
*/
+
static void rt_del(unsigned long dst)
{
struct rtable *r, **rp;
DPRINTF((DBG_RT, "RT: flushing for dst %s\n", in_ntoa(dst)));
rp = &rt_base;
+
+ /*
+ * This must be done with interrupts off because we could take
+ * an ICMP_REDIRECT.
+ */
+
save_flags(flags);
cli();
- while((r = *rp) != NULL) {
- if (r->rt_dst != dst) {
+ while((r = *rp) != NULL)
+ {
+ if (r->rt_dst != dst)
+ {
rp = &r->rt_next;
continue;
}
*rp = r->rt_next;
+
+ /*
+ * If we delete the loopback route update its pointer.
+ */
+
if (rt_loopback == r)
rt_loopback = NULL;
kfree_s(r, sizeof(struct rtable));
/*
- * Remove all routing table entries for a device.
+ * Remove all routing table entries for a device. This is called when
+ * a device is downed.
*/
-void rt_flush(struct device *dev)
+
+void ip_rt_flush(struct device *dev)
{
struct rtable *r;
struct rtable **rp;
}
/*
- * Used by 'rt_add()' when we can't get the netmask any other way..
+ * Used by 'rt_add()' when we can't get the netmask any other way..
*
- * If the lower byte or two are zero, we guess the mask based on the
- * number of zero 8-bit net numbers, otherwise we use the "default"
- * masks judging by the destination address and our device netmask.
+ * If the lower byte or two are zero, we guess the mask based on the
+ * number of zero 8-bit net numbers, otherwise we use the "default"
+ * masks judging by the destination address and our device netmask.
*/
+
static inline unsigned long default_mask(unsigned long dst)
{
dst = ntohl(dst);
return htonl(IN_CLASSC_NET);
}
+
+/*
+ * If no mask is specified then generate a default entry.
+ */
+
static unsigned long guess_mask(unsigned long dst, struct device * dev)
{
unsigned long mask;
return dev->pa_mask;
}
+
+/*
+ * Find the route entry through which our gateway will be reached
+ */
+
static inline struct device * get_gw_dev(unsigned long gw)
{
struct rtable * rt;
- for (rt = rt_base ; ; rt = rt->rt_next) {
+ for (rt = rt_base ; ; rt = rt->rt_next)
+ {
if (!rt)
return NULL;
if ((gw ^ rt->rt_dst) & rt->rt_mask)
continue;
- /* gateways behind gateways are a no-no */
+ /*
+ * Gateways behind gateways are a no-no
+ */
+
if (rt->rt_flags & RTF_GATEWAY)
return NULL;
return rt->rt_dev;
}
/*
- * rewrote rt_add(), as the old one was weird. Linus
+ * Rewrote rt_add(), as the old one was weird - Linus
+ *
+ * This routine is used to update the IP routing table, either
+ * from the kernel (ICMP_REDIRECT) or via an ioctl call issued
+ * by the superuser.
*/
-void rt_add(short flags, unsigned long dst, unsigned long mask,
+
+void ip_rt_add(short flags, unsigned long dst, unsigned long mask,
unsigned long gw, struct device *dev)
{
struct rtable *r, *rt;
struct rtable **rp;
unsigned long cpuflags;
- if (flags & RTF_HOST) {
+ /*
+ * A host is a unique machine and has no network bits.
+ */
+
+ if (flags & RTF_HOST)
+ {
mask = 0xffffffff;
- } else if (!mask) {
- if (!((dst ^ dev->pa_addr) & dev->pa_mask)) {
+ }
+
+ /*
+ * Calculate the network mask
+ */
+
+ else if (!mask)
+ {
+ if (!((dst ^ dev->pa_addr) & dev->pa_mask))
+ {
mask = dev->pa_mask;
flags &= ~RTF_GATEWAY;
- if (flags & RTF_DYNAMIC) {
+ if (flags & RTF_DYNAMIC)
+ {
/*printk("Dynamic route to my own net rejected\n");*/
return;
}
- } else
+ }
+ else
mask = guess_mask(dst, dev);
dst &= mask;
}
+
+ /*
+ * A gateway must be reachable and not a local address
+ */
+
if (gw == dev->pa_addr)
flags &= ~RTF_GATEWAY;
- if (flags & RTF_GATEWAY) {
- /* don't try to add a gateway we can't reach.. */
+
+ if (flags & RTF_GATEWAY)
+ {
+ /*
+ * Don't try to add a gateway we can't reach..
+ */
+
if (dev != get_gw_dev(gw))
return;
+
flags |= RTF_GATEWAY;
- } else
+ }
+ else
gw = 0;
- /* Allocate an entry. */
+
+ /*
+ * Allocate an entry and fill it in.
+ */
+
rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
- if (rt == NULL) {
+ if (rt == NULL)
+ {
DPRINTF((DBG_RT, "RT: no memory for new route!\n"));
return;
}
rt->rt_mask = mask;
rt->rt_mtu = dev->mtu;
rt_print(rt);
+
/*
- * What we have to do is loop though this until we have
- * found the first address which has a higher generality than
- * the one in rt. Then we can put rt in right before it.
+ * What we have to do is loop though this until we have
+ * found the first address which has a higher generality than
+ * the one in rt. Then we can put rt in right before it.
+ * The interrupts must be off for this process.
*/
+
save_flags(cpuflags);
cli();
- /* remove old route if we are getting a duplicate. */
+
+ /*
+ * Remove old route if we are getting a duplicate.
+ */
+
rp = &rt_base;
- while ((r = *rp) != NULL) {
- if (r->rt_dst != dst) {
+ while ((r = *rp) != NULL)
+ {
+ if (r->rt_dst != dst)
+ {
rp = &r->rt_next;
continue;
}
rt_loopback = NULL;
kfree_s(r, sizeof(struct rtable));
}
- /* add the new route */
+
+ /*
+ * Add the new route
+ */
+
rp = &rt_base;
while ((r = *rp) != NULL) {
if ((r->rt_mask & mask) != mask)
}
rt->rt_next = r;
*rp = rt;
+
+ /*
+ * Update the loopback route
+ */
+
if (rt->rt_dev->flags & IFF_LOOPBACK)
rt_loopback = rt;
+
+ /*
+ * Restore the interrupts and return
+ */
+
restore_flags(cpuflags);
return;
}
+
+/*
+ * Check if a mask is acceptable.
+ */
+
static inline int bad_mask(unsigned long mask, unsigned long addr)
{
if (addr & (mask = ~mask))
return 0;
}
+/*
+ * Process a route add request from the user
+ */
+
static int rt_new(struct rtentry *r)
{
int err;
struct device * dev = NULL;
unsigned long flags, daddr, mask, gw;
- if ((devname = r->rt_dev) != NULL) {
+ /*
+ * If a device is specified find it.
+ */
+
+ if ((devname = r->rt_dev) != NULL)
+ {
err = getname(devname, &devname);
if (err)
return err;
if (!dev)
return -EINVAL;
}
+
+ /*
+ * If the device isn't INET, don't allow it
+ */
if (r->rt_dst.sa_family != AF_INET)
return -EAFNOSUPPORT;
+ /*
+ * Make local copies of the important bits
+ */
+
flags = r->rt_flags;
daddr = ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr;
mask = ((struct sockaddr_in *) &r->rt_genmask)->sin_addr.s_addr;
gw = ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr;
-/* BSD emulation: Permits route add someroute gw one-of-my-addresses
- to indicate which iface. Not as clean as the nice Linux dev technique
- but people keep using it... */
- if (!dev && (flags & RTF_GATEWAY)) {
+
+ /*
+ * BSD emulation: Permits route add someroute gw one-of-my-addresses
+ * to indicate which iface. Not as clean as the nice Linux dev technique
+ * but people keep using it...
+ */
+
+ if (!dev && (flags & RTF_GATEWAY))
+ {
struct device *dev2;
- for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next) {
- if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw) {
+ for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next)
+ {
+ if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw)
+ {
flags &= ~RTF_GATEWAY;
dev = dev2;
break;
}
}
+ /*
+ * Ignore faulty masks
+ */
+
if (bad_mask(mask, daddr))
mask = 0;
+ /*
+ * Set the mask to nothing for host routes.
+ */
+
if (flags & RTF_HOST)
mask = 0xffffffff;
else if (mask && r->rt_genmask.sa_family != AF_INET)
return -EAFNOSUPPORT;
- if (flags & RTF_GATEWAY) {
+ /*
+ * You can only gateway IP via IP..
+ */
+
+ if (flags & RTF_GATEWAY)
+ {
if (r->rt_gateway.sa_family != AF_INET)
return -EAFNOSUPPORT;
if (!dev)
dev = get_gw_dev(gw);
- } else if (!dev)
- dev = dev_check(daddr);
+ }
+ else if (!dev)
+ dev = ip_dev_check(daddr);
+ /*
+ * Unknown device.
+ */
+
if (dev == NULL)
return -ENETUNREACH;
- rt_add(flags, daddr, mask, gw, dev);
+ /*
+ * Add the route
+ */
+
+ ip_rt_add(flags, daddr, mask, gw, dev);
return 0;
}
+/*
+ * Remove a route, as requested by the user.
+ */
+
static int rt_kill(struct rtentry *r)
{
struct sockaddr_in *trg;
}
-/* Called from the PROCfs module. */
-int
-rt_get_info(char *buffer)
+/*
+ * Called from the PROCfs module. This outputs /proc/net/route.
+ */
+
+int rt_get_info(char *buffer, char **start, off_t offset, int length)
{
- struct rtable *r;
- char *pos;
-
- pos = buffer;
+ struct rtable *r;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+ int size;
- pos += sprintf(pos,
+ len += sprintf(buffer,
"Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n");
+ pos=len;
- /* This isn't quite right -- r->rt_dst is a struct! */
- for (r = rt_base; r != NULL; r = r->rt_next) {
- pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
- r->rt_dev->name, r->rt_dst, r->rt_gateway,
- r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
- r->rt_mask);
- }
- return(pos - buffer);
+ /*
+ * This isn't quite right -- r->rt_dst is a struct!
+ */
+
+ for (r = rt_base; r != NULL; r = r->rt_next)
+ {
+ size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
+ r->rt_dev->name, r->rt_dst, r->rt_gateway,
+ r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
+ r->rt_mask);
+ len+=size;
+ pos+=size;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
/*
- * This is hackish, but results in better code. Use "-S" to see why.
+ * This is hackish, but results in better code. Use "-S" to see why.
*/
+
#define early_out ({ goto no_route; 1; })
-struct rtable * rt_route(unsigned long daddr, struct options *opt)
+/*
+ * Route a packet. This needs to be fairly quick. Florian & Co.
+ * suggested a unified ARP and IP routing cache. Done right its
+ * probably a brilliant idea. I'd actually suggest a unified
+ * ARP/IP routing/Socket pointer cache. Volunteers welcome
+ */
+
+struct rtable * ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr)
{
struct rtable *rt;
- for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) {
+ for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next)
+ {
if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
break;
- /* broadcast addresses can be special cases.. */
+ /*
+ * broadcast addresses can be special cases..
+ */
+
if ((rt->rt_dev->flags & IFF_BROADCAST) &&
rt->rt_dev->pa_brdaddr == daddr)
break;
}
+
+ if(src_addr!=NULL)
+ *src_addr= rt->rt_dev->pa_addr;
+
if (daddr == rt->rt_dev->pa_addr) {
if ((rt = rt_loopback) == NULL)
goto no_route;
return NULL;
}
-static int get_old_rtent(struct old_rtentry * src, struct rtentry * rt)
+/*
+ * Backwards compatibility
+ */
+
+static int ip_get_old_rtent(struct old_rtentry * src, struct rtentry * rt)
{
int err;
struct old_rtentry tmp;
((struct sockaddr_in *) &rt->rt_genmask)->sin_addr.s_addr = tmp.rt_genmask;
rt->rt_flags = tmp.rt_flags;
rt->rt_dev = tmp.rt_dev;
+ printk("Warning: obsolete routing request made.\n");
return 0;
}
-int rt_ioctl(unsigned int cmd, void *arg)
+/*
+ * Handle IP routing ioctl calls. These are used to manipulate the routing tables
+ */
+
+int ip_rt_ioctl(unsigned int cmd, void *arg)
{
int err;
struct rtentry rt;
- switch(cmd) {
- case DDIOCSDBG:
- return dbg_ioctl(arg, DBG_RT);
- case SIOCADDRTOLD:
- case SIOCDELRTOLD:
- if (!suser())
- return -EPERM;
- err = get_old_rtent((struct old_rtentry *) arg, &rt);
- if (err)
- return err;
- return (cmd == SIOCDELRTOLD) ? rt_kill(&rt) : rt_new(&rt);
- case SIOCADDRT:
- case SIOCDELRT:
- if (!suser())
- return -EPERM;
- err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry));
- if (err)
- return err;
- memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
- return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
+ switch(cmd)
+ {
+ case DDIOCSDBG: /* Control debugging */
+ return dbg_ioctl(arg, DBG_RT);
+
+ case SIOCADDRTOLD: /* Old style add route */
+ case SIOCDELRTOLD: /* Old style delete route */
+ if (!suser())
+ return -EPERM;
+ err = ip_get_old_rtent((struct old_rtentry *) arg, &rt);
+ if (err)
+ return err;
+ return (cmd == SIOCDELRTOLD) ? rt_kill(&rt) : rt_new(&rt);
+
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!suser())
+ return -EPERM;
+ err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry));
+ if (err)
+ return err;
+ memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
+ return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
}
return -EINVAL;
};
-extern void rt_flush(struct device *dev);
-extern void rt_add(short flags, unsigned long addr, unsigned long mask,
+extern void ip_rt_flush(struct device *dev);
+extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
unsigned long gw, struct device *dev);
-extern struct rtable *rt_route(unsigned long daddr, struct options *opt);
-extern int rt_get_info(char * buffer);
-extern int rt_ioctl(unsigned int cmd, void *arg);
+extern struct rtable *ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr);
+extern int rt_get_info(char * buffer, char **start, off_t offset, int length);
+extern int ip_rt_ioctl(unsigned int cmd, void *arg);
#endif /* _ROUTE_H */
/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * A saner implementation of the skbuff stuff scattered everywhere
- * in the old NET2D code.
+ * Routines having to do with the 'struct sk_buff' memory handlers.
*
* Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
+ * Florian La Roche <rzsfl@rz.uni-sb.de>
*
- * Fixes:
- * Alan Cox : Tracks memory and number of buffers for kernel memory report
- * and memory leak hunting.
- * Alan Cox : More generic kfree handler
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
*/
-
+
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/in.h>
-#include <linux/string.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
-#include "arp.h"
+#include <linux/string.h>
#include "route.h"
#include "tcp.h"
#include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
-/* Socket buffer operations. Ideally much of this list swap stuff ought to be using
- exch instructions on the 386, and CAS/CAS2 on a 68K. This is the boring generic
- slow C version. No doubt when Linus sees this comment he'll do horrible things
- to this code 8-)
-*/
-
/*
* Resource tracking variables
*/
+
+volatile unsigned long net_memory = 0;
+volatile unsigned long net_skbcount = 0;
-volatile unsigned long net_memory=0;
-volatile unsigned long net_skbcount=0;
+
+#if CONFIG_SKB_CHECK
/*
* Debugging paranoia. Can go later when this crud stack works
- */
+ */
+int skb_check(struct sk_buff *skb, int head, int line, char *file)
+{
+ if (head) {
+ if (skb->magic_debug_cookie != SK_HEAD_SKB) {
+ printk("File: %s Line %d, found a bad skb-head\n",
+ file,line);
+ return -1;
+ }
+ if (!skb->next || !skb->prev) {
+ printk("skb_check: head without next or prev\n");
+ return -1;
+ }
+ if (skb->next->magic_debug_cookie != SK_HEAD_SKB
+ && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad next head-skb member\n",
+ file,line);
+ return -1;
+ }
+ if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
+ && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad prev head-skb member\n",
+ file,line);
+ return -1;
+ }
+#if 0
+ {
+ struct sk_buff *skb2 = skb->next;
+ int i = 0;
+ while (skb2 != skb && i < 5) {
+ if (skb_check(skb2, 0, line, file) < 0) {
+ printk("bad queue element in whole queue\n");
+ return -1;
+ }
+ i++;
+ skb2 = skb2->next;
+ }
+ }
+#endif
+ return 0;
+ }
+ if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
+ && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad next skb member\n",
+ file,line);
+ return -1;
+ }
+ if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
+ && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+ printk("File: %s Line %d, bad prev skb member\n",
+ file,line);
+ return -1;
+ }
-void skb_check(struct sk_buff *skb, int line, char *file)
-{
if(skb->magic_debug_cookie==SK_FREED_SKB)
{
printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
file,line);
- printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
- skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
+ printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+ skb,skb->truesize,skb->mem_len,skb->free);
+ return -1;
}
if(skb->magic_debug_cookie!=SK_GOOD_SKB)
{
printk("File: %s Line %d, passed a non skb!\n", file,line);
- printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
- skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
+ printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+ skb,skb->truesize,skb->mem_len,skb->free);
+ return -1;
}
if(skb->mem_len!=skb->truesize)
{
printk("File: %s Line %d, Dubious size setting!\n",file,line);
- printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p\n",
- skb,skb->truesize,skb->mem_len,skb->magic,skb->list);
+ printk("skb=%p, real size=%ld, claimed size=%ld\n",
+ skb,skb->truesize,skb->mem_len);
+ return -1;
}
/* Guess it might be acceptable then */
+ return 0;
+}
+#endif
+
+
+void skb_queue_head_init(struct sk_buff_head *list)
+{
+ list->prev = (struct sk_buff *)list;
+ list->next = (struct sk_buff *)list;
+#if CONFIG_SKB_CHECK
+ list->magic_debug_cookie = SK_HEAD_SKB;
+#endif
}
+
/*
* Insert an sk_buff at the start of a list.
*/
-
-void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk)
+
+void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
{
unsigned long flags;
+ struct sk_buff *list = (struct sk_buff *)list_;
- IS_SKB(newsk);
- if(newsk->list)
- printk("Suspicious queue head: sk_buff on list!\n");
save_flags(flags);
cli();
- newsk->list=list;
- newsk->next=*list;
+#if CONFIG_SKB_CHECK
+ IS_SKB(newsk);
+ IS_SKB_HEAD(list);
+ if (newsk->next || newsk->prev)
+ printk("Suspicious queue head: sk_buff on list!\n");
+#endif
+
+ newsk->next = list->next;
+ newsk->prev = list;
- if(*list)
- newsk->prev=(*list)->prev;
- else
- newsk->prev=newsk;
- newsk->prev->next=newsk;
- newsk->next->prev=newsk;
- IS_SKB(newsk->prev);
- IS_SKB(newsk->next);
- *list=newsk;
+ newsk->next->prev = newsk;
+ newsk->prev->next = newsk;
+
restore_flags(flags);
}
/*
* Insert an sk_buff at the end of a list.
*/
-
-void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
+
+void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
{
unsigned long flags;
+ struct sk_buff *list = (struct sk_buff *)list_;
- if(newsk->list)
- printk("Suspicious queue tail: sk_buff on list!\n");
-
- IS_SKB(newsk);
save_flags(flags);
cli();
- newsk->list=list;
- if(*list)
- {
- (*list)->prev->next=newsk;
- newsk->prev=(*list)->prev;
- newsk->next=*list;
- (*list)->prev=newsk;
- }
- else
- {
- newsk->next=newsk;
- newsk->prev=newsk;
- *list=newsk;
- }
- IS_SKB(newsk->prev);
- IS_SKB(newsk->next);
- restore_flags(flags);
+#if CONFIG_SKB_CHECK
+ if (newsk->next || newsk->prev)
+ printk("Suspicious queue tail: sk_buff on list!\n");
+ IS_SKB(newsk);
+ IS_SKB_HEAD(list);
+#endif
+ newsk->next = list;
+ newsk->prev = list->prev;
+
+ newsk->next->prev = newsk;
+ newsk->prev->next = newsk;
+
+ restore_flags(flags);
}
/*
* so you can grab read and free buffers as another process adds them.
*/
-struct sk_buff *skb_dequeue(struct sk_buff *volatile* list)
+struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
{
long flags;
struct sk_buff *result;
+ struct sk_buff *list = (struct sk_buff *)list_;
save_flags(flags);
cli();
- if(*list==NULL)
- {
+ IS_SKB_HEAD(list);
+
+ result = list->next;
+ if (result == list) {
restore_flags(flags);
- return(NULL);
+ return NULL;
}
+
+ result->next->prev = list;
+ list->next = result->next;
- result=*list;
- if(result->next==result)
- *list=NULL;
- else
- {
- result->next->prev=result->prev;
- result->prev->next=result->next;
- *list=result->next;
- }
+ result->next = NULL;
+ result->prev = NULL;
- IS_SKB(result);
restore_flags(flags);
-
- if(result->list!=list)
- printk("Dequeued packet has invalid list pointer\n");
-
- result->list=0;
- result->next=0;
- result->prev=0;
- return(result);
+
+ return result;
}
/*
* Insert a packet before another one in a list.
*/
-
+
void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
+#if CONFIG_SKB_CHECK
IS_SKB(old);
IS_SKB(newsk);
- if(!old->list)
+ if(!old->next || !old->prev)
printk("insert before unlisted item!\n");
- if(newsk->list)
+ if(newsk->next || newsk->prev)
printk("inserted item is already on a list.\n");
+#endif
save_flags(flags);
cli();
- newsk->list=old->list;
- newsk->next=old;
- newsk->prev=old->prev;
- newsk->next->prev=newsk;
- newsk->prev->next=newsk;
-
+ newsk->next = old;
+ newsk->prev = old->prev;
+ old->prev = newsk;
+ newsk->prev->next = newsk;
+
restore_flags(flags);
}
/*
* Place a packet after a given packet in a list.
*/
-
+
void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
+#if CONFIG_SKB_CHECK
IS_SKB(old);
IS_SKB(newsk);
- if(!old->list)
+ if(!old->next || !old->prev)
printk("append before unlisted item!\n");
- if(newsk->list)
+ if(newsk->next || newsk->prev)
printk("append item is already on a list.\n");
+#endif
save_flags(flags);
cli();
- newsk->list=old->list;
- newsk->prev=old;
- newsk->next=old->next;
- newsk->next->prev=newsk;
- newsk->prev->next=newsk;
+
+ newsk->prev = old;
+ newsk->next = old->next;
+ newsk->next->prev = newsk;
+ old->next = newsk;
restore_flags(flags);
}
* MUST EXIST when you unlink. Thus a list must have its contents unlinked
* _FIRST_.
*/
-
+
void skb_unlink(struct sk_buff *skb)
{
unsigned long flags;
+
save_flags(flags);
cli();
IS_SKB(skb);
- if(skb->list)
+ if(skb->prev && skb->next)
{
- skb->next->prev=skb->prev;
- skb->prev->next=skb->next;
- if(*skb->list==skb)
- {
- if(skb->next==skb)
- *skb->list=NULL;
- else
- *skb->list=skb->next;
- }
- skb->next=0;
- skb->prev=0;
- skb->list=0;
+ skb->next->prev = skb->prev;
+ skb->prev->next = skb->next;
+ skb->next = NULL;
+ skb->prev = NULL;
}
+#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
+ else
+ printk("skb_unlink: not a linked element\n");
+#endif
restore_flags(flags);
}
-/*
- * An skbuff list has had its head reassigned. Move all the list
- * pointers. Must be called with ints off during the whole head
- * shifting
- */
-
-void skb_new_list_head(struct sk_buff *volatile* list)
-{
- struct sk_buff *skb=skb_peek(list);
- if(skb!=NULL)
- {
- do
- {
- IS_SKB(skb);
- skb->list=list;
- skb=skb->next;
- }
- while(skb!=*list);
- }
-}
-
-/*
- * Peek an sk_buff. Unlike most other operations you _MUST_
- * be careful with this one. A peek leaves the buffer on the
- * list and someone else may run off with it. For an interrupt
- * type system cli() peek the buffer copy the data and sti();
- */
-
-struct sk_buff *skb_peek(struct sk_buff *volatile* list)
-{
- return *list;
-}
-
/*
* Get a clone of an sk_buff. This is the safe way to peek at
* a socket queue without accidents. Its a bit long but most
* anyway. Only the memcpy of upto 4K with ints off is not
* as nice as I'd like.
*/
-
-struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
+
+struct sk_buff *skb_peek_copy(struct sk_buff_head *list_)
{
+ struct sk_buff *list = (struct sk_buff *)list_;
struct sk_buff *orig,*newsk;
unsigned long flags;
unsigned int len;
/* Now for some games to avoid races */
+ IS_SKB_HEAD(list);
do
{
save_flags(flags);
cli();
- orig=skb_peek(list);
- if(orig==NULL)
- {
+ orig = list->next;
+ if (orig == list) {
restore_flags(flags);
return NULL;
}
IS_SKB(orig);
- len=orig->truesize;
+ len = orig->truesize;
restore_flags(flags);
- newsk=alloc_skb(len,GFP_KERNEL); /* May sleep */
+ newsk = alloc_skb(len,GFP_KERNEL); /* May sleep */
- if(newsk==NULL) /* Oh dear... not to worry */
+ if (newsk == NULL) /* Oh dear... not to worry */
return NULL;
-
+
save_flags(flags);
cli();
- if(skb_peek(list)!=orig) /* List changed go around another time */
+ if (list->next != orig) /* List changed go around another time */
{
restore_flags(flags);
- newsk->sk=NULL;
- newsk->free=1;
- newsk->mem_addr=newsk;
- newsk->mem_len=len;
+ newsk->sk = NULL;
+ newsk->free = 1;
+ newsk->mem_addr = newsk;
+ newsk->mem_len = len;
kfree_skb(newsk, FREE_WRITE);
continue;
}
-
+
IS_SKB(orig);
IS_SKB(newsk);
memcpy(newsk,orig,len);
- newsk->list=NULL;
- newsk->magic=0;
- newsk->next=NULL;
- newsk->prev=NULL;
- newsk->mem_addr=newsk;
- newsk->h.raw+=((char *)newsk-(char *)orig);
- newsk->link3=NULL;
- newsk->sk=NULL;
- newsk->free=1;
+ newsk->next = NULL;
+ newsk->prev = NULL;
+ newsk->mem_addr = newsk;
+ newsk->h.raw += ((char *)newsk - (char *)orig);
+ newsk->link3 = NULL;
+ newsk->sk = NULL;
+ newsk->free = 1;
}
while(0);
-
+
restore_flags(flags);
- return(newsk);
-}
+ return newsk;
+}
/*
* Free an sk_buff. This still knows about things it should
void kfree_skb(struct sk_buff *skb, int rw)
{
- if (skb == NULL) {
+ if (skb == NULL)
+ {
printk("kfree_skb: skb = NULL\n");
return;
- }
+ }
IS_SKB(skb);
- if(skb->lock)
+ if (skb->lock)
{
- skb->free=1; /* Free when unlocked */
+ skb->free = 1; /* Free when unlocked */
return;
- }
-
- if(skb->free == 2)
+ }
+ if (skb->free == 2)
printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n");
- if(skb->list)
- printk("Warning: kfree_skb passed an skb still on a list.\n");
- skb->magic = 0;
- if (skb->sk)
+ if (skb->next)
+ printk("Warning: kfree_skb passed an skb still on a list.\n");
+ if (skb->sk)
{
- if(skb->sk->prot!=NULL)
+ if(skb->sk->prot!=NULL)
{
if (rw)
- skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
- else
- skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
+ skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
+ else
+ skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
}
else
else
skb->sk->wmem_alloc-=skb->mem_len;
if(!skb->sk->dead)
- wake_up_interruptible(skb->sk->sleep);
+ wake_up_interruptible(skb->sk->sleep);
kfree_skbmem(skb->mem_addr,skb->mem_len);
}
- }
- else
+ }
+ else
kfree_skbmem(skb->mem_addr, skb->mem_len);
}
* Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
* fields and also do memory statistics to find all the [BEEP] leaks.
*/
-
-struct sk_buff *alloc_skb(unsigned int size,int priority)
-{
- struct sk_buff *skb;
-
- if (intr_count && priority != GFP_ATOMIC) {
+
+ struct sk_buff *alloc_skb(unsigned int size,int priority)
+ {
+ struct sk_buff *skb;
+
+ if (intr_count && priority!=GFP_ATOMIC) {
static int count = 0;
if (++count < 5) {
printk("alloc_skb called nonatomically from interrupt %08lx\n",
((unsigned long *)&size)[-1]);
priority = GFP_ATOMIC;
}
- }
- skb=(struct sk_buff *)kmalloc(size,priority);
- if(skb==NULL)
- return NULL;
- skb->free= 2; /* Invalid so we pick up forgetful users */
- skb->list= 0; /* Not on a list */
- skb->lock= 0;
- skb->truesize=size;
- skb->mem_len=size;
- skb->mem_addr=skb;
- skb->fraglist=NULL;
- net_memory+=size;
- net_skbcount++;
- skb->magic_debug_cookie=SK_GOOD_SKB;
- skb->users=0;
- return skb;
+ }
+
+ size+=sizeof(struct sk_buff);
+ skb=(struct sk_buff *)kmalloc(size,priority);
+ if (skb == NULL)
+ return NULL;
+
+ skb->free = 2; /* Invalid so we pick up forgetful users */
+ skb->lock = 0;
+ skb->truesize = size;
+ skb->mem_len = size;
+ skb->mem_addr = skb;
+ skb->fraglist = NULL;
+ skb->prev = skb->next = NULL;
+ skb->link3 = NULL;
+ skb->sk = NULL;
+ skb->stamp.tv_sec=0; /* No idea about time */
+ net_memory += size;
+ net_skbcount++;
+#if CONFIG_SKB_CHECK
+ skb->magic_debug_cookie = SK_GOOD_SKB;
+#endif
+ skb->users = 0;
+ return skb;
}
/*
* Free an skbuff by memory
- */
+ */
void kfree_skbmem(void *mem,unsigned size)
{
- struct sk_buff *x=(struct sk_buff *) mem;
+#if CONFIG_SKB_CHECK
+ struct sk_buff *x = mem;
IS_SKB(x);
- if(x->magic_debug_cookie==SK_GOOD_SKB)
+ if(x->magic_debug_cookie == SK_GOOD_SKB)
{
- x->magic_debug_cookie=SK_FREED_SKB;
+ x->magic_debug_cookie = SK_FREED_SKB;
kfree_s(mem,size);
net_skbcount--;
- net_memory-=size;
+ net_memory -= size;
}
+ else
+ printk("kfree_skbmem: bad magic cookie\n");
+#else
+ kfree_s(mem, size);
+#endif
}
/*
- * Skbuff device locking
+ * Duplicate an sk_buff. The new one is not owned by a socket or locked
+ * and will be freed on deletion.
*/
-
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
+{
+ struct sk_buff *n;
+ unsigned long offset;
+
+ n=alloc_skb(skb->mem_len-sizeof(struct sk_buff),priority);
+ if(n==NULL)
+ return NULL;
+
+ offset=((char *)n)-((char *)skb);
+
+ memcpy(n->data,skb->data,skb->mem_len-sizeof(struct sk_buff));
+ n->len=skb->len;
+ n->link3=NULL;
+ n->sk=NULL;
+ n->when=skb->when;
+ n->dev=skb->dev;
+ n->h.raw=skb->h.raw+offset;
+ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+ n->fraglen=skb->fraglen;
+ n->fraglist=skb->fraglist;
+ n->saddr=skb->saddr;
+ n->daddr=skb->daddr;
+ n->raddr=skb->raddr;
+ n->acked=skb->acked;
+ n->used=skb->used;
+ n->free=1;
+ n->arp=skb->arp;
+ n->tries=0;
+ n->lock=0;
+ n->users=0;
+ return n;
+}
+
+
+/*
+ * Skbuff device locking
+ */
+
void skb_kept_by_device(struct sk_buff *skb)
{
skb->lock++;
save_flags(flags);
cli();
- if (!--skb->lock) {
- if (skb->free==1)
- kfree_skb(skb,mode);
- }
+ if (!--skb->lock && skb->free == 1)
+ kfree_skb(skb,mode);
restore_flags(flags);
}
int skb_device_locked(struct sk_buff *skb)
{
- if(skb->lock)
- return 1;
- return 0;
+ return skb->lock? 1 : 0;
}
+
+++ /dev/null
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * Definitions for the 'struct sk_buff' memory handlers.
- *
- * Version: @(#)skbuff.h 1.0.4 05/20/93
- *
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- * Corey Minyard <wf-rch!minyard@relay.EU.net>
- *
- * Fixes:
- * Alan Cox : Volatiles (this makes me unhappy - we want proper asm linked list stuff)
- * Alan Cox : Declaration for new primitives
- * Alan Cox : Fraglist support (idea by Donald Becker)
- * Alan Cox : 'users' counter. Combines with datagram changes to avoid skb_peek_copy
- * being used.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-#ifndef _SKBUFF_H
-#define _SKBUFF_H
-#include <linux/malloc.h>
-
-#ifdef CONFIG_IPX
-#include "ipx.h"
-#endif
-
-#define HAVE_ALLOC_SKB /* For the drivers to know */
-
-
-#define FREE_READ 1
-#define FREE_WRITE 0
-
-
-struct sk_buff {
- unsigned long magic_debug_cookie;
- struct sk_buff *volatile next;
- struct sk_buff *volatile prev;
- struct sk_buff *volatile link3;
- struct sk_buff *volatile* list;
- struct sock *sk;
- volatile unsigned long when; /* used to compute rtt's */
- struct device *dev;
- void *mem_addr;
- union {
- struct tcphdr *th;
- struct ethhdr *eth;
- struct iphdr *iph;
- struct udphdr *uh;
- struct arphdr *arp;
- unsigned char *raw;
- unsigned long seq;
-#ifdef CONFIG_IPX
- ipx_packet *ipx;
-#endif
- } h;
- struct iphdr *ip_hdr; /* For IPPROTO_RAW */
- unsigned long mem_len;
- unsigned long len;
- unsigned long fraglen;
- struct sk_buff *fraglist; /* Fragment list */
- unsigned long truesize;
- unsigned long saddr;
- unsigned long daddr;
- int magic;
- volatile char acked,
- used,
- free,
- arp;
- unsigned char tries,lock; /* Lock is now unused */
- unsigned short users; /* User count - see datagram.c (and soon seqpacket.c/stream.c) */
- unsigned long padding[0];
- unsigned char data[0];
-};
-
-#define SK_WMEM_MAX 32767
-#define SK_RMEM_MAX 32767
-
-#define SK_FREED_SKB 0x0DE2C0DE
-#define SK_GOOD_SKB 0xDEC0DED1
-
-extern void print_skb(struct sk_buff *);
-extern void kfree_skb(struct sk_buff *skb, int rw);
-extern void skb_queue_head(struct sk_buff * volatile *list,struct sk_buff *buf);
-extern void skb_queue_tail(struct sk_buff * volatile *list,struct sk_buff *buf);
-extern struct sk_buff * skb_dequeue(struct sk_buff * volatile *list);
-extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk);
-extern void skb_append(struct sk_buff *old,struct sk_buff *newsk);
-extern void skb_unlink(struct sk_buff *buf);
-extern void skb_new_list_head(struct sk_buff *volatile* list);
-extern struct sk_buff * skb_peek(struct sk_buff * volatile *list);
-extern struct sk_buff * skb_peek_copy(struct sk_buff * volatile *list);
-extern struct sk_buff * alloc_skb(unsigned int size, int priority);
-extern void kfree_skbmem(void *mem, unsigned size);
-extern void skb_kept_by_device(struct sk_buff *skb);
-extern void skb_device_release(struct sk_buff *skb, int mode);
-extern int skb_device_locked(struct sk_buff *skb);
-extern void skb_check(struct sk_buff *skb,int, char *);
-#define IS_SKB(skb) skb_check((skb),__LINE__,__FILE__)
-
-extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
-extern int datagram_select(struct sock *sk, int sel_type, select_table *wait);
-extern void skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
-extern void skb_free_datagram(struct sk_buff *skb);
-#endif /* _SKBUFF_H */
--- /dev/null
+/*
+ *
+ * SNMP MIB entries for the IP subsystem.
+ *
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ * We don't chose to implement SNMP in the kernel (this would
+ * be silly as SNMP is a pain in the backside in places). We do
+ * however need to collect the MIB statistics and export them
+ * out of /proc (eventually)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _SNMP_H
+#define _SNMP_H
+
+/*
+ * We use all unsigned longs. Linux will soon be so reliable that even these
+ * will rapidly get too small 8-). Seriously consider the IpInReceives count
+ * on the 20Gb/s + networks people expect in a few years time!
+ */
+
+struct ip_mib
+{
+ unsigned long IpForwarding;
+ unsigned long IpDefaultTTL;
+ unsigned long IpInReceives;
+ unsigned long IpInHdrErrors;
+ unsigned long IpInAddrErrors;
+ unsigned long IpForwDatagrams;
+ unsigned long IpInUnknownProtos;
+ unsigned long IpInDiscards;
+ unsigned long IpInDelivers;
+ unsigned long IpOutRequests;
+ unsigned long IpOutDiscards;
+ unsigned long IpOutNoRoutes;
+ unsigned long IpReasmTimeout;
+ unsigned long IpReasmReqds;
+ unsigned long IpReasmOKs;
+ unsigned long IpReasmFails;
+ unsigned long IpFragOKs;
+ unsigned long IpFragFails;
+ unsigned long IpFragCreates;
+};
+
+
+struct icmp_mib
+{
+ unsigned long IcmpInMsgs;
+ unsigned long IcmpInErrors;
+ unsigned long IcmpInDestUnreachs;
+ unsigned long IcmpInTimeExcds;
+ unsigned long IcmpInParmProbs;
+ unsigned long IcmpInSrcQuenchs;
+ unsigned long IcmpInRedirects;
+ unsigned long IcmpInEchos;
+ unsigned long IcmpInEchoReps;
+ unsigned long IcmpInTimestamps;
+ unsigned long IcmpInTimestampReps;
+ unsigned long IcmpInAddrMasks;
+ unsigned long IcmpInAddrMaskReps;
+ unsigned long IcmpOutMsgs;
+ unsigned long IcmpOutErrors;
+ unsigned long IcmpOutDestUnreachs;
+ unsigned long IcmpOutTimeExcds;
+ unsigned long IcmpOutParmProbs;
+ unsigned long IcmpOutSrcQuenchs;
+ unsigned long IcmpOutRedirects;
+ unsigned long IcmpOutEchos;
+ unsigned long IcmpOutEchoReps;
+ unsigned long IcmpOutTimestamps;
+ unsigned long IcmpOutTimestampReps;
+ unsigned long IcmpOutAddrMasks;
+ unsigned long IcmpOutAddrMaskReps;
+};
+
+struct tcp_mib
+{
+ unsigned long TcpRtoAlgorithm;
+ unsigned long TcpRtoMin;
+ unsigned long TcpRtoMax;
+ unsigned long TcpMaxConn;
+ unsigned long TcpActiveOpens;
+ unsigned long TcpPassiveOpens;
+ unsigned long TcpAttemptFails;
+ unsigned long TcpEstabResets;
+ unsigned long TcpCurrEstab;
+ unsigned long TcpInSegs;
+ unsigned long TcpOutSegs;
+ unsigned long TcpRetransSegs;
+};
+
+struct udp_mib
+{
+ unsigned long UdpInDatagrams;
+ unsigned long UdpNoPorts;
+ unsigned long UdpInErrors;
+ unsigned long UdpOutDatagrams;
+};
+
+
+#endif
#include <asm/segment.h>
#include <asm/system.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "arp.h"
#include "route.h"
#include "tcp.h"
#include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "raw.h"
#include "icmp.h"
}
printk(" wmem_alloc = %lu\n", sk->wmem_alloc);
printk(" rmem_alloc = %lu\n", sk->rmem_alloc);
- printk(" send_head = %p\n", sk->send_head);
printk(" state = %d\n",sk->state);
- printk(" wback = %p, rqueue = %p\n", sk->wback, sk->rqueue);
- printk(" wfront = %p\n", sk->wfront);
printk(" daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr);
printk(" num = %d", sk->num);
printk(" next = %p\n", sk->next);
printk(" rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n",
sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
printk(" prot = %p\n", sk->prot);
- printk(" pair = %p, back_log = %p\n", sk->pair,sk->back_log);
+ printk(" pair = %p\n", sk->pair);
printk(" inuse = %d , blog = %d\n", sk->inuse, sk->blog);
printk(" dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
printk(" retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout);
}
+#if 0
void
print_skb(struct sk_buff *skb)
{
printk(" mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len);
printk(" used = %d free = %d\n", skb->used,skb->free);
}
+#endif
struct sock *sk2;
DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1));
- if (!sk1) {
- printk("sock.c: remove_sock: sk1 == NULL\n");
- return;
- }
if (!sk1->prot) {
printk("sock.c: remove_sock: sk1->prot == NULL\n");
/* Now we can no longer get new packets. */
delete_timer(sk);
+ while ((skb = tcp_dequeue_partial(sk)) != NULL) {
+ IS_SKB(skb);
+ kfree_skb(skb, FREE_WRITE);
+ }
- while ((skb = tcp_dequeue_partial(sk)) != NULL)
- {
- IS_SKB(skb);
- kfree_skb(skb, FREE_WRITE);
- }
-
- /* Cleanup up the write buffer. */
- for(skb = sk->wfront; skb != NULL; )
- {
- struct sk_buff *skb2;
-
- skb2=(struct sk_buff *)skb->next;
- if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
- printk("sock.c:destroy_sock write queue with bad magic(%X)\n",
- skb->magic);
- break;
- }
+ /* Cleanup up the write buffer. */
+ while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
IS_SKB(skb);
kfree_skb(skb, FREE_WRITE);
- skb = skb2;
}
- sk->wfront = NULL;
- sk->wback = NULL;
-
- if (sk->rqueue != NULL)
- {
- while((skb=skb_dequeue(&sk->rqueue))!=NULL)
- {
- /*
- * This will take care of closing sockets that were
- * listening and didn't accept everything.
- */
- if (skb->sk != NULL && skb->sk != sk)
- {
- IS_SKB(skb);
- skb->sk->dead = 1;
- skb->sk->prot->close(skb->sk, 0);
- }
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+ /*
+ * This will take care of closing sockets that were
+ * listening and didn't accept everything.
+ */
+ if (skb->sk != NULL && skb->sk != sk)
+ {
IS_SKB(skb);
- kfree_skb(skb, FREE_READ);
+ skb->sk->dead = 1;
+ skb->sk->prot->close(skb->sk, 0);
}
- }
- sk->rqueue = NULL;
+ IS_SKB(skb);
+ kfree_skb(skb, FREE_READ);
+ }
/* Now we need to clean up the send head. */
- for(skb = sk->send_head; skb != NULL; )
- {
+ cli();
+ for(skb = sk->send_head; skb != NULL; )
+ {
struct sk_buff *skb2;
/*
* We need to remove skb from the transmit queue,
* or maybe the arp queue.
*/
- cli();
- /* see if it's in a transmit queue. */
- /* this can be simplified quite a bit. Look */
- /* at tcp.c:tcp_ack to see how. */
- if (skb->next != NULL)
- {
+ if (skb->next && skb->prev) {
+ printk("destroy_sock: unlinked skb\n");
IS_SKB(skb);
skb_unlink(skb);
}
skb->dev = NULL;
- sti();
- skb2 = (struct sk_buff *)skb->link3;
+ skb2 = skb->link3;
kfree_skb(skb, FREE_WRITE);
skb = skb2;
- }
- sk->send_head = NULL;
+ }
+ sk->send_head = NULL;
+ sti();
/* And now the backlog. */
- if (sk->back_log != NULL)
- {
+ while((skb=skb_dequeue(&sk->back_log))!=NULL) {
/* this should never happen. */
- printk("cleaning back_log. \n");
- cli();
- skb = (struct sk_buff *)sk->back_log;
- do
- {
- struct sk_buff *skb2;
-
- skb2 = (struct sk_buff *)skb->next;
- kfree_skb(skb, FREE_READ);
- skb = skb2;
- }
- while(skb != sk->back_log);
- sti();
+ printk("cleaning back_log\n");
+ kfree_skb(skb, FREE_READ);
}
- sk->back_log = NULL;
/* Now if it has a half accepted/ closed socket. */
if (sk->pair)
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
switch(cmd) {
case F_SETOWN:
sk->nonagle = 0;
#endif
sk->type = sock->type;
+ sk->stamp.tv_sec=0;
sk->protocol = protocol;
sk->wmem_alloc = 0;
sk->rmem_alloc = 0;
sk->max_ack_backlog = 0;
sk->inuse = 0;
sk->delay_acks = 0;
- sk->wback = NULL;
- sk->wfront = NULL;
- sk->rqueue = NULL;
+ skb_queue_head_init(&sk->write_queue);
+ skb_queue_head_init(&sk->receive_queue);
sk->mtu = 576;
sk->prot = prot;
sk->sleep = sock->wait;
sk->daddr = 0;
- sk->saddr = my_addr();
+ sk->saddr = ip_my_addr();
sk->err = 0;
sk->next = NULL;
sk->pair = NULL;
sk->broadcast = 0;
sk->timer.data = (unsigned long)sk;
sk->timer.function = &net_timer;
- sk->back_log = NULL;
+ skb_queue_head_init(&sk->back_log);
sk->blog = 0;
sock->data =(void *) sk;
sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
struct sock *sk, *sk2;
unsigned short snum;
int err;
+ int chk_addr_ret;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
-
/* check this error. */
if (sk->state != TCP_CLOSE) return(-EIO);
if (sk->num != 0) return(-EINVAL);
}
if (snum < PROT_SOCK && !suser()) return(-EACCES);
- if (addr.sin_addr.s_addr!=0 && chk_addr(addr.sin_addr.s_addr)!=IS_MYADDR)
+ chk_addr_ret = ip_chk_addr(addr.sin_addr.s_addr);
+ if (addr.sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR)
return(-EADDRNOTAVAIL); /* Source address MUST be ours! */
- if (chk_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0)
+ if (chk_addr_ret || addr.sin_addr.s_addr == 0)
sk->saddr = addr.sin_addr.s_addr;
DPRINTF((DBG_INET, "sock_array[%d] = %X:\n", snum &(SOCK_ARRAY_SIZE -1),
sock->conn = NULL;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
{
int err;
sk1 = (struct sock *) sock->data;
- if (sk1 == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
/*
* We've been passed an extra socket.
* We need to free it up because the tcp module creates
* it's own when it accepts one.
*/
- if (newsock->data) {
- struct sock * sk = (struct sock *) newsock->data;
- newsock->data = NULL;
- sk->dead = 1;
+ if (newsock->data)
+ {
+ struct sock *sk=(struct sock *)newsock->data;
+ newsock->data=NULL;
destroy_sock(sk);
}
-
+
if (sk1->prot->accept == NULL) return(-EOPNOTSUPP);
/* Restore the state if we have been interrupted, and then returned. */
sin.sin_addr.s_addr = sk->daddr;
} else {
sin.sin_port = sk->dummy_th.source;
- if (sk->saddr == 0) sin.sin_addr.s_addr = my_addr();
+ if (sk->saddr == 0) sin.sin_addr.s_addr = ip_my_addr();
else sin.sin_addr.s_addr = sk->saddr;
}
len = sizeof(sin);
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
/* We may need to bind the socket. */
if (sk->num == 0) {
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
/* We may need to bind the socket. */
if (sk->num == 0) {
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sk->prot->recvfrom == NULL) return(-EOPNOTSUPP);
struct sock *sk;
sk = (struct sock *) sock->data;
- if (sk == NULL) {
- printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return(0);
- }
if (sk->prot->select == NULL) {
DPRINTF((DBG_INET, "select on non-selectable socket.\n"));
put_fs_long(sk->proc,(int *)arg);
}
return(0);
+
+ case SIOCGSTAMP:
+ if (sk)
+ {
+ if(sk->stamp.tv_sec==0)
+ return -ENOENT;
+ err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval));
+ if(err)
+ return err;
+ memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval));
+ return 0;
+ }
+ return -EINVAL;
+
#if 0 /* FIXME: */
case SIOCATMARK:
printk("AF_INET: ioctl(SIOCATMARK, 0x%08X)\n",(void *) arg);
case SIOCADDRT: case SIOCADDRTOLD:
case SIOCDELRT: case SIOCDELRTOLD:
- return(rt_ioctl(cmd,(void *) arg));
+ return(ip_rt_ioctl(cmd,(void *) arg));
case SIOCDARP:
case SIOCGARP:
case SIOCSARP:
return(arp_ioctl(cmd,(void *) arg));
- case IP_SET_DEV:
case SIOCGIFCONF:
case SIOCGIFFLAGS:
case SIOCSIFFLAGS:
case SIOCSIFMTU:
case SIOCSIFLINK:
case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case OLD_SIOCGIFHWADDR:
return(dev_ioctl(cmd,(void *) arg));
default:
struct sk_buff * c = alloc_skb(size, priority);
if (c) {
cli();
- sk->wmem_alloc+= size;
+ sk->wmem_alloc+= c->mem_len;
sti();
}
return c;
struct sk_buff *c = alloc_skb(size, priority);
if (c) {
cli();
- sk->rmem_alloc += size;
+ sk->rmem_alloc += c->mem_len;
sti();
}
return(c);
{
DPRINTF((DBG_INET, "sock_wfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
- IS_SKB((struct sk_buff *) mem);
+ IS_SKB(mem);
kfree_skbmem(mem, size);
if (sk) {
sk->wmem_alloc -= size;
sock_rfree(struct sock *sk, void *mem, unsigned long size)
{
DPRINTF((DBG_INET, "sock_rfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
- IS_SKB((struct sk_buff *) mem);
+ IS_SKB(mem);
kfree_skbmem(mem, size);
if (sk) {
sk->rmem_alloc -= size;
void release_sock(struct sock *sk)
{
- if (!sk) {
- printk("sock.c: release_sock sk == NULL\n");
- return;
- }
- if (!sk->prot) {
-/* printk("sock.c: release_sock sk->prot == NULL\n"); */
+ struct sk_buff *skb;
+
+ if (!sk->prot)
return;
- }
if (sk->blog) return;
/* See if we have any packets built up. */
- cli();
sk->inuse = 1;
- while(sk->back_log != NULL) {
- struct sk_buff *skb;
-
+ while((skb = skb_dequeue(&sk->back_log)) != NULL) {
sk->blog = 1;
- skb =(struct sk_buff *)sk->back_log;
DPRINTF((DBG_INET, "release_sock: skb = %X:\n", skb));
- if (skb->next != skb) {
- sk->back_log = skb->next;
- skb->prev->next = skb->next;
- skb->next->prev = skb->prev;
- } else {
- sk->back_log = NULL;
- }
- sti();
- DPRINTF((DBG_INET, "sk->back_log = %X\n", sk->back_log));
if (sk->prot->rcv) sk->prot->rcv(skb, skb->dev, sk->opt,
skb->saddr, skb->len, skb->daddr, 1,
-
- /* Only used for/by raw sockets. */
- (struct inet_protocol *)sk->pair);
- cli();
+ /* Only used for/by raw sockets. */
+ (struct inet_protocol *)sk->pair);
}
sk->blog = 0;
sk->inuse = 0;
- sti();
if (sk->dead && sk->state == TCP_CLOSE) {
/* Should be about 2 rtt's */
reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
extern unsigned long seq_offset;
-/* Called by ddi.c on kernel startup. */
+/*
+ * Called by ddi.c on kernel startup.
+ */
+
void inet_proto_init(struct ddi_proto *pro)
{
- struct inet_protocol *p;
- int i;
+ struct inet_protocol *p;
+ int i;
- printk("Swansea University Computer Society Net2Debugged [1.30]\n");
- /* Set up our UNIX VFS major device. */
- if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) {
- printk("%s: cannot register major device %d!\n",
+ printk("Swansea University Computer Society NET3.010\n");
+ /*
+ * Set up our UNIX VFS major device. (compatibility)
+ */
+
+ if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0)
+ {
+ printk("%s: cannot register major device %d!\n",
pro->name, AF_INET_MAJOR);
- return;
- }
+ return;
+ }
- /* Tell SOCKET that we are alive... */
- (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+ /*
+ * Tell SOCKET that we are alive...
+ */
+
+ (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
- seq_offset = CURRENT_TIME*250;
+ seq_offset = CURRENT_TIME*250;
- /* Add all the protocols. */
- for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
- tcp_prot.sock_array[i] = NULL;
- udp_prot.sock_array[i] = NULL;
- raw_prot.sock_array[i] = NULL;
- }
- printk("IP Protocols: ");
- for(p = inet_protocol_base; p != NULL;) {
- struct inet_protocol *tmp;
-
- tmp = (struct inet_protocol *) p->next;
- inet_add_protocol(p);
- printk("%s%s",p->name,tmp?", ":"\n");
- p = tmp;
- }
+ /*
+ * Add all the protocols.
+ */
+
+ for(i = 0; i < SOCK_ARRAY_SIZE; i++)
+ {
+ tcp_prot.sock_array[i] = NULL;
+ udp_prot.sock_array[i] = NULL;
+ raw_prot.sock_array[i] = NULL;
+ }
- /* Initialize the DEV module. */
- dev_init();
+ printk("IP Protocols: ");
+ for(p = inet_protocol_base; p != NULL;)
+ {
+ struct inet_protocol *tmp;
+
+ tmp = (struct inet_protocol *) p->next;
+ inet_add_protocol(p);
+ printk("%s%s",p->name,tmp?", ":"\n");
+ p = tmp;
+ }
- /* Initialize the "Buffer Head" pointers. */
- bh_base[INET_BH].routine = inet_bh;
+ /*
+ * Initialize the DEV module.
+ */
+ dev_init();
+ /*
+ * Set the ARP module up
+ */
+ arp_init();
+ /*
+ * Set the IP module up
+ */
+ ip_init();
+
+ /*
+ * Initialize the "Buffer Head" pointers.
+ */
+
+ bh_base[INET_BH].routine = inet_bh;
}
#include <linux/ip.h> /* struct options */
#include <linux/tcp.h> /* struct tcphdr */
-#include "skbuff.h" /* struct sk_buff */
+#include <linux/skbuff.h> /* struct sk_buff */
#include "protocol.h" /* struct inet_protocol */
#ifdef CONFIG_AX25
#include "ax25.h"
int proc;
struct sock *next;
struct sock *pair;
- struct sk_buff *volatile send_tail;
- struct sk_buff *volatile send_head;
- struct sk_buff *volatile back_log;
+ struct sk_buff * volatile send_head;
+ struct sk_buff * volatile send_tail;
+ struct sk_buff_head back_log;
struct sk_buff *partial;
struct timer_list partial_timer;
long retransmits;
- struct sk_buff *volatile wback,
- *volatile wfront,
- *volatile rqueue;
+ struct sk_buff_head write_queue,
+ receive_queue;
struct proto *prot;
struct wait_queue **sleep;
unsigned long daddr;
/* This part is used for the timeout functions (timer.c). */
int timeout; /* What are we waiting for? */
struct timer_list timer;
+ struct timeval stamp;
/* identd */
struct socket *socket;
* Charles Hedrick : TCP fixes
* Toomas Tamm : TCP window fixes
* Alan Cox : Small URG fix to rlogin ^C ack fight
- * Charles Hedrick : Window fix
+ * Charles Hedrick : Rewrote most of it to actually work
* Linus : Rewrote tcp_read() and URG handling
* completely
* Gerhard Koerting: Fixed some missing timer handling
* it causes a select. Linux can - given the official select semantics I
* feel that _really_ its the BSD network programs that are bust (notably
* inetd, which hangs occasionally because of this).
- * Add VJ Fastrecovery algorithm ?
* Protocol closedown badly messed up.
- * Incompatiblity with spider ports (tcp hangs on that
- * socket occasionally).
- * MSG_PEEK and read on same socket at once can cause crashes.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <linux/termios.h>
#include <linux/in.h>
#include <linux/fcntl.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
#include "ip.h"
#include "protocol.h"
#include "icmp.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
-#include "arp.h"
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#define SEQ_TICK 3
unsigned long seq_offset;
+struct tcp_mib tcp_statistics;
+
#define SUBNETSARELOCAL
static __inline__ int
__print_th(th);
}
-/* This routine grabs the first thing off of a rcv queue. */
-static struct sk_buff *
-get_firstr(struct sock *sk)
-{
- return skb_dequeue(&sk->rqueue);
-}
/* This routine picks a TCP windows for a socket based on
the following constraints
*/
if (icmp_err_convert[err & 0xff].fatal) {
if (sk->state == TCP_SYN_SENT) {
+ tcp_statistics.TcpAttemptFails++;
sk->state = TCP_CLOSE;
sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */
}
unsigned long counted;
unsigned long amount;
struct sk_buff *skb;
- int count=0;
int sum;
unsigned long flags;
if(sk && sk->debug)
printk("tcp_readable: %p - ",sk);
- if (sk == NULL || skb_peek(&sk->rqueue) == NULL) /* Empty sockets are easy! */
+ save_flags(flags);
+ cli();
+ if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL)
{
+ restore_flags(flags);
if(sk && sk->debug)
printk("empty\n");
return(0);
counted = sk->copied_seq+1; /* Where we are at the moment */
amount = 0;
- save_flags(flags); /* So nobody adds things at the wrong moment */
- cli();
- skb =(struct sk_buff *)sk->rqueue;
-
/* Do until a push or until we are out of data. */
do {
- count++;
-#ifdef OLD
- /* This is wrong: It breaks Chameleon amongst other stacks */
- if (count > 20) {
- restore_flags(flags);
- DPRINTF((DBG_TCP, "tcp_readable, more than 20 packets without a psh\n"));
- printk("tcp_read: possible read_queue corruption.\n");
- return(amount);
- }
-#endif
if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */
break;
sum = skb->len -(counted - skb->h.th->seq); /* Length - header but start from where we are up to (avoid overlaps) */
counted += sum;
}
if (amount && skb->h.th->psh) break;
- skb =(struct sk_buff *)skb->next; /* Move along */
- } while(skb != sk->rqueue);
+ skb = skb->next;
+ } while(skb != (struct sk_buff *)&sk->receive_queue);
if (amount && !sk->urginline && sk->urg_data &&
(sk->urg_seq - sk->copied_seq) <= (counted - sk->copied_seq))
amount--; /* don't count urg data */
select_wait(sk->sleep, wait);
if(sk->debug)
printk("-select out");
- if (skb_peek(&sk->rqueue) != NULL) {
+ if (skb_peek(&sk->receive_queue) != NULL) {
if (sk->state == TCP_LISTEN || tcp_readable(sk)) {
release_sock(sk);
if(sk->debug)
DPRINTF((DBG_TCP,
"tcp_select: sleeping on write sk->wmem_alloc = %d, "
"sk->packets_out = %d\n"
- "sk->wback = %X, sk->wfront = %X\n"
"sk->write_seq = %u, sk->window_seq=%u\n",
sk->wmem_alloc, sk->packets_out,
- sk->wback, sk->wfront,
sk->write_seq, sk->window_seq));
release_sock(sk);
{
unsigned long sum;
- if (saddr == 0) saddr = my_addr();
+ if (saddr == 0) saddr = ip_my_addr();
print_th(th);
__asm__("\t addl %%ecx,%%ebx\n"
"\t adcl %%edx,%%ebx\n"
return;
}
}
-
+
+ tcp_statistics.TcpOutSegs++;
/* We need to complete and send the packet. */
tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
sk->cong_window, sk->packets_out));
DPRINTF((DBG_TCP, "sk->write_seq = %d, sk->window_seq = %d\n",
sk->write_seq, sk->window_seq));
- skb->next = NULL;
- skb->magic = TCP_WRITE_QUEUE_MAGIC;
- if (sk->wback == NULL) {
- sk->wfront = skb;
- } else {
- sk->wback->next = skb;
+ if (skb->next != NULL) {
+ printk("tcp_send_partial: next != NULL\n");
+ skb_unlink(skb);
}
- sk->wback = skb;
- if (before(sk->window_seq, sk->wfront->h.seq) &&
+ skb_queue_tail(&sk->write_queue, skb);
+ if (before(sk->window_seq, sk->write_queue.next->h.seq) &&
sk->send_head == NULL &&
sk->ack_backlog == 0)
reset_timer(sk, TIME_PROBE0, sk->rto);
if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) {
reset_timer(sk, TIME_WRITE, 10);
}
-if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
+ if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
return;
}
- buff->mem_addr = buff;
- buff->mem_len = MAX_ACK_SIZE;
buff->len = sizeof(struct tcphdr);
buff->sk = sk;
t1 =(struct tcphdr *) buff->data;
if (tmp < 0) {
buff->free=1;
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
-if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
+ if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
return;
}
buff->len += tmp;
sk->ack_backlog = 0;
sk->bytes_rcv = 0;
sk->ack_timed = 0;
- if (sk->send_head == NULL && sk->wfront == NULL && sk->timeout == TIME_WRITE)
+ if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL
+ && sk->timeout == TIME_WRITE)
{
if(sk->keepopen)
reset_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
if (sk->debug)
printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
+ tcp_statistics.TcpOutSegs++;
sk->prot->queue_xmit(sk, dev, buff, 1);
}
release_sock(sk);
/* NB: following must be mtu, because mss can be increased.
* mss is always <= mtu */
- skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
+ skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header, 0, GFP_KERNEL);
sk->inuse = 1;
send_tmp = skb;
} else {
/* We will release the socket incase we sleep here. */
release_sock(sk);
- skb = prot->wmalloc(sk, copy + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
+ skb = prot->wmalloc(sk, copy + prot->max_header , 0, GFP_KERNEL);
sk->inuse = 1;
}
return;
}
- buff->mem_addr = buff;
- buff->mem_len = MAX_ACK_SIZE;
buff->len = sizeof(struct tcphdr);
buff->sk = sk;
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
sk->prot->queue_xmit(sk, dev, buff, 1);
+ tcp_statistics.TcpOutSegs++;
}
* We have to loop through all the buffer headers,
* and try to free up all the space we can.
*/
- while((skb=skb_peek(&sk->rqueue)) != NULL )
+ while((skb=skb_peek(&sk->receive_queue)) != NULL)
{
if (!skb->used)
break;
current->state = TASK_INTERRUPTIBLE;
- skb = sk->rqueue;
+ skb = skb_peek(&sk->receive_queue);
do {
if (!skb)
break;
goto found_ok_skb;
if (!(flags & MSG_PEEK))
skb->used = 1;
- skb = (struct sk_buff *)skb->next;
- } while (skb != sk->rqueue);
+ skb = skb->next;
+ } while (skb != (struct sk_buff *)&sk->receive_queue);
if (copied)
break;
sk->inuse = 1;
DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
- buff->mem_addr = buff;
- buff->mem_len = MAX_RESET_SIZE;
buff->sk = sk;
buff->len = sizeof(*t1);
t1 =(struct tcphdr *) buff->data;
* Can't just queue this up.
* It should go at the end of the write queue.
*/
- if (sk->wback != NULL) {
- buff->free=0;
- buff->next = NULL;
- sk->wback->next = buff;
- sk->wback = buff;
- buff->magic = TCP_WRITE_QUEUE_MAGIC;
+ if (skb_peek(&sk->write_queue) != NULL) {
+ buff->free=0;
+ if (buff->next != NULL) {
+ printk("tcp_shutdown: next != NULL\n");
+ skb_unlink(buff);
+ }
+ skb_queue_tail(&sk->write_queue, buff);
} else {
sk->sent_seq = sk->write_seq;
sk->prot->queue_xmit(sk, dev, buff, 0);
struct sk_buff *buff;
struct tcphdr *t1;
int tmp;
-
+ struct device *ndev=NULL;
+
/*
* We need to grab some memory, and put together an RST,
* and then put it into the queue to be sent.
return;
DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff));
- buff->mem_addr = buff;
- buff->mem_len = MAX_RESET_SIZE;
buff->len = sizeof(*t1);
buff->sk = NULL;
buff->dev = dev;
t1 =(struct tcphdr *) buff->data;
/* Put in the IP header and routing stuff. */
- tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
+ tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
sizeof(struct tcphdr),tos,ttl);
if (tmp < 0) {
buff->free = 1;
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, saddr, daddr, sizeof(*t1), NULL);
prot->queue_xmit(NULL, dev, buff, 1);
+ tcp_statistics.TcpOutSegs++;
}
unsigned char *ptr;
struct sock *newsk;
struct tcphdr *th;
+ struct device *ndev=NULL;
int tmp;
DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
} else {
DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl);
+ tcp_statistics.TcpAttemptFails++;
kfree_skb(skb, FREE_READ);
return;
}
* flurry of syns from eating up all our memory.
*/
if (sk->ack_backlog >= sk->max_ack_backlog) {
+ tcp_statistics.TcpAttemptFails++;
kfree_skb(skb, FREE_READ);
return;
}
newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
if (newsk == NULL) {
/* just ignore the syn. It will get retransmitted. */
+ tcp_statistics.TcpAttemptFails++;
kfree_skb(skb, FREE_READ);
return;
}
DPRINTF((DBG_TCP, "newsk = %X\n", newsk));
- memcpy((void *)newsk,(void *)sk, sizeof(*newsk));
- newsk->wback = NULL;
- newsk->wfront = NULL;
- newsk->rqueue = NULL;
+ memcpy(newsk, sk, sizeof(*newsk));
+ skb_queue_head_init(&newsk->write_queue);
+ skb_queue_head_init(&newsk->receive_queue);
newsk->send_head = NULL;
newsk->send_tail = NULL;
- newsk->back_log = NULL;
+ skb_queue_head_init(&newsk->back_log);
newsk->rtt = TCP_CONNECT_TIME << 3;
newsk->rto = TCP_CONNECT_TIME;
newsk->mdev = 0;
newsk->dead = 1;
release_sock(newsk);
kfree_skb(skb, FREE_READ);
+ tcp_statistics.TcpAttemptFails++;
return;
}
- buff->mem_addr = buff;
- buff->mem_len = MAX_SYN_SIZE;
buff->len = sizeof(struct tcphdr)+4;
buff->sk = newsk;
t1 =(struct tcphdr *) buff->data;
/* Put in the IP header and routing stuff. */
- tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev,
+ tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev,
IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
/* Something went wrong. */
release_sock(newsk);
skb->sk = sk;
kfree_skb(skb, FREE_READ);
+ tcp_statistics.TcpAttemptFails++;
return;
}
sk->rmem_alloc -= skb->mem_len;
newsk->rmem_alloc += skb->mem_len;
- skb_queue_tail(&sk->rqueue,skb);
+ skb_queue_tail(&sk->receive_queue,skb);
sk->ack_backlog++;
release_sock(newsk);
+ tcp_statistics.TcpOutSegs++;
}
sk->state_change(sk);
/* We need to flush the recv. buffs. */
- if (skb_peek(&sk->rqueue) != NULL)
+ if (skb_peek(&sk->receive_queue) != NULL)
{
struct sk_buff *skb;
if(sk->debug)
printk("Clean rcv queue\n");
- while((skb=skb_dequeue(&sk->rqueue))!=NULL)
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
{
if(skb->len > 0 && after(skb->h.th->seq + skb->len + 1 , sk->copied_seq))
need_reset = 1;
if(sk->debug)
printk("Cleaned.\n");
}
- sk->rqueue = NULL;
/* Get rid off any half-completed packets. */
if (sk->partial) {
reset_timer(sk, TIME_CLOSE, 100);
return;
}
- buff->mem_addr = buff;
- buff->mem_len = MAX_FIN_SIZE;
buff->sk = sk;
buff->free = 1;
buff->len = sizeof(*t1);
reset_timer(sk, TIME_CLOSE,4*sk->rto);
if(timeout)
tcp_time_wait(sk);
+
DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
release_sock(sk);
return;
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
- if (sk->wfront == NULL) {
+ tcp_statistics.TcpOutSegs++;
+
+ if (skb_peek(&sk->write_queue) == NULL) {
sk->sent_seq = sk->write_seq;
prot->queue_xmit(sk, dev, buff, 0);
} else {
reset_timer(sk, TIME_WRITE, sk->rto);
- buff->next = NULL;
- if (sk->wback == NULL) {
- sk->wfront = buff;
- } else {
- sk->wback->next = buff;
+ if (buff->next != NULL) {
+ printk("tcp_close: next != NULL\n");
+ skb_unlink(buff);
}
- sk->wback = buff;
- buff->magic = TCP_WRITE_QUEUE_MAGIC;
+ skb_queue_tail(&sk->write_queue, buff);
}
if (sk->state == TCP_CLOSE_WAIT) {
if(sk->zapped)
return;
- while(sk->wfront != NULL &&
- before(sk->wfront->h.seq, sk->window_seq +1) &&
+ while((skb = skb_peek(&sk->write_queue)) != NULL &&
+ before(skb->h.seq, sk->window_seq + 1) &&
(sk->retransmits == 0 ||
sk->timeout != TIME_WRITE ||
- before(sk->wfront->h.seq, sk->rcv_ack_seq +1))
- && sk->packets_out < sk->cong_window) {
- skb = sk->wfront;
+ before(skb->h.seq, sk->rcv_ack_seq + 1))
+ && sk->packets_out < sk->cong_window) {
IS_SKB(skb);
- sk->wfront = skb->next;
- if (sk->wfront == NULL) sk->wback = NULL;
- skb->next = NULL;
- if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
- printk("tcp.c skb with bad magic(%X) on write queue. Squashing "
- "queue\n", skb->magic);
- sk->wfront = NULL;
- sk->wback = NULL;
- return;
- }
- skb->magic = 0;
+ skb_unlink(skb);
DPRINTF((DBG_TCP, "Sending a packet.\n"));
/* See if we really need to send the packet. */
struct sk_buff *skb,*skb2,*skb3;
for (skb = sk->send_head; skb != NULL; skb = skb2) {
- skb2 = (struct sk_buff *)skb->link3;
+ skb2 = skb->link3;
if (list == NULL || before (skb2->h.seq, list->h.seq)) {
skb->link3 = list;
sk->send_tail = skb;
list = skb;
} else {
- for (skb3 = list; ; skb3 = (struct sk_buff *)skb3->link3) {
+ for (skb3 = list; ; skb3 = skb3->link3) {
if (skb3->link3 == NULL ||
before(skb->h.seq, skb3->link3->h.seq)) {
skb->link3 = skb3->link3;
cli();
while (skb2 != NULL) {
skb = skb2;
- skb2 = (struct sk_buff *)skb->link3;
+ skb2 = skb->link3;
skb->link3 = NULL;
if (after(skb->h.seq, sk->window_seq)) {
if (sk->packets_out > 0) sk->packets_out--;
skb_unlink(skb);
}
/* Now add it to the write_queue. */
- skb->magic = TCP_WRITE_QUEUE_MAGIC;
- if (wskb == NULL) {
- skb->next = sk->wfront;
- sk->wfront = skb;
- } else {
- skb->next = wskb->next;
- wskb->next = skb;
- }
- if (sk->wback == wskb) sk->wback = skb;
+ if (wskb == NULL)
+ skb_queue_head(&sk->write_queue,skb);
+ else
+ skb_append(wskb,skb);
wskb = skb;
} else {
if (sk->send_head == NULL) {
* it needs to be for normal retransmission
*/
if (sk->timeout == TIME_PROBE0) {
- if (sk->wfront != NULL && /* should always be non-null */
- ! before (sk->window_seq, sk->wfront->h.seq)) {
+ if (skb_peek(&sk->write_queue) != NULL && /* should always be non-null */
+ ! before (sk->window_seq, sk->write_queue.next->h.seq)) {
sk->retransmits = 0;
sk->backoff = 0;
/* recompute rto from rtt. this eliminates any backoff */
oskb = sk->send_head;
IS_SKB(oskb);
- sk->send_head =(struct sk_buff *)oskb->link3;
+ sk->send_head = oskb->link3;
if (sk->send_head == NULL) {
sk->send_tail = NULL;
}
- /* We may need to remove this from the dev send list. */
- skb_unlink(oskb); /* Much easier! */
+ /* We may need to remove this from the dev send list. */
+ if (oskb->next)
+ skb_unlink(oskb);
sti();
- oskb->magic = 0;
kfree_skb(oskb, FREE_WRITE); /* write. */
if (!sk->dead) sk->write_space(sk);
} else {
* Maybe we can take some stuff off of the write queue,
* and put it onto the xmit queue.
*/
- if (sk->wfront != NULL) {
- if (after (sk->window_seq+1, sk->wfront->h.seq) &&
+ if (skb_peek(&sk->write_queue) != NULL) {
+ if (after (sk->window_seq+1, sk->write_queue.next->h.seq) &&
(sk->retransmits == 0 ||
sk->timeout != TIME_WRITE ||
- before(sk->wfront->h.seq, sk->rcv_ack_seq +1))
+ before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1))
&& sk->packets_out < sk->cong_window) {
flag |= 1;
tcp_write_xmit(sk);
- } else if (before(sk->window_seq, sk->wfront->h.seq) &&
+ } else if (before(sk->window_seq, sk->write_queue.next->h.seq) &&
sk->send_head == NULL &&
sk->ack_backlog == 0 &&
sk->state != TCP_TIME_WAIT) {
}
if (sk->packets_out == 0 && sk->partial != NULL &&
- sk->wfront == NULL && sk->send_head == NULL) {
+ skb_peek(&sk->write_queue) == NULL && sk->send_head == NULL) {
flag |= 1;
tcp_send_partial(sk);
}
if (sk->shutdown & RCV_SHUTDOWN) {
sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
tcp_reset(sk->saddr, sk->daddr, skb->h.th,
- sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
+ sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
+ tcp_statistics.TcpEstabResets++;
sk->state = TCP_CLOSE;
sk->err = EPIPE;
sk->shutdown = SHUTDOWN_MASK;
*/
/* This should start at the last one, and then go around forwards. */
- if (sk->rqueue == NULL) {
+ if (skb_peek(&sk->receive_queue) == NULL) {
DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
-#ifdef OLDWAY
- sk->rqueue = skb;
- skb->next = skb;
- skb->prev = skb;
- skb->list = &sk->rqueue;
-#else
- skb_queue_head(&sk->rqueue,skb);
-#endif
+ skb_queue_head(&sk->receive_queue,skb);
skb1= NULL;
} else {
DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
- for(skb1=sk->rqueue->prev; ; skb1 =(struct sk_buff *)skb1->prev) {
+ for(skb1=sk->receive_queue.prev; ; skb1 = skb1->prev) {
if(sk->debug)
{
printk("skb1=%p :", skb1);
printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq,
sk->acked_seq);
}
-#ifdef OLD
- if (after(th->seq+1, skb1->h.th->seq)) {
- skb->prev = skb1;
- skb->next = skb1->next;
- skb->next->prev = skb;
- skb1->next = skb;
- if (skb1 == sk->rqueue) sk->rqueue = skb;
- break;
- }
- if (skb1->prev == sk->rqueue) {
- skb->next= skb1;
- skb->prev = skb1->prev;
- skb->prev->next = skb;
- skb1->prev = skb;
- skb1 = NULL; /* so we know we might be able
- to ack stuff. */
- break;
- }
-#else
if (th->seq==skb1->h.th->seq && skb->len>= skb1->len)
{
skb_append(skb1,skb);
skb_append(skb1,skb);
break;
}
- if (skb1 == sk->rqueue)
+ if (skb1 == skb_peek(&sk->receive_queue))
{
- skb_queue_head(&sk->rqueue, skb);
+ skb_queue_head(&sk->receive_queue, skb);
break;
}
-#endif
}
DPRINTF((DBG_TCP, "skb = %X:\n", skb));
}
sk->shutdown |= RCV_SHUTDOWN;
}
- for(skb2 = (struct sk_buff *)skb->next;
- skb2 !=(struct sk_buff *) sk->rqueue;
- skb2 = (struct sk_buff *)skb2->next) {
+ for(skb2 = skb->next;
+ skb2 != (struct sk_buff *)&sk->receive_queue;
+ skb2 = skb2->next) {
if (before(skb2->h.th->seq, sk->acked_seq+1)) {
if (after(skb2->h.th->ack_seq, sk->acked_seq))
{
* for the send side. He could be sending us stuff as large as mtu.
*/
while (sk->prot->rspace(sk) < sk->mtu) {
- skb1 = skb_peek(&sk->rqueue);
+ skb1 = skb_peek(&sk->receive_queue);
if (skb1 == NULL) {
printk("INET: tcp.c:tcp_data memory leak detected.\n");
break;
}
skb_unlink(skb1);
-#ifdef OLDWAY
- if (skb1->prev == skb1) {
- sk->rqueue = NULL;
- } else {
- sk->rqueue = (struct sk_buff *)skb1->prev;
- skb1->next->prev = skb1->prev;
- skb1->prev->next = skb1->next;
- }
-#endif
kfree_skb(skb1, FREE_READ);
}
tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
/* ok, got the correct packet, update info */
sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
if (!sk->dead)
- wake_up_interruptible(sk->sleep);
+ sk->data_ready(sk,0);
return 0;
}
/* Contains the one that needs to be acked */
reset_timer(sk, TIME_CLOSE, TCP_TIMEOUT_LEN);
sk->fin_seq = th->seq+1;
+ tcp_statistics.TcpCurrEstab--;
sk->state = TCP_CLOSE_WAIT;
if (th->rst) sk->shutdown = SHUTDOWN_MASK;
break;
/* avoid the race. */
cli();
sk->inuse = 1;
- while((skb = get_firstr(sk)) == NULL) {
+ while((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
if (flags & O_NONBLOCK) {
sti();
release_sock(sk);
DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr)));
/* Don't want a TCP connection going to a broadcast address */
- if (chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) {
+ if (ip_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) {
DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n"));
return(-ENETUNREACH);
}
return(-ENOMEM);
}
sk->inuse = 1;
- buff->mem_addr = buff;
- buff->mem_len = MAX_SYN_SIZE;
buff->len = 24;
buff->sk = sk;
buff->free = 1;
sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
sk->prot->queue_xmit(sk, dev, buff, 0);
+ tcp_statistics.TcpActiveOpens++;
+ tcp_statistics.TcpOutSegs++;
release_sock(sk);
return(0);
ignore_it:
DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n"));
+ if (th->rst)
+ return 0;
+
/*
* Send a reset if we get something not ours and we are
* unsynchronized. Note: We don't do anything to our end. We
return 1;
}
- if (th->rst)
- return 0;
-
/* Try to resync things. */
tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
return 0;
DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n"));
return(0);
}
-#if 0 /* FIXME: it's ok for protocol to be NULL */
- if (!protocol) {
- DPRINTF((DBG_TCP, "tcp.c: tcp_rcv protocol = NULL\n"));
- return(0);
- }
- if (!opt) { /* FIXME: it's ok for opt to be NULL */
- DPRINTF((DBG_TCP, "tcp.c: tcp_rcv opt = NULL\n"));
- }
-#endif
- if (!dev) {
+ if (!dev)
+ {
DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n"));
return(0);
}
+
+ tcp_statistics.TcpInSegs++;
+
th = skb->h.th;
/* Find the socket. */
/* We may need to add it to the backlog here. */
cli();
if (sk->inuse) {
- if (sk->back_log == NULL) {
- sk->back_log = skb;
- skb->next = skb;
- skb->prev = skb;
- } else {
- skb->next = sk->back_log;
- skb->prev = sk->back_log->prev;
- skb->prev->next = skb;
- skb->next->prev = skb;
- }
+ skb_queue_head(&sk->back_log, skb);
sti();
return(0);
}
case TCP_FIN_WAIT2:
case TCP_TIME_WAIT:
if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
-if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
-#ifdef undef
-/* nice idea, but tcp_sequence already does this. Maybe it shouldn't?? */
- if(!th->rst)
- tcp_send_ack(sk->sent_seq, sk->acked_seq,
- sk, th, saddr);
-#endif
+ if (inet_debug == DBG_SLIP)
+ printk("\rtcp_rcv: not in seq\n");
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
- if (th->rst) {
+ if (th->rst)
+ {
+ tcp_statistics.TcpEstabResets++;
+ tcp_statistics.TcpCurrEstab--;
sk->zapped=1;
/* This means the thing should really be closed. */
sk->err = ECONNRESET;
- if (sk->state == TCP_CLOSE_WAIT) {
+ if (sk->state == TCP_CLOSE_WAIT)
+ {
sk->err = EPIPE;
}
*/
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
- if (!sk->dead) {
+ if (!sk->dead)
+ {
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
- if (
-#if 0
- if ((opt && (opt->security != 0 ||
- opt->compartment != 0)) ||
-#endif
- th->syn) {
+ if (th->syn)
+ {
+ tcp_statistics.TcpCurrEstab--;
+ tcp_statistics.TcpEstabResets++;
sk->err = ECONNRESET;
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
return(0);
}
- if (th->syn) {
-#if 0
- if (opt->security != 0 || opt->compartment != 0) {
- tcp_reset(daddr, saddr, th, prot, opt,dev);
- release_sock(sk);
- return(0);
- }
-#endif
-
+ if (th->syn)
+ {
/*
* Now we just put the whole thing including
* the header and saddr, and protocol pointer
default:
- if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
+ if (!tcp_sequence(sk, th, len, opt, saddr,dev))
+ {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
case TCP_SYN_SENT:
- if (th->rst) {
+ if (th->rst)
+ {
+ tcp_statistics.TcpAttemptFails++;
sk->err = ECONNREFUSED;
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
sk->zapped = 1;
- if (!sk->dead) {
+ if (!sk->dead)
+ {
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
-#if 0
- if (opt->security != 0 || opt->compartment != 0) {
- sk->err = ECONNRESET;
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- tcp_reset(daddr, saddr, th, sk->prot, opt, dev);
- if (!sk->dead) {
- wake_up_interruptible(sk->sleep);
- }
- kfree_skb(skb, FREE_READ);
- release_sock(sk);
- return(0);
- }
-#endif
- if (!th->ack) {
- if (th->syn) {
+ if (!th->ack)
+ {
+ if (th->syn)
+ {
sk->state = TCP_SYN_RECV;
}
return(0);
}
- switch(sk->state) {
+ switch(sk->state)
+ {
case TCP_SYN_SENT:
- if (!tcp_ack(sk, th, saddr, len)) {
+ if (!tcp_ack(sk, th, saddr, len))
+ {
+ tcp_statistics.TcpAttemptFails++;
tcp_reset(daddr, saddr, th,
sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
* If the syn bit is also set, switch to
* tcp_syn_recv, and then to established.
*/
- if (!th->syn) {
+ if (!th->syn)
+ {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
sk, th, sk->daddr);
case TCP_SYN_RECV:
- if (!tcp_ack(sk, th, saddr, len)) {
+ if (!tcp_ack(sk, th, saddr, len))
+ {
+ tcp_statistics.TcpAttemptFails++;
tcp_reset(daddr, saddr, th,
sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
+
+ tcp_statistics.TcpCurrEstab++;
sk->state = TCP_ESTABLISHED;
/*
buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
if (buff == NULL) return;
- buff->mem_addr = buff;
- buff->mem_len = MAX_ACK_SIZE;
buff->len = sizeof(struct tcphdr);
buff->free = 1;
buff->sk = sk;
* This will prevent the timer from automatically being restarted.
*/
sk->prot->queue_xmit(sk, dev, buff, 1);
+ tcp_statistics.TcpOutSegs++;
}
void
sk->prot->retransmits ++;
}
-
/*
* Socket option code for TCP.
- */
+ */
+
int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
int val,err;
#include <linux/tcp.h>
-#define MAX_SYN_SIZE 44 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_FIN_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_ACK_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_RESET_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_SYN_SIZE 44 + MAX_HEADER
+#define MAX_FIN_SIZE 40 + MAX_HEADER
+#define MAX_ACK_SIZE 40 + MAX_HEADER
+#define MAX_RESET_SIZE 40 + MAX_HEADER
#define MAX_WINDOW 8192
#define MIN_WINDOW 2048
#define MAX_ACK_BACKLOG 2
#define TCP_NO_CHECK 0 /* turn to one if you want the default
* to be no checksum */
-#define TCP_WRITE_QUEUE_MAGIC 0xa5f23477
/*
* TCP option
#include <linux/timer.h>
#include <asm/system.h>
#include <linux/interrupt.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "arp.h"
{
delete_timer (t);
- if (timeout != -1)
- t->timeout = timeout;
+ t->timeout = timeout;
#if 1
/* FIXME: ??? */
int why = sk->timeout;
/* timeout is overwritten by 'delete_timer' and 'reset_timer' */
- if (sk->inuse || in_inet_bh()) {
+ cli();
+ if (sk->inuse || in_bh) {
sk->timer.expires = 10;
add_timer(&sk->timer);
+ sti();
return;
}
sk->inuse = 1;
+ sti();
DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why));
- if (sk->wfront &&
- before(sk->window_seq, sk->wfront->h.seq) &&
+ if (skb_peek(&sk->write_queue) &&
+ before(sk->window_seq, sk->write_queue.next->h.seq) &&
sk->send_head == NULL &&
sk->ack_backlog == 0 &&
sk->state != TCP_TIME_WAIT)
if (sk->ack_backlog) {
sk->prot->read_wakeup (sk);
if (! sk->dead)
- wake_up_interruptible (sk->sleep);
+ sk->data_ready(sk,0);
}
/* Now we need to figure out why the socket was on the timer. */
sk->state = TCP_CLOSE;
delete_timer (sk);
/* Kill the ARP entry in case the hardware has changed. */
- arp_destroy_maybe (sk->daddr);
+ arp_destroy (sk->daddr, 0);
if (!sk->dead)
- wake_up_interruptible (sk->sleep);
+ sk->state_change(sk);
sk->shutdown = SHUTDOWN_MASK;
reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME);
release_sock (sk);
/* It could be we got here because we needed to send an ack.
* So we need to check for that.
*/
- if (sk->send_head) {
- if (jiffies < (sk->send_head->when + sk->rto)) {
- reset_timer (sk, TIME_WRITE,
- (sk->send_head->when + sk->rto - jiffies));
+ {
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ skb = sk->send_head;
+ if (!skb) {
+ restore_flags(flags);
+ } else {
+ if (jiffies < skb->when + sk->rto) {
+ reset_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
+ restore_flags(flags);
release_sock (sk);
break;
}
+ restore_flags(flags);
/* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
sk->retransmits, sk->packets_out, sk->cong_window); */
DPRINTF ((DBG_TMR, "retransmitting.\n"));
if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n"));
- arp_destroy_maybe (sk->daddr);
+ arp_destroy (sk->daddr, 0);
ip_route_check (sk->daddr);
}
if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
}
release_sock (sk);
break;
+ }
case TIME_KEEPOPEN:
/* Send something to keep the connection open. */
if (sk->prot->write_wakeup)
if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n"));
- arp_destroy_maybe (sk->daddr);
+ arp_destroy (sk->daddr, 0);
ip_route_check (sk->daddr);
release_sock (sk);
break;
}
if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n"));
- arp_destroy_maybe (sk->daddr);
+ arp_destroy (sk->daddr, 0);
sk->err = ETIMEDOUT;
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) {
sk->state = TCP_TIME_WAIT;
if (!sk->dead)
- wake_up_interruptible (sk->sleep);
+ sk->state_change(sk);
release_sock (sk);
} else {
sk->prot->close (sk, 1);
release_sock (sk);
break;
default:
- printk ("net timer expired - reason unknown, sk=%08X\n", (int)sk);
+ printk ("net_timer: timer expired - reason unknown\n");
release_sock (sk);
break;
}
* Alan Cox : Broadcasting without option set returns EACCES.
* Alan Cox : No wakeup calls. Instead we now use the callbacks.
* Alan Cox : Use ip_tos and ip_ttl
+ * Alan Cox : SNMP Mibs
*
*
* This program is free software; you can redistribute it and/or
#include <linux/timer.h>
#include <linux/termios.h>
#include <linux/mm.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
#include "sock.h"
#include "udp.h"
#include "icmp.h"
+/*
+ * SNMP MIB for the UDP layer
+ */
-#define min(a,b) ((a)<(b)?(a):(b))
+struct udp_mib udp_statistics;
-static void
-print_udp(struct udphdr *uh)
+
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+static void print_udp(struct udphdr *uh)
{
- if (inet_debug != DBG_UDP) return;
-
- if (uh == NULL) {
- printk("(NULL)\n");
- return;
- }
- printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
- printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
+ if (inet_debug != DBG_UDP)
+ return;
+
+ if (uh == NULL)
+ {
+ printk("(NULL)\n");
+ return;
+ }
+ printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
+ printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
}
* header points to the first 8 bytes of the udp header. We need
* to find the appropriate port.
*/
-void
-udp_err(int err, unsigned char *header, unsigned long daddr,
+
+void udp_err(int err, unsigned char *header, unsigned long daddr,
unsigned long saddr, struct inet_protocol *protocol)
{
- struct udphdr *th;
- struct sock *sk;
- struct iphdr *ip=(struct iphdr *)header;
+ struct udphdr *th;
+ struct sock *sk;
+ struct iphdr *ip=(struct iphdr *)header;
- header += 4*ip->ihl;
-
- th = (struct udphdr *)header;
+ header += 4*ip->ihl;
+
+ /*
+ * Find the 8 bytes of post IP header ICMP included for usA
+ */
+ th = (struct udphdr *)header;
- DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\
-sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));
+ DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\
+ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));
- sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);
+ sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);
- if (sk == NULL)
- return; /* No socket for error */
+ if (sk == NULL)
+ return; /* No socket for error */
- if (err < 0) /* As per the calling spec */
- {
- sk->err = -err;
- sk->error_report(sk); /* User process wakes to see error */
- return;
- }
-
- if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) { /* Slow down! */
- if (sk->cong_window > 1)
- sk->cong_window = sk->cong_window/2;
- return;
- }
-
- sk->err = icmp_err_convert[err & 0xff].errno;
-
- /* It's only fatal if we have connected to them. */
- if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) {
- sk->err=ECONNREFUSED;
- }
- sk->error_report(sk);
+ if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8))
+ { /* Slow down! */
+ if (sk->cong_window > 1)
+ sk->cong_window = sk->cong_window/2;
+ return;
+ }
+
+ sk->err = icmp_err_convert[err & 0xff].errno;
+
+ /*
+ * It's only fatal if we have connected to them. I'm not happy
+ * with this code. Some BSD comparisons need doing.
+ */
+
+ if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED)
+ {
+ sk->err=ECONNREFUSED;
+ }
+
+ sk->error_report(sk);
}
-static unsigned short
-udp_check(struct udphdr *uh, int len,
- unsigned long saddr, unsigned long daddr)
+static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr)
{
- unsigned long sum;
+ unsigned long sum;
- DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
+ DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
uh, len, saddr, daddr));
- print_udp(uh);
-
- __asm__("\t addl %%ecx,%%ebx\n"
- "\t adcl %%edx,%%ebx\n"
- "\t adcl $0, %%ebx\n"
- : "=b"(sum)
- : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)
- : "cx","bx","dx" );
-
- if (len > 3) {
- __asm__("\tclc\n"
- "1:\n"
- "\t lodsl\n"
- "\t adcl %%eax, %%ebx\n"
- "\t loop 1b\n"
- "\t adcl $0, %%ebx\n"
- : "=b"(sum) , "=S"(uh)
- : "0"(sum), "c"(len/4) ,"1"(uh)
- : "ax", "cx", "bx", "si" );
- }
-
- /* Convert from 32 bits to 16 bits. */
- __asm__("\t movl %%ebx, %%ecx\n"
- "\t shrl $16,%%ecx\n"
- "\t addw %%cx, %%bx\n"
- "\t adcw $0, %%bx\n"
- : "=b"(sum)
- : "0"(sum)
- : "bx", "cx");
-
- /* Check for an extra word. */
- if ((len & 2) != 0) {
- __asm__("\t lodsw\n"
- "\t addw %%ax,%%bx\n"
- "\t adcw $0, %%bx\n"
- : "=b"(sum), "=S"(uh)
- : "0"(sum) ,"1"(uh)
- : "si", "ax", "bx");
- }
-
- /* Now check for the extra byte. */
- if ((len & 1) != 0) {
- __asm__("\t lodsb\n"
- "\t movb $0,%%ah\n"
- "\t addw %%ax,%%bx\n"
- "\t adcw $0, %%bx\n"
- : "=b"(sum)
- : "0"(sum) ,"S"(uh)
- : "si", "ax", "bx");
- }
-
- /* We only want the bottom 16 bits, but we never cleared the top 16. */
- return((~sum) & 0xffff);
+ print_udp(uh);
+
+ __asm__( "\t addl %%ecx,%%ebx\n"
+ "\t adcl %%edx,%%ebx\n"
+ "\t adcl $0, %%ebx\n"
+ : "=b"(sum)
+ : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)
+ : "cx","bx","dx" );
+
+ if (len > 3)
+ {
+ __asm__("\tclc\n"
+ "1:\n"
+ "\t lodsl\n"
+ "\t adcl %%eax, %%ebx\n"
+ "\t loop 1b\n"
+ "\t adcl $0, %%ebx\n"
+ : "=b"(sum) , "=S"(uh)
+ : "0"(sum), "c"(len/4) ,"1"(uh)
+ : "ax", "cx", "bx", "si" );
+ }
+
+ /*
+ * Convert from 32 bits to 16 bits.
+ */
+
+ __asm__("\t movl %%ebx, %%ecx\n"
+ "\t shrl $16,%%ecx\n"
+ "\t addw %%cx, %%bx\n"
+ "\t adcw $0, %%bx\n"
+ : "=b"(sum)
+ : "0"(sum)
+ : "bx", "cx");
+
+ /*
+ * Check for an extra word.
+ */
+
+ if ((len & 2) != 0)
+ {
+ __asm__("\t lodsw\n"
+ "\t addw %%ax,%%bx\n"
+ "\t adcw $0, %%bx\n"
+ : "=b"(sum), "=S"(uh)
+ : "0"(sum) ,"1"(uh)
+ : "si", "ax", "bx");
+ }
+
+ /*
+ * Now check for the extra byte.
+ */
+
+ if ((len & 1) != 0)
+ {
+ __asm__("\t lodsb\n"
+ "\t movb $0,%%ah\n"
+ "\t addw %%ax,%%bx\n"
+ "\t adcw $0, %%bx\n"
+ : "=b"(sum)
+ : "0"(sum) ,"S"(uh)
+ : "si", "ax", "bx");
+ }
+
+ /*
+ * We only want the bottom 16 bits, but we never cleared the top 16.
+ */
+
+ return((~sum) & 0xffff);
}
+/*
+ * Generate UDP checksums. These may be disabled, eg for fast NFS over ethernet
+ * We default them enabled.. if you turn them off you either know what you are
+ * doing or get burned...
+ */
-static void
-udp_send_check(struct udphdr *uh, unsigned long saddr,
+static void udp_send_check(struct udphdr *uh, unsigned long saddr,
unsigned long daddr, int len, struct sock *sk)
{
- uh->check = 0;
- if (sk && sk->no_check)
- return;
- uh->check = udp_check(uh, len, saddr, daddr);
- if (uh->check == 0) uh->check = 0xffff;
+ uh->check = 0;
+ if (sk && sk->no_check)
+ return;
+ uh->check = udp_check(uh, len, saddr, daddr);
+
+ /*
+ * FFFF and 0 are the same, pick the right one as 0 in the
+ * actual field means no checksum.
+ */
+
+ if (uh->check == 0)
+ uh->check = 0xffff;
}
-static int
-udp_send(struct sock *sk, struct sockaddr_in *sin,
+static int udp_send(struct sock *sk, struct sockaddr_in *sin,
unsigned char *from, int len)
{
- struct sk_buff *skb;
- struct device *dev;
- struct udphdr *uh;
- unsigned char *buff;
- unsigned long saddr;
- int size, tmp;
- int err;
+ struct sk_buff *skb;
+ struct device *dev;
+ struct udphdr *uh;
+ unsigned char *buff;
+ unsigned long saddr;
+ int size, tmp;
+ int err;
- DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
+ DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),
from, len));
- err=verify_area(VERIFY_READ, from, len);
- if(err)
- return(err);
-
- /* Allocate a copy of the packet. */
- size = sizeof(struct sk_buff) + sk->prot->max_header + len;
- skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
- if (skb == NULL) return(-ENOMEM);
-
- skb->mem_addr = skb;
- skb->mem_len = size;
- skb->sk = NULL; /* to avoid changing sk->saddr */
- skb->free = 1;
- skb->arp = 0;
-
- /* Now build the IP and MAC header. */
- buff = skb->data;
- saddr = 0;
- dev = NULL;
- DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
- saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
- tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
- &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
- skb->sk=sk; /* So memory is freed correctly */
-
- if (tmp < 0 ) {
- sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
- return(tmp);
- }
- buff += tmp;
- saddr = dev->pa_addr;
- DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
+ err=verify_area(VERIFY_READ, from, len);
+ if(err)
+ return(err);
+
+ /*
+ * Allocate an sk_buff copy of the packet.
+ */
+
+ size = sk->prot->max_header + len;
+ skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
- skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */
- skb->dev = dev;
- /* Fill in the UDP header. */
- uh = (struct udphdr *) buff;
- uh->len = htons(len + sizeof(struct udphdr));
- uh->source = sk->dummy_th.source;
- uh->dest = sin->sin_port;
- buff = (unsigned char *) (uh + 1);
+ if (skb == NULL)
+ return(-ENOMEM);
+
+ skb->sk = NULL; /* to avoid changing sk->saddr */
+ skb->free = 1;
+
+ /*
+ * Now build the IP and MAC header.
+ */
+
+ buff = skb->data;
+ saddr = 0;
+ dev = NULL;
+ DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
+ saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
+ tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
+ &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
+ skb->sk=sk; /* So memory is freed correctly */
+
+ /*
+ * Unable to put a header on the packet.
+ */
+
+ if (tmp < 0 )
+ {
+ sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ return(tmp);
+ }
+
+ buff += tmp;
+ saddr = skb->saddr; /*dev->pa_addr;*/
+ DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
- /* Copy the user data. */
- memcpy_fromfs(buff, from, len);
+ skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */
+ skb->dev = dev;
+
+ /*
+ * Fill in the UDP header.
+ */
+
+ uh = (struct udphdr *) buff;
+ uh->len = htons(len + sizeof(struct udphdr));
+ uh->source = sk->dummy_th.source;
+ uh->dest = sin->sin_port;
+ buff = (unsigned char *) (uh + 1);
- /* Set up the UDP checksum. */
- udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
+ /*
+ * Copy the user data.
+ */
+
+ memcpy_fromfs(buff, from, len);
- /* Send the datagram to the interface. */
- sk->prot->queue_xmit(sk, dev, skb, 1);
+ /*
+ * Set up the UDP checksum.
+ */
+
+ udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
- return(len);
+ /*
+ * Send the datagram to the interface.
+ */
+
+ udp_statistics.UdpOutDatagrams++;
+
+ sk->prot->queue_xmit(sk, dev, skb, 1);
+ return(len);
}
-static int
-udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
+static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
unsigned flags, struct sockaddr_in *usin, int addr_len)
{
- struct sockaddr_in sin;
- int tmp;
- int err;
+ struct sockaddr_in sin;
+ int tmp;
+ int err;
- DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
+ DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
- /* Check the flags. */
- if (flags)
- return(-EINVAL);
- if (len < 0)
- return(-EINVAL);
- if (len == 0)
- return(0);
+ /*
+ * Check the flags. We support no flags for UDP sending
+ */
+ if (flags)
+ return(-EINVAL);
+ if (len < 0)
+ return(-EINVAL);
+ if (len == 0)
+ return(0);
- /* Get and verify the address. */
- if (usin) {
- if (addr_len < sizeof(sin)) return(-EINVAL);
- err=verify_area(VERIFY_READ, usin, sizeof(sin));
- if(err)
- return err;
- memcpy_fromfs(&sin, usin, sizeof(sin));
- if (sin.sin_family && sin.sin_family != AF_INET)
- return(-EINVAL);
- if (sin.sin_port == 0)
- return(-EINVAL);
- } else {
- if (sk->state != TCP_ESTABLISHED) return(-EINVAL);
- sin.sin_family = AF_INET;
- sin.sin_port = sk->dummy_th.dest;
- sin.sin_addr.s_addr = sk->daddr;
- }
+ /*
+ * Get and verify the address.
+ */
+
+ if (usin)
+ {
+ if (addr_len < sizeof(sin))
+ return(-EINVAL);
+ err=verify_area(VERIFY_READ, usin, sizeof(sin));
+ if(err)
+ return err;
+ memcpy_fromfs(&sin, usin, sizeof(sin));
+ if (sin.sin_family && sin.sin_family != AF_INET)
+ return(-EINVAL);
+ if (sin.sin_port == 0)
+ return(-EINVAL);
+ }
+ else
+ {
+ if (sk->state != TCP_ESTABLISHED)
+ return(-EINVAL);
+ sin.sin_family = AF_INET;
+ sin.sin_port = sk->dummy_th.dest;
+ sin.sin_addr.s_addr = sk->daddr;
+ }
- if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
- return -EACCES; /* Must turn broadcast on first */
- sk->inuse = 1;
+ /*
+ * BSD socket semantics. You must set SO_BROADCAST to permit
+ * broadcasting of data.
+ */
+ if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+ return -EACCES; /* Must turn broadcast on first */
+
+ sk->inuse = 1;
- /* Send the packet. */
- tmp = udp_send(sk, &sin, from, len);
+ /* Send the packet. */
+ tmp = udp_send(sk, &sin, from, len);
- /* The datagram has been sent off. Release the socket. */
- release_sock(sk);
- return(tmp);
+ /* The datagram has been sent off. Release the socket. */
+ release_sock(sk);
+ return(tmp);
}
+/*
+ * In BSD SOCK_DGRAM a write is just like a send.
+ */
-static int
-udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
+static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
unsigned flags)
{
- return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
+ return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
}
-int
-udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+/*
+ * IOCTL requests applicable to the UDP protocol
+ */
+
+int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- int err;
- switch(cmd) {
- case DDIOCSDBG:
+ int err;
+ switch(cmd)
+ {
+ case DDIOCSDBG:
{
int val;
}
}
break;
- case TIOCOUTQ:
+ case TIOCOUTQ:
{
unsigned long amount;
return(0);
}
- case TIOCINQ:
+ case TIOCINQ:
{
struct sk_buff *skb;
unsigned long amount;
if (sk->state == TCP_LISTEN) return(-EINVAL);
amount = 0;
- skb = sk->rqueue;
+ skb = skb_peek(&sk->receive_queue);
if (skb != NULL) {
/*
* We will only return the amount
return(0);
}
- default:
- return(-EINVAL);
- }
- return(0);
+ default:
+ return(-EINVAL);
+ }
+ return(0);
}
/*
- * This should be easy, if there is something there we\
- * return it, otherwise we block.
+ * This should be easy, if there is something there we\
+ * return it, otherwise we block.
*/
-int
-udp_recvfrom(struct sock *sk, unsigned char *to, int len,
+
+int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
int noblock, unsigned flags, struct sockaddr_in *sin,
int *addr_len)
{
- int copied = 0;
- struct sk_buff *skb;
- int er;
-
+ int copied = 0;
+ struct sk_buff *skb;
+ int er;
- /*
- * This will pick up errors that occured while the program
- * was doing something else.
- */
- if (sk->err) {
- int err;
- err = -sk->err;
- sk->err = 0;
- return(err);
- }
-
- if (len == 0)
- return(0);
- if (len < 0)
- return(-EINVAL);
+ /*
+ * This will pick up errors that occured while the program
+ * was doing something else.
+ */
+
+ if (sk->err)
+ {
+ int err;
+
+ err = -sk->err;
+ sk->err = 0;
+ return(err);
+ }
+
+ if (len == 0)
+ return(0);
+ if (len < 0)
+ return(-EINVAL);
- if (addr_len) {
- er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+ /*
+ * Check any passed addresses
+ */
+
+ if (addr_len)
+ {
+ er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+ if(er)
+ return(er);
+ put_fs_long(sizeof(*sin), addr_len);
+ }
+
+ if(sin)
+ {
+ er=verify_area(VERIFY_WRITE, sin, sizeof(*sin));
+ if(er)
+ return(er);
+ }
+
+ /*
+ * Check the buffer we were given
+ */
+
+ er=verify_area(VERIFY_WRITE,to,len);
if(er)
- return(er);
- put_fs_long(sizeof(*sin), addr_len);
- }
- if(sin)
- {
- er=verify_area(VERIFY_WRITE, sin, sizeof(*sin));
- if(er)
- return(er);
- }
- er=verify_area(VERIFY_WRITE,to,len);
- if(er)
- return er;
- skb=skb_recv_datagram(sk,flags,noblock,&er);
- if(skb==NULL)
- return er;
- copied = min(len, skb->len);
+ return er;
+
+ /*
+ * From here the generic datagram does a lot of the work. Come
+ * the finished NET3, it will do _ALL_ the work!
+ */
+
+ skb=skb_recv_datagram(sk,flags,noblock,&er);
+ if(skb==NULL)
+ return er;
+
+ copied = min(len, skb->len);
- /* FIXME : should use udp header size info value */
- skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
+ /*
+ * FIXME : should use udp header size info value
+ */
+
+ skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
+ sk->stamp=skb->stamp;
- /* Copy the address. */
- if (sin) {
- struct sockaddr_in addr;
+ /* Copy the address. */
+ if (sin)
+ {
+ struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = skb->h.uh->source;
- addr.sin_addr.s_addr = skb->daddr;
- memcpy_tofs(sin, &addr, sizeof(*sin));
- }
+ addr.sin_family = AF_INET;
+ addr.sin_port = skb->h.uh->source;
+ addr.sin_addr.s_addr = skb->daddr;
+ memcpy_tofs(sin, &addr, sizeof(*sin));
+ }
- skb_free_datagram(skb);
- release_sock(sk);
- return(copied);
+ skb_free_datagram(skb);
+ release_sock(sk);
+ return(copied);
}
+/*
+ * Read has the same semantics as recv in SOCK_DGRAM
+ */
-int
-udp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
+int udp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
unsigned flags)
{
- return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+ return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
}
if (sin.sin_family && sin.sin_family != AF_INET)
return(-EAFNOSUPPORT);
- if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+ if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
return -EACCES; /* Must turn broadcast on first */
sk->daddr = sin.sin_addr.s_addr;
}
-/* All we need to do is get the socket, and then do a checksum. */
-int
-udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+/*
+ * All we need to do is get the socket, and then do a checksum.
+ */
+
+int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
unsigned long daddr, unsigned short len,
unsigned long saddr, int redo, struct inet_protocol *protocol)
{
- struct sock *sk;
- struct udphdr *uh;
-
- uh = (struct udphdr *) skb->h.uh;
- sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
- if (sk == NULL)
- {
- if (chk_addr(daddr) == IS_MYADDR)
- {
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
- }
+ struct sock *sk;
+ struct udphdr *uh;
+
/*
- * Hmm. We got an UDP broadcast to a port to which we
- * don't wanna listen. The only thing we can do now is
- * to ignore the packet... -FvK
+ * Get the header.
*/
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
+ uh = (struct udphdr *) skb->h.uh;
+
+ ip_statistics.IpInDelivers++;
- if (uh->check && udp_check(uh, len, saddr, daddr)) {
- DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
+
+ sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
+ if (sk == NULL)
+ {
+ udp_statistics.UdpNoPorts++;
+ if (ip_chk_addr(daddr) == IS_MYADDR)
+ {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+ }
+ /*
+ * Hmm. We got an UDP broadcast to a port to which we
+ * don't wanna listen. Ignore it.
+ */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ if (uh->check && udp_check(uh, len, saddr, daddr))
+ {
+ printk("UDP: bad checksum.\n");
+ DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
+ udp_statistics.UdpInErrors++;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
- skb->sk = sk;
- skb->dev = dev;
- skb->len = len;
+ skb->sk = sk;
+ skb->dev = dev;
+ skb->len = len;
-/* These are supposed to be switched. */
- skb->daddr = saddr;
- skb->saddr = daddr;
+ /*
+ * These are supposed to be switched.
+ */
+
+ skb->daddr = saddr;
+ skb->saddr = daddr;
- /* Charge it to the socket. */
- if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
- {
- skb->sk = NULL;
- kfree_skb(skb, FREE_WRITE);
- release_sock(sk);
- return(0);
- }
- sk->rmem_alloc += skb->mem_len;
+ /*
+ * Charge it to the socket, dropping if the queue is full.
+ */
+
+ if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
+ {
+ udp_statistics.UdpInErrors++;
+ ip_statistics.IpInDiscards++;
+ ip_statistics.IpInDelivers--;
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ release_sock(sk);
+ return(0);
+ }
+ sk->rmem_alloc += skb->mem_len;
+ udp_statistics.UdpInDatagrams++;
+
+ /*
+ * At this point we should print the thing out.
+ */
- /* At this point we should print the thing out. */
- DPRINTF((DBG_UDP, "<< \n"));
- print_udp(uh);
+ DPRINTF((DBG_UDP, "<< \n"));
+ print_udp(uh);
- /* Now add it to the data chain and wake things up. */
+ /*
+ * Now add it to the data chain and wake things up.
+ */
- skb_queue_tail(&sk->rqueue,skb);
+ skb->len = len - sizeof(*uh);
+ skb_queue_tail(&sk->receive_queue,skb);
- skb->len = len - sizeof(*uh);
- if (!sk->dead)
- sk->data_ready(sk,skb->len);
+ if (!sk->dead)
+ sk->data_ready(sk,skb->len);
- release_sock(sk);
- return(0);
+ release_sock(sk);
+ return(0);
}
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <stdarg.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/skbuff.h>
-/* Display an IP address in readable format. */
+/*
+ * Display an IP address in readable format.
+ */
+
char *in_ntoa(unsigned long in)
{
- static char buff[18];
- register char *p;
+ static char buff[18];
+ char *p;
- p = (char *) ∈
- sprintf(buff, "%d.%d.%d.%d",
- (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
- return(buff);
+ p = (char *) ∈
+ sprintf(buff, "%d.%d.%d.%d",
+ (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
+ return(buff);
}
-/* Convert an ASCII string to binary IP. */
-unsigned long
-in_aton(char *str)
+/*
+ * Convert an ASCII string to binary IP.
+ */
+
+unsigned long in_aton(char *str)
{
- unsigned long l;
- unsigned int val;
- int i;
-
- l = 0;
- for (i = 0; i < 4; i++) {
- l <<= 8;
- if (*str != '\0') {
- val = 0;
- while (*str != '\0' && *str != '.') {
- val *= 10;
- val += *str - '0';
- str++;
+ unsigned long l;
+ unsigned int val;
+ int i;
+
+ l = 0;
+ for (i = 0; i < 4; i++)
+ {
+ l <<= 8;
+ if (*str != '\0')
+ {
+ val = 0;
+ while (*str != '\0' && *str != '.')
+ {
+ val *= 10;
+ val += *str - '0';
+ str++;
+ }
+ l |= val;
+ if (*str != '\0')
+ str++;
}
- l |= val;
- if (*str != '\0') str++;
}
- }
- return(htonl(l));
+ return(htonl(l));
}
-void
-dprintf(int level, char *fmt, ...)
+/*
+ * Debugging print out
+ */
+
+void dprintf(int level, char *fmt, ...)
{
- va_list args;
- char *buff;
- extern int vsprintf(char * buf, const char * fmt, va_list args);
-
- if (level != inet_debug) return;
-
- buff = (char *) kmalloc(256, GFP_ATOMIC);
- if (buff != NULL) {
- va_start(args, fmt);
- vsprintf(buff, fmt, args);
- va_end(args);
- printk(buff);
- kfree(buff);
- }
+ va_list args;
+ char *buff;
+ extern int vsprintf(char * buf, const char * fmt, va_list args);
+
+ if (level != inet_debug)
+ return;
+
+ buff = (char *) kmalloc(256, GFP_ATOMIC);
+ if (buff != NULL)
+ {
+ va_start(args, fmt);
+ vsprintf(buff, fmt, args);
+ va_end(args);
+ printk(buff);
+ kfree(buff);
+ }
+ else
+ printk("Debugging output lost: No free memory.\n");
}
-
-int
-dbg_ioctl(void *arg, int level)
+/*
+ * Debugging ioctl() requests
+ */
+
+int dbg_ioctl(void *arg, int level)
{
- int val;
- int err;
+ int val;
+ int err;
- if (!suser()) return(-EPERM);
- err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
- if(err)
- return err;
- val = get_fs_long((int *)arg);
- switch(val) {
- case 0: /* OFF */
- inet_debug = DBG_OFF;
- break;
- case 1: /* ON, INET */
- inet_debug = level;
- break;
-
- case DBG_RT: /* modules */
- case DBG_DEV:
- case DBG_ETH:
- case DBG_PROTO:
- case DBG_TMR:
- case DBG_PKT:
- case DBG_RAW:
-
- case DBG_LOOPB: /* drivers */
- case DBG_SLIP:
-
- case DBG_ARP: /* protocols */
- case DBG_IP:
- case DBG_ICMP:
- case DBG_TCP:
- case DBG_UDP:
-
- inet_debug = val;
- break;
-
- default:
- return(-EINVAL);
- }
-
- return(0);
+ if (!suser())
+ return(-EPERM);
+ err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
+ if(err)
+ return err;
+ val = get_fs_long((int *)arg);
+ switch(val)
+ {
+ case 0: /* OFF */
+ inet_debug = DBG_OFF;
+ break;
+ case 1: /* ON, INET */
+ inet_debug = level;
+ break;
+
+ case DBG_RT: /* modules */
+ case DBG_DEV:
+ case DBG_ETH:
+ case DBG_PROTO:
+ case DBG_TMR:
+ case DBG_PKT:
+ case DBG_RAW:
+
+ case DBG_LOOPB: /* drivers */
+ case DBG_SLIP:
+
+ case DBG_ARP: /* protocols */
+ case DBG_IP:
+ case DBG_ICMP:
+ case DBG_TCP:
+ case DBG_UDP:
+
+ inet_debug = val;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+
+ return(0);
}
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
+ *
+ * This module is effectively the top level interface to the BSD socket
+ * paradigm. Because it is very simple it works well for Unix domain sockets,
+ * but requires a whole layer of substructure for the other protocols.
+ *
+ * In addition it lacks an effective kernel -> kernel interface to go with
+ * the user one.
*/
#include <linux/config.h>
/* Called from PROCfs. */
-int unix_get_info(char *buffer)
+int unix_get_info(char *buffer, char **start, off_t offset, int length)
{
- char *pos;
- int i;
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
+ int i;
- pos = buffer;
- pos += sprintf(pos, "Num RefCount Protocol Flags Type St Path\n");
+ len += sprintf(buffer, "Num RefCount Protocol Flags Type St Path\n");
- for(i = 0; i < NSOCKETS; i++) {
- if (unix_datas[i].refcnt>0) {
- pos += sprintf(pos, "%2d: %08X %08X %08lX %04X %02X", i,
- unix_datas[i].refcnt,
- unix_datas[i].protocol,
- unix_datas[i].socket->flags,
- unix_datas[i].socket->type,
- unix_datas[i].socket->state
- );
+ for(i = 0; i < NSOCKETS; i++)
+ {
+ if (unix_datas[i].refcnt>0)
+ {
+ len += sprintf(buffer+len, "%2d: %08X %08X %08lX %04X %02X", i,
+ unix_datas[i].refcnt,
+ unix_datas[i].protocol,
+ unix_datas[i].socket->flags,
+ unix_datas[i].socket->type,
+ unix_datas[i].socket->state
+ );
- /* If socket is bound to a filename, we'll print it. */
- if(unix_datas[i].sockaddr_len>0) {
- pos += sprintf(pos, " %s\n",
+ /* If socket is bound to a filename, we'll print it. */
+ if(unix_datas[i].sockaddr_len>0)
+ {
+ len += sprintf(buffer+len, " %s\n",
unix_datas[i].sockaddr_un.sun_path);
- } else { /* just add a newline */
- *pos='\n';
- pos++;
- *pos='\0';
- }
-
- /*
- * Check whether buffer _may_ overflow in the next loop.
- * Since sockets may have very very long paths, we make
- * PATH_MAX+80 the minimum space left for a new line.
- */
- if (pos > buffer+PAGE_SIZE-80-PATH_MAX) {
- printk("UNIX: netinfo: oops, too many sockets.\n");
- return(pos - buffer);
+ }
+ else
+ { /* just add a newline */
+ buffer[len++]='\n';
+ buffer[len++]='\0';
+ }
+
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
}
}
- }
- return(pos - buffer);
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
old_fs = get_fs();
set_fs(get_ds());
i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0);
- if (i == -EEXIST)
- i = -EADDRINUSE;
- if (i == 0)
- i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
+ if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
set_fs(old_fs);
if (i < 0) {
- dprintf(1, "UNIX: bind: can't open socket %s\n", fname);
+ printk("UNIX: bind: can't open socket %s\n", fname);
return(i);
}
upd->sockaddr_len = sockaddr_len; /* now its legal */