📄 dev.c
字号:
/*
* 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)
*
* 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 <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 "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"
#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
#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
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
#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;
static unsigned long ip_bcast = 0;
/* Return the lesser of the two values. */
static unsigned long
min(unsigned long a, unsigned long b)
{
if (a < b) return(a);
return(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);
}
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...).
*/
unsigned long
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);
}
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;
}
}
}
/*
* 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
{
for(p1=ptype_base;p1->next!=NULL;p1=p1->next);
p1->next=pt;
}
}
else
ptype_base = pt;
}
/* Remove a protocol ID from the list. This will change soon. */
void
dev_remove_pack(struct packet_type *pt)
{
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();
return;
}
if (pt1->next -> type == pt ->type && pt->type != NET16(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);
}
/* Find an interface that can handle addresses for a certain address. */
struct device * 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;
}
/* Prepare an interface for use. */
int
dev_open(struct device *dev)
{
int ret = 0;
if (dev->open)
ret = dev->open(dev);
if (ret == 0)
dev->flags |= (IFF_UP | IFF_RUNNING);
return(ret);
}
/* Completely shutdown an interface. */
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)
{
struct sk_buff *skb;
while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
if(skb->free)
kfree_skb(skb,FREE_WRITE);
ct++;
}
}
return(0);
}
/* 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 */
/* at the front or the back of the */
/* queue. */
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;
}
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;
}
if (pri < 0) {
pri = -pri-1;
where = 1;
}
if (pri >= DEV_NUMBUFFS) {
printk("bad priority in dev_queue_xmit.\n");
pri = 1;
}
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();
}
/*
* Receive a packet from a device driver and queue it for the upper
* (protocol) levels. It always succeeds.
*/
void
netif_rx(struct sk_buff *skb)
{
/* Set any necessary flags. */
skb->sk = NULL;
skb->free = 1;
/* and add it to the "backlog" queue. */
IS_SKB(skb);
skb_queue_tail(&backlog,skb);
/* If any packet arrived, mark it for processing. */
if (backlog != NULL) 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.
*
* Return values: 1 <- exit I can't do any more
* 0 <- feed me more (i.e. "done", "OK").
*/
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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -