📄 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_IPXstatic 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_AX25static struct packet_type ax25_packet_type = { NET16(ETH_P_AX25), 0, ax25_rcv, NULL,#ifdef CONFIG_IPX &ipx_packet_type#else NULL#endif};#endifstatic 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 longmin(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 longget_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);}intip_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 longmy_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. */voiddev_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. */voiddev_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. */intdev_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. */intdev_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. */voiddev_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. */voidnetif_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"). */intdev_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 + -