📄 dev.c
字号:
/* * NET3 Protocol independent 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> * David Hinds <dhinds@allegro.stanford.edu> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * Adam Sulmicki <adam@cfar.umd.edu> * * Changes: * Marcelo Tosatti <marcelo@conectiva.com.br> : dont accept mtu 0 or < * Alan Cox : device private ioctl copies fields back. * Alan Cox : Transmit queue code does relevant stunts to * keep the queue safe. * Alan Cox : Fixed double lock. * Alan Cox : Fixed promisc NULL pointer trap * ???????? : Support the full private ioctl range * Alan Cox : Moved ioctl permission check into drivers * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI * Alan Cox : 100 backlog just doesn't cut it when * you start doing multicast video 8) * Alan Cox : Rewrote net_bh and list manager. * Alan Cox : Fix ETH_P_ALL echoback lengths. * Alan Cox : Took out transmit every packet pass * Saved a few bytes in the ioctl handler * Alan Cox : Network driver sets packet type before calling netif_rx. Saves * a function call a packet. * Alan Cox : Hashed net_bh() * Richard Kooijman: Timestamp fixes. * Alan Cox : Wrong field in SIOCGIFDSTADDR * Alan Cox : Device lock protection. * Alan Cox : Fixed nasty side effect of device close changes. * Rudi Cilibrasi : Pass the right thing to set_mac_address() * Dave Miller : 32bit quantity for the device lock to make it work out * on a Sparc. * Bjorn Ekwall : Added KERNELD hack. * Alan Cox : Cleaned up the backlog initialise. * Craig Metz : SIOCGIFCONF fix if space for under * 1 device. * Thomas Bogendoerfer : Return ENODEV for dev_open, if there * is no device open function. * Andi Kleen : Fix error reporting for SIOCGIFCONF * Michael Chastain : Fix signed/unsigned for SIOCGIFCONF * Cyrus Durgin : Cleaned for KMOD * Adam Sulmicki : Bug Fix : Network Device Unload * A network device unload needs to purge * the backlog queue. * Paul Rusty Russel : SIOCSIFNAME * Andrea Arcangeli : dev_clear_backlog() needs the * skb_queue_lock held. */#include <asm/uaccess.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/errno.h>#include <linux/interrupt.h>#include <linux/if_ether.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/notifier.h>#include <linux/skbuff.h>#include <net/sock.h>#include <linux/rtnetlink.h>#include <net/slhc.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <net/br.h>#include <net/dst.h>#include <net/pkt_sched.h>#include <net/profile.h>#include <linux/init.h>#include <linux/kmod.h>#ifdef CONFIG_NET_RADIO#include <linux/wireless.h>#endif /* CONFIG_NET_RADIO */#ifdef CONFIG_PLIPextern int plip_init(void);#endifNET_PROFILE_DEFINE(dev_queue_xmit)NET_PROFILE_DEFINE(net_bh)NET_PROFILE_DEFINE(net_bh_skb)const char *if_port_text[] = { "unknown", "BNC", "10baseT", "AUI", "100baseT", "100baseTX", "100baseFX"};/* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. * * Why 16. Because with 16 the only overlap we get on a hash of the * low nibble of the protocol value is RARP/SNAP/X.25. * * 0800 IP * 0001 802.3 * 0002 AX.25 * 0004 802.2 * 8035 RARP * 0005 SNAP * 0805 X.25 * 0806 ARP * 8137 IPX * 0009 Localtalk * 86DD IPv6 */struct packet_type *ptype_base[16]; /* 16 way hashed list */struct packet_type *ptype_all = NULL; /* Taps *//* * Device list lock. Setting it provides that interface * will not disappear unexpectedly while kernel sleeps. */atomic_t dev_lockct = ATOMIC_INIT(0);/* * Our notifier list */#ifdef _HURD_struct notifier_block *netdev_chain=NULL;#elsestatic struct notifier_block *netdev_chain=NULL;#endif/* * Device drivers call our routines to queue packets here. We empty the * queue in the bottom half handler. */static struct sk_buff_head backlog;#ifdef CONFIG_NET_FASTROUTEint netdev_fastroute;int netdev_fastroute_obstacles;struct net_fastroute_stats dev_fastroute_stat;#endifstatic void dev_clear_backlog(struct device *dev);/****************************************************************************************** Protocol management and registration routines*******************************************************************************************//* * For efficiency */int netdev_nit=0;/* * Add a protocol ID to the list. Now that the input handler is * smarter we can dispense with all the messy stuff that used to be * here. * * BEWARE!!! Protocol handlers, mangling input packets, * MUST BE last in hash buckets and checking protocol handlers * MUST start from promiscous ptype_all chain in net_bh. * It is true now, do not change it. * Explantion follows: if protocol handler, mangling packet, will * be the first on list, it is not able to sense, that packet * is cloned and should be copied-on-write, so that it will * change it and subsequent readers will get broken packet. * --ANK (980803) */void dev_add_pack(struct packet_type *pt){ int hash;#ifdef CONFIG_NET_FASTROUTE /* Hack to detect packet socket */ if (pt->data) { netdev_fastroute_obstacles++; dev_clear_fastroute(pt->dev); }#endif if(pt->type==htons(ETH_P_ALL)) { netdev_nit++; pt->next=ptype_all; ptype_all=pt; } else { hash=ntohs(pt->type)&15; pt->next = ptype_base[hash]; ptype_base[hash] = pt; }}/* * Remove a protocol ID from the list. */void dev_remove_pack(struct packet_type *pt){ struct packet_type **pt1; if(pt->type==htons(ETH_P_ALL)) { netdev_nit--; pt1=&ptype_all; } else pt1=&ptype_base[ntohs(pt->type)&15]; for(; (*pt1)!=NULL; pt1=&((*pt1)->next)) { if(pt==(*pt1)) { *pt1=pt->next; synchronize_bh();#ifdef CONFIG_NET_FASTROUTE if (pt->data) netdev_fastroute_obstacles--;#endif return; } } printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);}/***************************************************************************************** Device Interface Subroutines******************************************************************************************//* * Find an interface by name. */struct device *dev_get(const char *name){ struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (strcmp(dev->name, name) == 0) return(dev); } return NULL;}struct device * dev_get_by_index(int ifindex){ struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->ifindex == ifindex) return(dev); } return NULL;}struct device *dev_getbyhwaddr(unsigned short type, char *ha){ struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->type == type && memcmp(dev->dev_addr, ha, dev->addr_len) == 0) return(dev); } return(NULL);}/* * Passed a format string - eg "lt%d" it will try and find a suitable * id. Not efficient for many devices, not called a lot.. */int dev_alloc_name(struct device *dev, const char *name){ int i; /* * If you need over 100 please also fix the algorithm... */ for(i=0;i<100;i++) { sprintf(dev->name,name,i); if(dev_get(dev->name)==NULL) return i; } return -ENFILE; /* Over 100 of the things .. bail out! */}struct device *dev_alloc(const char *name, int *err){ struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL); if(dev==NULL) { *err=-ENOBUFS; return NULL; } dev->name=(char *)(dev+1); /* Name string space */ *err=dev_alloc_name(dev,name); if(*err<0) { kfree(dev); return NULL; } return dev;}void netdev_state_change(struct device *dev){ if (dev->flags&IFF_UP) notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);}/* * Find and possibly load an interface. */#ifdef CONFIG_KMODvoid dev_load(const char *name){ if(!dev_get(name) && capable(CAP_SYS_MODULE)) request_module(name);}#elseextern inline void dev_load(const char *unused){;}#endifstatic int default_rebuild_header(struct sk_buff *skb){ printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n", skb->dev ? skb->dev->name : "NULL!!!"); kfree_skb(skb); return 1;}/* * Prepare an interface for use. */int dev_open(struct device *dev){ int ret = 0; /* * Is it already up? */ if (dev->flags&IFF_UP) return 0; /* * Call device private open method */ if (dev->open) ret = dev->open(dev); /* * If it went open OK then: */ if (ret == 0) { /* * nil rebuild_header routine, * that should be never called and used as just bug trap. */ if (dev->rebuild_header == NULL) dev->rebuild_header = default_rebuild_header; /* * Set the flags. */ dev->flags |= (IFF_UP | IFF_RUNNING); /* * Initialize multicasting status */ dev_mc_upload(dev); /* * Wakeup transmit queue engine */ dev_activate(dev); /* * ... and announce new interface. */ notifier_call_chain(&netdev_chain, NETDEV_UP, dev); } return(ret);}#ifdef CONFIG_NET_FASTROUTEstatic __inline__ void dev_do_clear_fastroute(struct device *dev){ if (dev->accept_fastpath) { int i; for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++) dst_release_irqwait(xchg(dev->fastpath+i, NULL)); }}void dev_clear_fastroute(struct device *dev){ if (dev) { dev_do_clear_fastroute(dev); } else { for (dev = dev_base; dev; dev = dev->next) dev_do_clear_fastroute(dev); }}#endif/* * Completely shutdown an interface. */int dev_close(struct device *dev){ if (!(dev->flags&IFF_UP)) return 0; dev_deactivate(dev); dev_lock_wait(); /* * Call the device specific close. This cannot fail. * Only if device is UP */ if (dev->stop) dev->stop(dev); if (dev->start) printk("dev_close: bug %s still running\n", dev->name); /* * Device is now down. */ dev_clear_backlog(dev); dev->flags&=~(IFF_UP|IFF_RUNNING);#ifdef CONFIG_NET_FASTROUTE dev_clear_fastroute(dev);#endif /* * Tell people we are going down */ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); return(0);}/* * Device change register/unregister. These are not inline or static * as we export them to the world. */int register_netdevice_notifier(struct notifier_block *nb){ return notifier_chain_register(&netdev_chain, nb);}int unregister_netdevice_notifier(struct notifier_block *nb){ return notifier_chain_unregister(&netdev_chain,nb);}/* * Support routine. Sends outgoing frames to any network * taps currently in use. */void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev){ struct packet_type *ptype; get_fast_time(&skb->stamp); for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) { /* Never send packets back to the socket * they originated from - MvS (miquels@drinkel.ow.org) */ if ((ptype->dev == dev || !ptype->dev) && ((struct sock *)ptype->data != skb->sk)) { struct sk_buff *skb2; if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) break; /* Code, following below is wrong. The only reason, why it does work is that ONLY packet sockets receive outgoing packets. If such a packet will be (occasionally) received by normal packet handler, which expects that mac header is pulled... */ /* More sensible variant. skb->nh should be correctly set by sender, so that the second statement is just protection against buggy protocols. */ skb2->mac.raw = skb2->data; if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) { if (net_ratelimit()) printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name); skb2->nh.raw = skb2->data; if (dev->hard_header) skb2->nh.raw += dev->hard_header_len; } skb2->h.raw = skb2->nh.raw; skb2->pkt_type = PACKET_OUTGOING; ptype->func(skb2, skb->dev, ptype); } }}/* * Fast path for loopback frames. */void dev_loopback_xmit(struct sk_buff *skb){ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); if (newskb==NULL) return; newskb->mac.raw = newskb->data; skb_pull(newskb, newskb->nh.raw - newskb->data); newskb->pkt_type = PACKET_LOOPBACK; newskb->ip_summed = CHECKSUM_UNNECESSARY; if (newskb->dst==NULL) printk(KERN_DEBUG "BUG: packet without dst looped back 1\n"); netif_rx(newskb);}int dev_queue_xmit(struct sk_buff *skb){ struct device *dev = skb->dev; struct Qdisc *q;#ifdef CONFIG_NET_PROFILE start_bh_atomic(); NET_PROFILE_ENTER(dev_queue_xmit);#endif start_bh_atomic(); q = dev->qdisc; if (q->enqueue) { q->enqueue(skb, q); qdisc_wakeup(dev); end_bh_atomic();#ifdef CONFIG_NET_PROFILE NET_PROFILE_LEAVE(dev_queue_xmit); end_bh_atomic();#endif return 0; } /* The device has no queue. Common case for software devices: loopback, all the sorts of tunnels... Really, it is unlikely that bh protection is necessary here: virtual devices do not generate EOI events. However, it is possible, that they rely on bh protection made by us here. */ if (dev->flags&IFF_UP) { if (netdev_nit) dev_queue_xmit_nit(skb,dev); if (dev->hard_start_xmit(skb, dev) == 0) { end_bh_atomic();#ifdef CONFIG_NET_PROFILE NET_PROFILE_LEAVE(dev_queue_xmit); end_bh_atomic();#endif return 0; } if (net_ratelimit()) printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name); } end_bh_atomic(); kfree_skb(skb);#ifdef CONFIG_NET_PROFILE NET_PROFILE_LEAVE(dev_queue_xmit); end_bh_atomic();#endif return 0;}/*======================================================================= Receiver rotutines =======================================================================*/int netdev_dropping = 0;int netdev_max_backlog = 300;atomic_t netdev_rx_dropped;#ifdef CONFIG_CPU_IS_SLOWint net_cpu_congestion;#endif#ifdef CONFIG_NET_HW_FLOWCONTROLint netdev_throttle_events;static unsigned long netdev_fc_mask = 1;unsigned long netdev_fc_xoff = 0;static struct{ void (*stimul)(struct device *); struct device *dev;} netdev_fc_slots[32];int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev)){ int bit = 0; unsigned long flags; save_flags(flags); cli(); if (netdev_fc_mask != ~0UL) { bit = ffz(netdev_fc_mask); netdev_fc_slots[bit].stimul = stimul; netdev_fc_slots[bit].dev = dev; set_bit(bit, &netdev_fc_mask); clear_bit(bit, &netdev_fc_xoff); } restore_flags(flags); return bit;}void netdev_unregister_fc(int bit){ unsigned long flags; save_flags(flags); cli(); if (bit > 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -