From 967c65a99059fd459b956c1588ce0ba227912c4e Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 16 Dec 1997 05:34:03 +0000 Subject: Merge with Linux 2.1.72, part 1. --- net/core/Makefile | 8 +- net/core/dev.c | 1301 ++++++++++++++++++++++---------------------- net/core/dev_mcast.c | 21 +- net/core/iovec.c | 92 ++-- net/core/net_alias.c | 1464 -------------------------------------------------- net/core/scm.c | 15 +- net/core/skbuff.c | 2 +- net/core/sock.c | 55 +- 8 files changed, 747 insertions(+), 2211 deletions(-) (limited to 'net/core') diff --git a/net/core/Makefile b/net/core/Makefile index b7efbe6b4..2ae776157 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the Linux TCP/IP (INET) layer. +# Makefile for the Linux networking core. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -10,7 +10,7 @@ O_TARGET := core.o O_OBJS := sock.o skbuff.o iovec.o datagram.o dst.o scm.o \ - neighbour.o + neighbour.o rtnetlink.o ifeq ($(CONFIG_SYSCTL),y) O_OBJS += sysctl_net_core.o @@ -24,10 +24,6 @@ ifdef CONFIG_FIREWALL OX_OBJS += firewall.o endif -ifdef CONFIG_NET_ALIAS -O_OBJS += net_alias.o -endif - endif include $(TOPDIR)/Rules.make diff --git a/net/core/dev.c b/net/core/dev.c index 5970c5bab..8d94f6817 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -15,6 +15,7 @@ * Florian la Roche * Alan Cox * David Hinds + * Alexey Kuznetsov * * Changes: * Alan Cox : device private ioctl copies fields back. @@ -61,24 +62,20 @@ #include #include #include -#include #include #include #include -#include #include #include #include -#include -#include #include #include -#include +#include #include #include #include #include -#include +#include #include #ifdef CONFIG_KERNELD #include @@ -90,6 +87,7 @@ extern int plip_init(void); #endif + const char *if_port_text[] = { "unknown", "BNC", @@ -100,12 +98,6 @@ const char *if_port_text[] = { "100baseFX" }; -/* - * The list of devices, that are able to output. - */ - -static struct device *dev_up_base; - /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -130,16 +122,17 @@ struct packet_type *ptype_base[16]; /* 16 way hashed list */ struct packet_type *ptype_all = NULL; /* Taps */ /* - * Device list lock + * 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 */ -struct notifier_block *netdev_chain=NULL; +static struct notifier_block *netdev_chain=NULL; /* * Device drivers call our routines to queue packets here. We empty the @@ -148,14 +141,6 @@ struct notifier_block *netdev_chain=NULL; static struct sk_buff_head backlog; -/* - * We don't overdo the queue or we will thrash memory badly. - */ - -static int backlog_size = 0; - - - /****************************************************************************************** Protocol management and registration routines @@ -166,7 +151,7 @@ static int backlog_size = 0; * For efficiency */ -static int dev_nit=0; +int netdev_nit=0; /* * Add a protocol ID to the list. Now that the input handler is @@ -179,7 +164,7 @@ void dev_add_pack(struct packet_type *pt) int hash; if(pt->type==htons(ETH_P_ALL)) { - dev_nit++; + netdev_nit++; pt->next=ptype_all; ptype_all=pt; } @@ -201,7 +186,7 @@ void dev_remove_pack(struct packet_type *pt) struct packet_type **pt1; if(pt->type==htons(ETH_P_ALL)) { - dev_nit--; + netdev_nit--; pt1=&ptype_all; } else @@ -258,7 +243,6 @@ struct device *dev_getbyhwaddr(unsigned short type, char *ha) for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->type == type && - !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)) && memcmp(dev->dev_addr, ha, dev->addr_len) == 0) return(dev); } @@ -312,19 +296,20 @@ struct device *dev_alloc(const char *name, int *err) void dev_load(const char *name) { - if(!dev_get(name)) { -#ifdef CONFIG_NET_ALIAS - const char *sptr; - - for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break; - if (!(*sptr && *(sptr+1))) -#endif + if(!dev_get(name)) request_module(name); - } } #endif - + +static int +default_rebuild_header(struct sk_buff *skb) +{ + printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header -- BUG!\n", skb->dev->name); + kfree_skb(skb, FREE_WRITE); + return 1; +} + /* * Prepare an interface for use. */ @@ -333,6 +318,13 @@ 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 */ @@ -341,29 +333,39 @@ int dev_open(struct device *dev) ret = dev->open(dev); /* - * If it went open OK then set the flags + * 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); + /* - * Initialise multicasting status + * Initialize multicasting status */ dev_mc_upload(dev); - notifier_call_chain(&netdev_chain, NETDEV_UP, dev); - + /* - * Passive non transmitting devices (including - * aliases) need not be on this chain. + * Wakeup transmit queue engine */ - if (!net_alias_is(dev) && dev->tx_queue_len) - { - cli(); - dev->next_up = dev_up_base; - dev_up_base = dev; - sti(); - } + dev_activate(dev); + + /* + * ... and announce new interface. + */ + notifier_call_chain(&netdev_chain, NETDEV_UP, dev); + } return(ret); } @@ -375,17 +377,24 @@ int dev_open(struct device *dev) int dev_close(struct device *dev) { - int ct=0; - struct device **devp; + 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->flags & IFF_UP) && dev->stop) + if (dev->stop) dev->stop(dev); + if (dev->start) + printk("dev_close: bug %s still running\n", dev->name); + /* * Device is now down. */ @@ -397,36 +406,7 @@ int dev_close(struct device *dev) */ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); - /* - * Flush the multicast chain - */ - dev_mc_discard(dev); - - /* - * Purge any queued packets when we down the link - */ - while(ctbuffs[ct]))!=NULL) - kfree_skb(skb,FREE_WRITE); - ct++; - } - /* - * The device is no longer up. Drop it from the list. - */ - - devp = &dev_up_base; - while (*devp) - { - if (*devp == dev) - { - *devp = dev->next_up; - break; - } - devp = &(*devp)->next_up; - } return(0); } @@ -451,7 +431,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb) * taps currently in use. */ -static void queue_xmit_nit(struct sk_buff *skb, struct device *dev) +void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev) { struct packet_type *ptype; get_fast_time(&skb->stamp); @@ -467,180 +447,111 @@ static void queue_xmit_nit(struct sk_buff *skb, struct device *dev) struct sk_buff *skb2; if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) break; - skb2->mac.raw = skb2->data; - skb2->nh.raw = skb2->h.raw = skb2->data + dev->hard_header_len; - ptype->func(skb2, skb->dev, ptype); - } - } -} - -/* - * Send (or queue for sending) a packet. - * - * IMPORTANT: When this is called to resend frames. The caller MUST - * already have locked the sk_buff. Apart from that we do the - * rest of the magic. - */ - -static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) -{ - unsigned long flags; - struct sk_buff_head *list; - int retransmission = 0; /* used to say if the packet should go */ - /* at the front or the back of the */ - /* queue - front is a retransmit try */ - /* - * 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; - retransmission = 1; - } + /* Code, following below is wrong. -#ifdef CONFIG_NET_DEBUG - if (pri >= DEV_NUMBUFFS) - { - printk(KERN_WARNING "bad priority in do_dev_queue_xmit.\n"); - pri = 1; - } -#endif - - /* - * If we are bridging and this is directly generated output - * pass the frame via the bridge. - */ - -#ifdef CONFIG_BRIDGE - if(skb->pkt_bridged!=IS_BRIDGED && br_stats.flags & BR_UP) - { - if(br_tx_frame(skb)) - return; - } -#endif - - list = dev->buffs + pri; - - save_flags(flags); - - /* - * If this isn't a retransmission, use the first packet instead. - * Note: We don't do strict priority ordering here. We will in - * fact kick the queue that is our priority. The dev_tint reload - * does strict priority queueing. In effect what we are doing here - * is to add some random jitter to the queues and to do so by - * saving clocks. Doing a perfect priority queue isn't a good idea - * as you get some fascinating timing interactions. - */ + 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... + */ - if (!retransmission) - { - /* avoid overrunning the device queue.. */ - if (skb_queue_len(list) > dev->tx_queue_len) - { - dev_kfree_skb(skb, FREE_WRITE); - return; - } + /* 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; - /* copy outgoing packets to any sniffer packet handlers */ - if (dev_nit) - queue_xmit_nit(skb,dev); + 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; + } - if (skb_queue_len(list)) { - cli(); - __skb_queue_tail(list, skb); - skb = __skb_dequeue(list); - restore_flags(flags); + skb2->h.raw = skb2->nh.raw; + skb2->pkt_type = PACKET_OUTGOING; + ptype->func(skb2, skb->dev, ptype); } } - if (dev->hard_start_xmit(skb, dev) == 0) { - /* - * Packet is now solely the responsibility of the driver - */ - return; - } - - /* - * Transmission failed, put skb back into a list. Once on the list it's safe and - * no longer device locked (it can be freed safely from the device queue) - */ - cli(); - __skb_queue_head(list,skb); - restore_flags(flags); } /* - * Entry point for transmitting frames. + * 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; + + skb_pull(newskb, newskb->nh.raw - newskb->data); + 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; - - start_bh_atomic(); + struct Qdisc *q; /* * If the address has not been resolved. Call the device header rebuilder. * This can cover all protocols and technically not just ARP either. + * + * This call must be moved to protocol layer. + * Now it works only for IPv6 and for IPv4 in + * some unusual curcumstances (eql device). --ANK */ - if (!skb->arp) - { - /* - * FIXME: we should make the printk for no rebuild - * header a default rebuild_header routine and drop - * this call. Similarly we should make hard_header - * have a default NULL operation not check conditions. - */ - if (dev->rebuild_header) - { - if (dev->rebuild_header(skb)) - { - end_bh_atomic(); - return 0; - } - } - else - printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header!\n", dev->name); + if (!skb->arp && dev->rebuild_header(skb)) + return 0; + + q = dev->qdisc; + if (q->enqueue) { + start_bh_atomic(); + q->enqueue(skb, q); + qdisc_wakeup(dev); + end_bh_atomic(); + return 0; } - /* - * - * If dev is an alias, switch to its main device. - * "arp" resolution has been made with alias device, so - * arp entries refer to alias, not main. - * - */ - - if (net_alias_is(dev)) - skb->dev = dev = net_alias_main_dev(dev); + /* The device has no queue. Common case for software devices: + loopback, all the sorts of tunnels... - do_dev_queue_xmit(skb, dev, skb->priority); - end_bh_atomic(); + 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) { + start_bh_atomic(); + if (netdev_nit) + dev_queue_xmit_nit(skb,dev); + if (dev->hard_start_xmit(skb, dev) == 0) { + end_bh_atomic(); + return 0; + } + if (net_ratelimit()) + printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name); + end_bh_atomic(); + } + kfree_skb(skb, FREE_WRITE); return 0; } -/* - * 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; - skb_pull(newskb, newskb->nh.raw - newskb->data); - newskb->ip_summed = CHECKSUM_UNNECESSARY; - if (newskb->dst==NULL) - printk(KERN_DEBUG "BUG: packet without dst looped back 1\n"); - netif_rx(newskb); -} +/*======================================================================= + Receiver rotutines + =======================================================================*/ +int netdev_dropping = 0; +atomic_t netdev_rx_dropped; /* * Receive a packet from a device driver and queue it for the upper @@ -649,15 +560,6 @@ void dev_loopback_xmit(struct sk_buff *skb) void netif_rx(struct sk_buff *skb) { - static int dropping = 0; - - /* - * 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; if(skb->stamp.tv_sec==0) get_fast_time(&skb->stamp); @@ -665,13 +567,14 @@ void netif_rx(struct sk_buff *skb) * Check that we aren't overdoing things. */ - if (!backlog_size) - dropping = 0; - else if (backlog_size > 300) - dropping = 1; + if (!backlog.qlen) + netdev_dropping = 0; + else if (backlog.qlen > 300) + netdev_dropping = 1; - if (dropping) + if (netdev_dropping) { + atomic_inc(&netdev_rx_dropped); kfree_skb(skb, FREE_READ); return; } @@ -681,7 +584,6 @@ void netif_rx(struct sk_buff *skb) */ skb_queue_tail(&backlog,skb); - backlog_size++; /* * If any packet arrived, mark it for processing after the @@ -692,32 +594,37 @@ void netif_rx(struct sk_buff *skb) return; } -/* - * This routine causes all interfaces to try to send some data. - */ - -static void dev_transmit(void) +#ifdef CONFIG_BRIDGE +static inline void handle_bridge(struct skbuff *skb, unsigned short type) { - struct device *dev; - - for (dev = dev_up_base; dev != NULL; dev = dev->next_up) + if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(type))) { - if (dev->flags != 0 && !dev->tbusy) + /* + * We pass the bridge a complete frame. This means + * recovering the MAC header first. + */ + + int offset=skb->data-skb->mac.raw; + cli(); + skb_push(skb,offset); /* Put header back on for bridge */ + if(br_receive_frame(skb)) { - /* - * Kick the device - */ - dev_tint(dev); + sti(); + continue; } + /* + * Pull the MAC header off for the copy going to + * the upper layers. + */ + skb_pull(skb,offset); + sti(); } } +#endif - -/********************************************************************************** - - Receive Queue Processor - -***********************************************************************************/ +#ifdef CONFIG_CPU_IS_SLOW +int net_cpu_congestion; +#endif /* * When we are called the queue is ready to grab, the interrupts are @@ -732,7 +639,15 @@ void net_bh(void) struct packet_type *ptype; struct packet_type *pt_prev; unsigned short type; - int nit = 301; + unsigned long start_time = jiffies; +#ifdef CONFIG_CPU_IS_SLOW + static unsigned long start_busy = 0; + static unsigned long ave_busy = 0; + + if (start_busy == 0) + start_busy = start_time; + net_cpu_congestion = ave_busy>>8; +#endif /* * Can we send anything now? We want to clear the @@ -741,7 +656,8 @@ void net_bh(void) * latency on a transmit interrupt bh. */ - dev_transmit(); + if (qdisc_head.forw != &qdisc_head) + qdisc_run_queues(); /* * Any data left to process. This may occur because a @@ -761,55 +677,43 @@ void net_bh(void) { struct sk_buff * skb = backlog.next; + if (jiffies - start_time > 1) { + /* Give chance to other bottom halves to run */ + mark_bh(NET_BH); + return; + } + /* * We have a packet. Therefore the queue has shrunk */ cli(); __skb_unlink(skb, &backlog); - backlog_size--; sti(); - /* - * We do not want to spin in net_bh infinitely. --ANK - */ - if (--nit <= 0) - { - if (nit == 0) - printk(KERN_WARNING "net_bh: too many loops, dropping...\n"); +#ifdef CONFIG_CPU_IS_SLOW + if (ave_busy > 128*16) { kfree_skb(skb, FREE_WRITE); - continue; + while ((skb = skb_dequeue(&backlog)) != NULL) + kfree_skb(skb, FREE_WRITE); + break; } +#endif + -#ifdef CONFIG_BRIDGE + /* + * Fetch the packet protocol ID. + */ + + type = skb->protocol; + +#ifdef CONFIG_BRIDGE /* * If we are bridging then pass the frame up to the * bridging code (if this protocol is to be bridged). * If it is bridged then move on */ - - if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(skb->protocol))) - { - /* - * We pass the bridge a complete frame. This means - * recovering the MAC header first. - */ - - int offset=skb->data-skb->mac.raw; - cli(); - skb_push(skb,offset); /* Put header back on for bridge */ - if(br_receive_frame(skb)) - { - sti(); - continue; - } - /* - * Pull the MAC header off for the copy going to - * the upper layers. - */ - skb_pull(skb,offset); - sti(); - } + handle_bridge(skb, type); #endif /* @@ -822,12 +726,6 @@ void net_bh(void) /* XXX until we figure out every place to modify.. */ skb->h.raw = skb->nh.raw = skb->data; - /* - * Fetch the packet protocol ID. - */ - - type = skb->protocol; - /* * We got a packet ID. Now loop over the "known protocols" * list. There are two lists. The ptype_all list of taps (normally empty) @@ -837,15 +735,17 @@ void net_bh(void) pt_prev = NULL; for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) { - if(pt_prev) - { - struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); - if(skb2) - pt_prev->func(skb2,skb->dev, pt_prev); + if (!ptype->dev || ptype->dev == skb->dev) { + if(pt_prev) + { + struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); + if(skb2) + pt_prev->func(skb2,skb->dev, pt_prev); + } + pt_prev=ptype; } - pt_prev=ptype; } - + for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) { if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev)) @@ -872,7 +772,7 @@ void net_bh(void) pt_prev=ptype; } } /* End of protocol list loop */ - + /* * Is there a last item to send to ? */ @@ -883,16 +783,9 @@ void net_bh(void) * Has an unknown packet has been received ? */ - else + else { kfree_skb(skb, FREE_WRITE); - /* - * Again, see if we can transmit anything now. - * [Ought to take this out judging by tests it slows - * us down not speeds us up] - */ -#ifdef XMIT_EVERY - dev_transmit(); -#endif + } } /* End of queue loop */ /* @@ -903,64 +796,47 @@ void net_bh(void) * One last output flush. */ - dev_transmit(); + if (qdisc_head.forw != &qdisc_head) + qdisc_run_queues(); + +#ifdef CONFIG_CPU_IS_SLOW +{ + unsigned long start_idle = jiffies; + ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4); + start_busy = 0; +} +#endif } +/* Protocol dependent address dumping routines */ -/* - * This routine is called when an device driver (i.e. an - * interface) is ready to transmit a packet. - */ - -void dev_tint(struct device *dev) +static int (*gifconf[NPROTO])(struct device *dev, char *bufptr, int len); + +int register_gifconf(int family, int (*func)(struct device *dev, char *bufptr, int len)) { - int i; - unsigned long flags; - struct sk_buff_head * head; - - /* - * aliases do not transmit (for now :) ) - */ + if (family<0 || family>=NPROTO) + return -EINVAL; + gifconf[family] = func; + return 0; +} - if (net_alias_is(dev)) { - printk(KERN_DEBUG "net alias %s transmits\n", dev->name); - return; - } - head = dev->buffs; - save_flags(flags); - cli(); +/* + This ioctl is wrong by design. It really existed in some + old SYSV systems, only was named SIOCGIFNUM. + In multiprotocol environment it is just useless. + Well, SIOCGIFCONF is wrong too, but we have to preserve + it by compatibility reasons. - /* - * Work the queues in priority order - */ - for(i = 0;i < DEV_NUMBUFFS; i++,head++) - { + If someone wants to achieve the same effect, please, use undocumented + feature of SIOCGIFCONF: it returns buffer length, if buffer + is not supplied. - while (!skb_queue_empty(head)) { - struct sk_buff *skb; + Let's remove it, until someone started to use it. --ANK - skb = head->next; - __skb_unlink(skb, head); - /* - * Stop anyone freeing the buffer while we retransmit it - */ - restore_flags(flags); - /* - * Feed them to the output stage and if it fails - * indicate they re-queue at the front. - */ - do_dev_queue_xmit(skb,dev,-i - 1); - /* - * If we can take no more then stop here. - */ - if (dev->tbusy) - return; - cli(); - } - } - restore_flags(flags); -} + In any case, if someone cannot live without it, it should + be renamed to SIOCGIFNUM. + */ /* @@ -970,20 +846,26 @@ void dev_tint(struct device *dev) static int dev_ifcount(unsigned int *arg) { struct device *dev; - int err; unsigned int count = 0; for (dev = dev_base; dev != NULL; dev = dev->next) count++; - err = copy_to_user(arg, &count, sizeof(unsigned int)); - if (err) - return -EFAULT; - return 0; + return put_user(count, arg); } /* - * Map an interface index to its name (SIOGIFNAME) + * Map an interface index to its name (SIOCGIFNAME) + */ + +/* + * This call is useful, but I'd remove it too. + * + * The reason is purely aestetical, it is the only call + * from SIOC* family using struct ifreq in reversed manner. + * Besides that, it is pretty silly to put "drawing" facility + * to kernel, it is useful only to print ifindices + * in readable form, is not it? --ANK */ static int dev_ifname(struct ifreq *arg) @@ -1019,7 +901,6 @@ static int dev_ifname(struct ifreq *arg) static int dev_ifconf(char *arg) { struct ifconf ifc; - struct ifreq ifr; struct device *dev; char *pos; unsigned int len; @@ -1031,68 +912,51 @@ static int dev_ifconf(char *arg) err = copy_from_user(&ifc, arg, sizeof(struct ifconf)); if (err) - return -EFAULT; - len = ifc.ifc_len; + return -EFAULT; + pos = ifc.ifc_buf; + if (pos==NULL) + ifc.ifc_len=0; + len = ifc.ifc_len; - /* - * We now walk the device list filling each active device - * into the array. - */ - /* * Loop over the interfaces, and write an info block for each. */ - - dev_lock_wait(); - dev_lock_list(); - for (dev = dev_base; dev != NULL; dev = dev->next) - { - /* - * Have we run out of space here ? - */ - - if (len < sizeof(struct ifreq)) - break; + for (dev = dev_base; dev != NULL; dev = dev->next) { + int i; + for (i=0; iname); - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; + done = gifconf[i](dev, pos, len); + if (done<0) + return -EFAULT; - /* - * Write this block to the caller's space. - */ - - err = copy_to_user(pos, &ifr, sizeof(struct ifreq)); - if (err) - return -EFAULT; - pos += sizeof(struct ifreq); - len -= sizeof(struct ifreq); + len -= done; + if (pos) + pos += done; + } } - dev_unlock_list(); - /* * 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; - err = copy_to_user(arg, &ifc, sizeof(struct ifconf)); - if (err) + ifc.ifc_len -= len; + + if (copy_to_user(arg, &ifc, sizeof(struct ifconf))) return -EFAULT; /* * Report how much was filled in */ - return(pos - arg); + return ifc.ifc_len; } - /* * This is invoked by the /proc filesystem handler to display a device * in detail. @@ -1105,7 +969,7 @@ static int sprintf_stats(char *buffer, struct device *dev) int size; if (stats) - size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu\n", + size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n", dev->name, stats->rx_bytes, stats->rx_packets, stats->rx_errors, @@ -1117,7 +981,8 @@ static int sprintf_stats(char *buffer, struct device *dev) stats->tx_packets, stats->tx_errors, stats->tx_dropped, stats->tx_fifo_errors, stats->collisions, stats->tx_carrier_errors + stats->tx_aborted_errors - + stats->tx_window_errors + stats->tx_heartbeat_errors); + + stats->tx_window_errors + stats->tx_heartbeat_errors, + stats->multicast); else size = sprintf(buffer, "%6s: No statistics available.\n", dev->name); @@ -1252,272 +1117,216 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset, #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_RADIO */ +void dev_set_promiscuity(struct device *dev, int inc) +{ + unsigned short old_flags = dev->flags; -/* - * 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) + dev->flags |= IFF_PROMISC; + if ((dev->promiscuity += inc) == 0) + dev->flags &= ~IFF_PROMISC; + if (dev->flags^old_flags) { + dev_mc_upload(dev); + printk(KERN_INFO "device %s %s promiscuous mode\n", + dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "leaved"); + } +} + +void dev_set_allmulti(struct device *dev, int inc) { - struct ifreq ifr; - struct device *dev; - int ret, err; + unsigned short old_flags = dev->flags; + + dev->flags |= IFF_ALLMULTI; + if ((dev->allmulti += inc) == 0) + dev->flags &= ~IFF_ALLMULTI; + if (dev->flags^old_flags) + dev_mc_upload(dev); +} + +int dev_change_flags(struct device *dev, unsigned flags) +{ + int ret; + int old_flags = dev->flags; /* - * Fetch the caller's info block into kernel space + * Set the flags on our device. */ - - err = copy_from_user(&ifr, arg, sizeof(struct ifreq)); - if (err) - return -EFAULT; + + dev->flags = (flags & (IFF_DEBUG|IFF_NOTRAILERS|IFF_RUNNING|IFF_NOARP| + IFF_SLAVE|IFF_MASTER| + IFF_MULTICAST|IFF_PORTSEL|IFF_AUTOMEDIA)) | + (dev->flags & (IFF_UP|IFF_VOLATILE|IFF_PROMISC)); /* - * See which interface the caller is talking about. - */ - + * Load in the correct multicast list now the flags have changed. + */ + + dev_mc_upload(dev); + /* - * - * net_alias_dev_get(): dev_get() with added alias naming magic. - * only allow alias creation/deletion if (getset==SIOCSIFADDR) - * + * Have we downed the interface. We handle IFF_UP ourselves + * according to user attempts to set it, rather than blindly + * setting it. */ - -#ifdef CONFIG_KERNELD - dev_load(ifr.ifr_name); -#endif -#ifdef CONFIG_NET_ALIAS - if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL) - return(err); -#else - if ((dev = dev_get(ifr.ifr_name)) == NULL) - return(-ENODEV); -#endif - switch(getset) + ret = 0; + if ((old_flags^flags)&IFF_UP) /* Bit is different ? */ + { + if(old_flags&IFF_UP) /* Gone down */ + ret=dev_close(dev); + else /* Come up */ + ret=dev_open(dev); + + if (ret == 0) + dev_mc_upload(dev); + } + + if (dev->flags&IFF_UP && + ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC|IFF_VOLATILE))) { + printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm); + notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); + } + + if ((flags^dev->gflags)&IFF_PROMISC) { + int inc = (flags&IFF_PROMISC) ? +1 : -1; + dev->gflags ^= IFF_PROMISC; + dev_set_promiscuity(dev, inc); + } + + return ret; +} + +/* + * Perform the SIOCxIFxxx calls. + */ + +static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) +{ + struct device *dev; + int err; + + if ((dev = dev_get(ifr->ifr_name)) == NULL) + return -ENODEV; + + switch(cmd) { case SIOCGIFFLAGS: /* Get interface flags */ - ifr.ifr_flags = dev->flags; - goto rarok; + ifr->ifr_flags = (dev->flags&~IFF_PROMISC)|(dev->gflags&IFF_PROMISC); + return 0; case SIOCSIFFLAGS: /* Set interface flags */ - { - int old_flags = dev->flags; - - /* - * We are not allowed to potentially close/unload - * a device until we get this lock. - */ - - dev_lock_wait(); - dev_lock_list(); - - /* - * Set the flags on our device. - */ - - dev->flags = (ifr.ifr_flags & ( - IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_PORTSEL | - IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_AUTOMEDIA | - IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER - | IFF_MULTICAST)) | (dev->flags & IFF_UP); - /* - * Load in the correct multicast list now the flags have changed. - */ - - dev_mc_upload(dev); - - /* - * Have we downed the interface. We handle IFF_UP ourselves - * according to user attempts to set it, rather than blindly - * setting it. - */ - - if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */ - { - if(old_flags&IFF_UP) /* Gone down */ - ret=dev_close(dev); - else /* Come up */ - { - ret=dev_open(dev); - if(ret<0) - dev->flags&=~IFF_UP; /* Open failed */ - } - } - else - ret=0; - /* - * Load in the correct multicast list now the flags have changed. - */ - - dev_mc_upload(dev); - if ((dev->flags&IFF_UP) && ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC))) - { - printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm); - notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); - } - if ((dev->flags^old_flags)&IFF_PROMISC) { - if (dev->flags&IFF_PROMISC) - printk(KERN_INFO "%s enters promiscuous mode.\n", dev->name); - else - printk(KERN_INFO "%s leave promiscuous mode.\n", dev->name); - } - dev_unlock_list(); - } - break; + return dev_change_flags(dev, ifr->ifr_flags); case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */ - - ifr.ifr_metric = dev->metric; - goto rarok; + ifr->ifr_metric = dev->metric; + return 0; case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */ - dev->metric = ifr.ifr_metric; - ret=0; - break; + dev->metric = ifr->ifr_metric; + return 0; case SIOCGIFMTU: /* Get the MTU of a device */ - ifr.ifr_mtu = dev->mtu; - goto rarok; + ifr->ifr_mtu = dev->mtu; + return 0; case SIOCSIFMTU: /* Set the MTU of a device */ - - if (ifr.ifr_mtu == dev->mtu) { - ret = 0; - break; - } + if (ifr->ifr_mtu == dev->mtu) + return 0; /* * MTU must be positive. */ - if(ifr.ifr_mtu<68) + if (ifr->ifr_mtu<0) return -EINVAL; if (dev->change_mtu) - ret = dev->change_mtu(dev, ifr.ifr_mtu); - else - { - dev->mtu = ifr.ifr_mtu; - ret = 0; + err = dev->change_mtu(dev, ifr->ifr_mtu); + else { + dev->mtu = ifr->ifr_mtu; + err = 0; } - if (!ret && dev->flags&IFF_UP) { + if (!err && dev->flags&IFF_UP) { printk(KERN_DEBUG "SIFMTU %s(%s)\n", dev->name, current->comm); notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev); } - break; - - case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently - do not support it */ - ret = -EINVAL; - break; - - case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */ - ret = -EINVAL; - break; + return err; case SIOCGIFHWADDR: - memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); - ifr.ifr_hwaddr.sa_family=dev->type; - goto rarok; + memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); + ifr->ifr_hwaddr.sa_family=dev->type; + return 0; case SIOCSIFHWADDR: if(dev->set_mac_address==NULL) return -EOPNOTSUPP; - if(ifr.ifr_hwaddr.sa_family!=dev->type) + if(ifr->ifr_hwaddr.sa_family!=dev->type) return -EINVAL; - ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr); - if (!ret) + err=dev->set_mac_address(dev,&ifr->ifr_hwaddr); + if (!err) notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); - break; + return err; + case SIOCSIFHWBROADCAST: + if(ifr->ifr_hwaddr.sa_family!=dev->type) + return -EINVAL; + memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN); + notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); + return 0; + case SIOCGIFMAP: - ifr.ifr_map.mem_start=dev->mem_start; - ifr.ifr_map.mem_end=dev->mem_end; - ifr.ifr_map.base_addr=dev->base_addr; - ifr.ifr_map.irq=dev->irq; - ifr.ifr_map.dma=dev->dma; - ifr.ifr_map.port=dev->if_port; - goto rarok; + ifr->ifr_map.mem_start=dev->mem_start; + ifr->ifr_map.mem_end=dev->mem_end; + ifr->ifr_map.base_addr=dev->base_addr; + ifr->ifr_map.irq=dev->irq; + ifr->ifr_map.dma=dev->dma; + ifr->ifr_map.port=dev->if_port; + return 0; case SIOCSIFMAP: - if(dev->set_config==NULL) - return -EOPNOTSUPP; - return dev->set_config(dev,&ifr.ifr_map); + if (dev->set_config) + return dev->set_config(dev,&ifr->ifr_map); + return -EOPNOTSUPP; case SIOCADDMULTI: - if(dev->set_multicast_list==NULL) - return -EINVAL; - if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) + if(dev->set_multicast_list==NULL || + ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) return -EINVAL; - dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1); + dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1); return 0; case SIOCDELMULTI: - if(dev->set_multicast_list==NULL) + if(dev->set_multicast_list==NULL || + ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) return -EINVAL; - if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) - return -EINVAL; - dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1); + dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1); return 0; - case SIOGIFINDEX: - ifr.ifr_ifindex = dev->ifindex; - goto rarok; - + case SIOCGIFINDEX: + ifr->ifr_ifindex = dev->ifindex; + return 0; /* * Unknown or private ioctl */ default: - if((getset >= SIOCDEVPRIVATE) && - (getset <= (SIOCDEVPRIVATE + 15))) { - if(dev->do_ioctl==NULL) - return -EOPNOTSUPP; - ret = dev->do_ioctl(dev, &ifr, getset); - if (!ret) - { - err = copy_to_user(arg,&ifr,sizeof(struct ifreq)); - if (err) - ret = -EFAULT; - } - break; + if(cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) { + if (dev->do_ioctl) + return dev->do_ioctl(dev, ifr, cmd); + return -EOPNOTSUPP; } #ifdef CONFIG_NET_RADIO - if((getset >= SIOCIWFIRST) && (getset <= SIOCIWLAST)) - { - if(dev->do_ioctl==NULL) - return -EOPNOTSUPP; - /* Perform the ioctl */ - ret=dev->do_ioctl(dev, &ifr, getset); - /* If return args... */ - if(IW_IS_GET(getset)) - { - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - { - ret = -EFAULT; - } - } - break; + if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + if (dev->do_ioctl) + return dev->do_ioctl(dev, ifr, cmd); + return -EOPNOTSUPP; } #endif /* CONFIG_NET_RADIO */ - ret = -EINVAL; } - return(ret); -/* - * The load of calls that return an ifreq and ok (saves memory). - */ -rarok: - err = copy_to_user(arg, &ifr, sizeof(struct ifreq)); - if (err) - err = -EFAULT; - return err; + return -EINVAL; } @@ -1528,47 +1337,98 @@ rarok: int dev_ioctl(unsigned int cmd, void *arg) { + struct ifreq ifr; + int ret; +#ifdef CONFIG_NET_ALIAS + char *colon; +#endif + + /* One special case: SIOCGIFCONF takes ifconf argument + and requires shared lock, because it sleeps writing + to user space. + */ + + if (cmd == SIOCGIFCONF) { + rtnl_shlock(); + dev_ifconf((char *) arg); + rtnl_shunlock(); + return 0; + } + if (cmd == SIOCGIFCOUNT) { + return dev_ifcount((unsigned int*)arg); + } + if (cmd == SIOCGIFNAME) { + return dev_ifname((struct ifreq *)arg); + } + + if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) + return -EFAULT; + + ifr.ifr_name[IFNAMSIZ-1] = 0; + +#ifdef CONFIG_NET_ALIAS + colon = strchr(ifr.ifr_name, ':'); + if (colon) + *colon = 0; +#endif + + /* + * See which interface the caller is talking about. + */ + +#ifdef CONFIG_KERNELD + dev_load(ifr.ifr_name); +#endif + switch(cmd) { - case SIOCGIFCONF: - (void) dev_ifconf((char *) arg); - return 0; - case SIOCGIFCOUNT: - return dev_ifcount((unsigned int *) arg); - case SIOGIFNAME: - return dev_ifname((struct ifreq *)arg); - /* - * Ioctl calls that can be done by all. + * These ioctl calls: + * - can be done by all. + * - atomic and do not require locking. + * - return a value */ case SIOCGIFFLAGS: case SIOCGIFMETRIC: case SIOCGIFMTU: - case SIOCGIFMEM: case SIOCGIFHWADDR: case SIOCGIFSLAVE: case SIOCGIFMAP: - case SIOGIFINDEX: - return dev_ifsioc(arg, cmd); + case SIOCGIFINDEX: + ret = dev_ifsioc(&ifr, cmd); + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; /* - * Ioctl calls requiring the power of a superuser + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - do not return a value */ case SIOCSIFFLAGS: case SIOCSIFMETRIC: case SIOCSIFMTU: - case SIOCSIFMEM: - case SIOCSIFHWADDR: case SIOCSIFMAP: + case SIOCSIFHWADDR: case SIOCSIFSLAVE: case SIOCADDMULTI: case SIOCDELMULTI: + case SIOCSIFHWBROADCAST: if (!suser()) return -EPERM; - return dev_ifsioc(arg, cmd); + rtnl_lock(); + ret = dev_ifsioc(&ifr, cmd); + rtnl_unlock(); + return ret; + case SIOCGIFMEM: + /* Get the per device memory space. We can add this but currently + do not support it */ + case SIOCSIFMEM: + /* Set the per device memory buffer space. Not applicable in our case */ case SIOCSIFLINK: return -EINVAL; @@ -1577,16 +1437,29 @@ int dev_ioctl(unsigned int cmd, void *arg) */ default: - if((cmd >= SIOCDEVPRIVATE) && - (cmd <= (SIOCDEVPRIVATE + 15))) { - return dev_ifsioc(arg, cmd); + if (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) { + rtnl_lock(); + ret = dev_ifsioc(&ifr, cmd); + rtnl_unlock(); + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; } #ifdef CONFIG_NET_RADIO - if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) - { - if((IW_IS_SET(cmd)) && (!suser())) - return -EPERM; - return dev_ifsioc(arg, cmd); + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + if (IW_IS_SET(cmd)) { + if (!suser()) + return -EPERM; + rtnl_lock(); + } + ret = dev_ifsioc(&ifr, cmd); + if (IW_IS_SET(cmd)) + rtnl_unlock(); + if (!ret && IW_IS_GET(cmd) && + copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return ret; } #endif /* CONFIG_NET_RADIO */ return -EINVAL; @@ -1596,9 +1469,103 @@ int dev_ioctl(unsigned int cmd, void *arg) int dev_new_index() { static int ifindex; - return ++ifindex; + for (;;) { + if (++ifindex <= 0) + ifindex=1; + if (dev_get_by_index(ifindex) == NULL) + return ifindex; + } +} + +static int dev_boot_phase = 1; + + +int register_netdevice(struct device *dev) +{ + struct device *d, **dp; + + if (dev_boot_phase) { + printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name); + + /* Check for existence, and append to tail of chain */ + for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) { + if (d == dev || strcmp(d->name, dev->name) == 0) + return -EEXIST; + } + dev->next = NULL; + *dp = dev; + return 0; + } + + dev->iflink = -1; + + /* Init, if this function is available */ + if (dev->init && dev->init(dev) != 0) + return -EIO; + + /* Check for existence, and append to tail of chain */ + for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) { + if (d == dev || strcmp(d->name, dev->name) == 0) + return -EEXIST; + } + dev->next = NULL; + dev_init_scheduler(dev); + dev->ifindex = dev_new_index(); + if (dev->iflink == -1) + dev->iflink = dev->ifindex; + *dp = dev; + + /* Notify protocols, that a new device appeared. */ + notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + + return 0; +} + +int unregister_netdevice(struct device *dev) +{ + struct device *d, **dp; + + if (dev_boot_phase == 0) { + /* If device is running, close it. + It is very bad idea, really we should + complain loudly here, but random hackery + in linux/drivers/net likes it. + */ + if (dev->flags & IFF_UP) + dev_close(dev); + + /* Shutdown queueing discipline. */ + dev_shutdown(dev); + + /* Notify protocols, that we are about to destroy + this device. They should clean all the things. + */ + notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); + + /* + * Flush the multicast chain + */ + dev_mc_discard(dev); + + /* To avoid pointers looking to nowhere, + we wait for end of critical section */ + dev_lock_wait(); + } + + /* And unlink it from device chain. */ + for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) { + if (d == dev) { + *dp = d->next; + d->next = NULL; + if (dev->destructor) + dev->destructor(dev); + return 0; + } + } + return -ENODEV; } + /* * Initialize the DEV module. At boot time this walks the device list and * unhooks any devices that fail to initialise (normally hardware not @@ -1606,14 +1573,15 @@ int dev_new_index() * */ extern int lance_init(void); -extern int pi_init(void); extern int bpq_init(void); extern int scc_init(void); extern void sdla_setup(void); extern void dlci_setup(void); -extern int pt_init(void); +extern int dmascc_init(void); extern int sm_init(void); -extern int baycom_init(void); +extern int baycom_ser_fdx_init(void); +extern int baycom_ser_hdx_init(void); +extern int baycom_par_init(void); extern int lapbeth_init(void); extern void arcnet_init(void); @@ -1641,6 +1609,8 @@ __initfunc(int net_dev_init(void)) { struct device *dev, **dp; + pktsched_init(); + /* * Initialise the packet receive queue. */ @@ -1660,18 +1630,16 @@ __initfunc(int net_dev_init(void)) * * Some devices want to be initialized early.. */ + #if defined(CONFIG_LANCE) lance_init(); #endif -#if defined(CONFIG_PI) - pi_init(); -#endif #if defined(CONFIG_SCC) scc_init(); #endif -#if defined(CONFIG_PT) - pt_init(); -#endif +#if defined(CONFIG_DMASCC) + dmascc_init(); +#endif #if defined(CONFIG_BPQETHER) bpq_init(); #endif @@ -1681,8 +1649,14 @@ __initfunc(int net_dev_init(void)) #if defined(CONFIG_SDLA) sdla_setup(); #endif -#if defined(CONFIG_BAYCOM) - baycom_init(); +#if defined(CONFIG_BAYCOM_PAR) + baycom_par_init(); +#endif +#if defined(CONFIG_BAYCOM_SER_FDX) + baycom_ser_fdx_init(); +#endif +#if defined(CONFIG_BAYCOM_SER_HDX) + baycom_ser_hdx_init(); #endif #if defined(CONFIG_SOUNDMODEM) sm_init(); @@ -1706,6 +1680,7 @@ __initfunc(int net_dev_init(void)) slhc_install(); #endif + /* * Add the devices. * If the call to dev->init fails, the dev is removed @@ -1716,11 +1691,7 @@ __initfunc(int net_dev_init(void)) dp = &dev_base; while ((dev = *dp) != NULL) { - int i; - for (i = 0; i < DEV_NUMBUFFS; i++) { - skb_queue_head_init(dev->buffs + i); - } - + dev->iflink = -1; if (dev->init && dev->init(dev)) { /* @@ -1732,6 +1703,9 @@ __initfunc(int net_dev_init(void)) { dp = &dev->next; dev->ifindex = dev_new_index(); + if (dev->iflink == -1) + dev->iflink = dev->ifindex; + dev_init_scheduler(dev); } } @@ -1745,18 +1719,13 @@ __initfunc(int net_dev_init(void)) #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_RADIO */ - /* - * Initialise net_alias engine - * - * - register net_alias device notifier - * - register proc entries: /proc/net/alias_types - * /proc/net/aliases - */ + init_bh(NET_BH, net_bh); -#ifdef CONFIG_NET_ALIAS - net_alias_init(); + dev_boot_phase = 0; + +#ifdef CONFIG_IP_PNP + ip_auto_config(); #endif - init_bh(NET_BH, net_bh); return 0; } diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 4aa6cbb0c..eaa1bd058 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -42,7 +42,6 @@ #include #include #include -#include /* @@ -69,19 +68,6 @@ void dev_mc_upload(struct device *dev) if(!(dev->flags&IFF_UP)) return; - /* - * An aliased device should end up with the combined - * multicast list of all its aliases. - * Really, multicasting with logical interfaces is very - * subtle question. Now we DO forward multicast packets - * to logical interfcases, that doubles multicast - * traffic but allows mrouted to work. - * Alas, mrouted does not understand aliases even - * in 4.4BSD --ANK - */ - - dev = net_alias_main_dev(dev); - /* * Devices with no set multicast don't get set */ @@ -99,7 +85,6 @@ void dev_mc_upload(struct device *dev) void dev_mc_delete(struct device *dev, void *addr, int alen, int all) { struct dev_mc_list **dmi; - dev = net_alias_main_dev(dev); for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next) { @@ -136,8 +121,6 @@ void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) { struct dev_mc_list *dmi; - dev = net_alias_main_dev(dev); - for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next) { if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) @@ -165,12 +148,12 @@ void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) void dev_mc_discard(struct device *dev) { - if (net_alias_is(dev)) - return; while(dev->mc_list!=NULL) { struct dev_mc_list *tmp=dev->mc_list; dev->mc_list=dev->mc_list->next; + if (tmp->dmi_users) + printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); kfree_s(tmp,sizeof(*tmp)); } dev->mc_count=0; diff --git a/net/core/iovec.c b/net/core/iovec.c index 9bc21ffc5..10aa7a4cc 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -192,69 +192,78 @@ int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, * * ip_build_xmit must ensure that when fragmenting only the last * call to this function will be unaligned also. - * - * FIXME: add an error handling path when a copy/checksum from - * user space failed because of a invalid pointer. */ -unsigned int csum_partial_copy_fromiovecend(unsigned char *kdata, - struct iovec *iov, int offset, - int len, int csum) +int csum_partial_copy_fromiovecend(unsigned char *kdata, + struct iovec *iov, int offset, + int len, int *csump) { - __u32 partial; - __u32 partial_cnt = 0; + int partial_cnt = 0; + int err = 0; + int csum; - while(offset>0) - { - if (offset > iov->iov_len) - { - offset -= iov->iov_len; + do { + int copy = iov->iov_len - offset; - } - else - { - u8 *base; - int copy; + if (copy >= 0) { + u8 *base = iov->iov_base + offset; - base = iov->iov_base + offset; - copy = min(len, iov->iov_len - offset); - offset = 0; + /* Normal case (single iov component) is fastly detected */ + if (len <= copy) { + *csump = csum_partial_copy_from_user(base, kdata, + len, *csump, &err); + return err; + } partial_cnt = copy % 4; - if (partial_cnt) - { + if (partial_cnt) { copy -= partial_cnt; - copy_from_user(&partial, base + copy, - partial_cnt); + err |= copy_from_user(kdata+copy, base+copy, partial_cnt); } - /* - * FIXME: add exception handling to the - * csum functions and set *err when an - * exception occurs. - */ - csum = csum_partial_copy_fromuser(base, kdata, - copy, csum); + *csump = csum_partial_copy_from_user(base, kdata, + copy, *csump, &err); len -= copy + partial_cnt; kdata += copy + partial_cnt; + iov++; + break; } - iov++; - } + iov++; + offset = -copy; + } while (offset > 0); + + csum = *csump; while (len>0) { u8 *base = iov->iov_base; - int copy=min(len, iov->iov_len); + int copy = min(len, iov->iov_len); + /* There is a remnant from previous iov. */ if (partial_cnt) { int par_len = 4 - partial_cnt; - copy_from_user(&partial, base + partial_cnt, par_len); - csum = csum_partial((u8*) &partial, 4, csum); + /* iov component is too short ... */ + if (par_len > copy) { + err |= copy_from_user(kdata, base, copy); + base += copy; + partial_cnt += copy; + kdata += copy; + len -= copy; + iov++; + if (len) + continue; + *csump = csum_partial(kdata-partial_cnt, partial_cnt, csum); + return err; + } + err |= copy_from_user(kdata, base, par_len); + csum = csum_partial(kdata-partial_cnt, 4, csum); base += par_len; copy -= par_len; + len -= par_len; + kdata += par_len; partial_cnt = 0; } @@ -264,16 +273,15 @@ unsigned int csum_partial_copy_fromiovecend(unsigned char *kdata, if (partial_cnt) { copy -= partial_cnt; - copy_from_user(&partial, base + copy, - partial_cnt); + err |= copy_from_user(kdata+copy, base + copy, partial_cnt); } } - csum = csum_partial_copy_fromuser(base, kdata, copy, csum); + csum = csum_partial_copy_from_user(base, kdata, copy, csum, &err); len -= copy + partial_cnt; kdata += copy + partial_cnt; iov++; } - - return csum; + *csump = csum; + return err; } diff --git a/net/core/net_alias.c b/net/core/net_alias.c index 807c2e935..e69de29bb 100644 --- a/net/core/net_alias.c +++ b/net/core/net_alias.c @@ -1,1464 +0,0 @@ -/* - * NET_ALIAS network device aliasing module. - * - * - * Version: @(#)net_alias.c 0.43 12/20/95 - * - * Authors: Juan Jose Ciarlante, - * Marcelo Fabian Roccasalva, - * - * Features: - * - AF_ independent: net_alias_type objects - * - AF_INET optimized - * - ACTUAL alias devices inserted in dev chain - * - fast hashed alias address lookup - * - net_alias_type objs registration/unreg., module-ables. - * - /proc/net/aliases & /proc/net/alias_types entries - * Fixes: - * JJC : several net_alias_type func. renamed. - * JJC : net_alias_type object methods now pass - * *this. - * JJC : xxx_rcv device selection based on - * addrs - * Andreas Schultz : Kerneld support. - * - * FIXME: - * - User calls sleep/wake_up locking. - * - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_KERNELD -#include -#endif - -/* - * Only allow the following flags to pass from main device to aliases - */ - -#define NET_ALIAS_IFF_MASK (IFF_UP|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MULTICAST) - -static struct net_alias_type * nat_getbytype(int type); -static int nat_attach_chg(struct net_alias_type *nat, int delta); -static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa); -static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias); - -static int net_alias_devinit(struct device *dev); -static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev); -static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa); -static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias); -static struct device *net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data); -static struct device *net_alias_dev_delete(struct device *main_dev, int slot, int *err); -static void net_alias_free(struct device *dev); - -/* - * net_alias_type base array, will hold net_alias_type obj hashed list - * heads. - */ - -struct net_alias_type *nat_base[16]; - - -/* - * Get net_alias_type ptr by type - */ - -extern __inline__ struct net_alias_type *nat_getbytype(int type) -{ - struct net_alias_type *nat; - for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next) - { - if (nat->type == type) - return nat; - } - return NULL; -} - - -/* - * Get addr32 representation (pre-hashing) of address. - * If NULL nat->get_addr32, assume sockaddr_in struct (IP-ish). - */ - -extern __inline__ __u32 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa) -{ - if (nat->get_addr32) - return nat->get_addr32(nat, sa); - else - return (*(struct sockaddr_in *)sa).sin_addr.s_addr; -} - - -/* - * Hashing code for alias_info->hash_tab entries - * 4 bytes -> 1/2 byte using xor complemented by af - */ - -extern __inline__ unsigned HASH(__u32 addr, int af) -{ - unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */ - tmp ^= (tmp>>8); /* 2 -> 1 */ - return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */ -} - - -/* - * get hash key for supplied net alias type and address - * nat must be !NULL - * the purpose here is to map a net_alias_type and a generic - * address to a hash code. - */ - -extern __inline__ int nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa) -{ - return HASH(nat_addr32(nat,sa), sa->sa_family); -} - - -/* - * Change net_alias_type number of attachments (bindings) - */ - -static int nat_attach_chg(struct net_alias_type *nat, int delta) -{ - unsigned long flags; - int n_at; - if (!nat) - return -1; - save_flags(flags); - cli(); - n_at = nat->n_attach + delta; - if (n_at < 0) - { - restore_flags(flags); - printk(KERN_WARNING - "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n", - nat->type); - return -1; - } - nat->n_attach = n_at; - restore_flags(flags); - return 0; -} - - -/* - * Bind alias to its type (family) object and call initialization hook - */ - -extern __inline__ int nat_bind(struct net_alias_type *nat, - struct net_alias *alias, struct sockaddr *sa) -{ - if (nat->alias_init_1) - nat->alias_init_1(nat, alias, sa); - return nat_attach_chg(nat, +1); -} - - -/* - * Unbind alias from type object and call alias destructor - */ - -extern __inline__ int nat_unbind(struct net_alias_type *nat, - struct net_alias *alias) -{ - if (nat->alias_done_1) - nat->alias_done_1(nat, alias); - return nat_attach_chg(nat, -1); -} - - -/* - * Compare device address with given. if NULL nat->dev_addr_chk, - * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish) - */ - -static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat, - struct device *dev, struct sockaddr *sa) -{ - if (nat->dev_addr_chk) - return nat->dev_addr_chk(nat, dev, sa); - else - return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr); -} - - -/* - * Alias device init() - * do nothing. - */ - -static int net_alias_devinit(struct device *dev) -{ -#ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias_devinit(%s) called.\n", dev->name); -#endif - return 0; -} - - -/* - * 2 options for multicast: - * 1) fake it for aliases. - * 2) allow aliases and actual device to set it. - * current choice: option 1 - */ -static void net_alias_setmulticast(struct device *dev) -{ -} - - -/* - * Hard_start_xmit() should not be called. - * ignore ... but shout!. - */ - -static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev) -{ - printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name); - dev_kfree_skb(skb, FREE_WRITE); - return 0; -} - - -static int net_alias_open(struct device * dev) -{ - return 0; -} - -static int net_alias_close(struct device * dev) -{ - return 0; -} - -/* - * setups a new (alias) device - */ - -static int net_alias_devsetup(struct net_alias *alias, - struct net_alias_type *nat, struct sockaddr *sa) -{ - struct device *main_dev; - struct device *dev; - int family; - int i; - - /* - * - * generic device setup based on main_dev info - * - * FIXME: is NULL bitwise 0 for all Linux platforms? - */ - - main_dev = alias->main_dev; - dev = &alias->dev; - memset(dev, '\0', sizeof(struct device)); - family = (sa)? sa->sa_family : main_dev->family; - - dev->alias_info = NULL; /* no aliasing recursion */ - dev->my_alias = alias; /* point to alias */ - dev->name = alias->name; - dev->type = main_dev->type; - dev->open = net_alias_open; - dev->stop = net_alias_close; - if (main_dev->set_multicast_list) - dev->set_multicast_list = net_alias_setmulticast; - dev->hard_header_len = main_dev->hard_header_len; - memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN); - memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN); - dev->addr_len = main_dev->addr_len; - dev->init = net_alias_devinit; - dev->hard_start_xmit = net_alias_hard_start_xmit; - dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP; - dev->ifindex = dev_new_index(); - - /* - * Only makes sense if same family (arguable) - */ - - if (family == main_dev->family) - { - dev->metric = main_dev->metric; - dev->mtu = main_dev->mtu; - dev->pa_alen = main_dev->pa_alen; - dev->hard_header = main_dev->hard_header; - dev->hard_header_cache = main_dev->hard_header_cache; - dev->header_cache_update = main_dev->header_cache_update; - dev->rebuild_header = main_dev->rebuild_header; - } - - /* - * Fill in the generic fields of the device structure. - * not actually used, avoids some dev.c #ifdef's - */ - - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - - dev->family = family; - return 0; -} - - -/* - * Slow alias find (parse the whole hash_tab) - * returns: alias' pointer address - */ - -static struct net_alias **net_alias_slow_findp(struct net_alias_info - *alias_info, struct net_alias *alias) -{ - unsigned idx, n_aliases; - struct net_alias **aliasp; - - /* - * For each alias_info's hash_tab entry, for every alias ... - */ - - n_aliases = alias_info->n_aliases; - for (idx=0; idx < 16 ; idx++) - { - for (aliasp = &alias_info->hash_tab[idx];*aliasp; - aliasp = &(*aliasp)->next) - { - if (*aliasp == alias) - return aliasp; - else - if (--n_aliases == 0) - break; /* faster give up */ - } - } - return NULL; -} - - -/* - * Create alias device for main_dev with given slot num. - * if sa==NULL will create a same_family alias device. - */ - -static struct device *net_alias_dev_create(struct device *main_dev, int slot, - int *err, struct sockaddr *sa, void *data) -{ - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct net_alias_type *nat; - struct device *dev; - unsigned long flags; - int family; - __u32 addr32; - - /* FIXME: lock */ - - alias_info = main_dev->alias_info; - - /* - * If NULL address given, take family from main_dev - */ - - family = (sa)? sa->sa_family : main_dev->family; - - /* - * Check if wanted family has a net_alias_type object registered - */ - - nat = nat_getbytype(family); - if (!nat) - { -#ifdef CONFIG_KERNELD - char modname[20]; - sprintf (modname,"netalias-%d", family); - request_module(modname); - - nat = nat_getbytype(family); - if (!nat) - { -#endif - printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n", - main_dev->name, slot, family); - /* *err = -EAFNOSUPPORT; */ - *err = -EINVAL; - return NULL; -#ifdef CONFIG_KERNELD - } -#endif - } - - /* - * Do not allow creation over downed devices - */ - - *err = -EIO; - - if (! (main_dev->flags & IFF_UP) ) - return NULL; - - /* - * If first alias, must also create alias_info - */ - - *err = -ENOMEM; - - if (!alias_info) - { - alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL); - if (!alias_info) - return NULL; /* ENOMEM */ - memset(alias_info, 0, sizeof(struct net_alias_info)); - } - - if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL))) - return NULL; /* ENOMEM */ - - memset(alias, 0, sizeof(struct net_alias)); - alias->slot = slot; - alias->main_dev = main_dev; - alias->nat = nat; - alias->next = NULL; - alias->data = data; - sprintf(alias->name, "%s:%d", main_dev->name, slot); - - /* - * Initialise alias' device structure - */ - - net_alias_devsetup(alias, nat, sa); - - dev = &alias->dev; - - save_flags(flags); - cli(); - - /* - * bind alias to its object type - * nat_bind calls nat->alias_init_1 - */ - - nat_bind(nat, alias, sa); - - /* - * If no address passed, take from device (could have been - * set by nat->alias_init_1) - */ - - addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr; - - /* - * Store hash key in alias: will speed-up rehashing and deletion - */ - - alias->hash = HASH(addr32, family); - - /* - * Insert alias in hashed linked list - */ - - aliasp = &alias_info->hash_tab[alias->hash]; - alias->next = *aliasp; - *aliasp = alias; - - /* - * If first alias ... - */ - - if (!alias_info->n_aliases++) - { - alias_info->taildev = main_dev; - main_dev->alias_info = alias_info; - } - - /* - * add device at tail (just after last main_dev alias) - */ - - dev->next = alias_info->taildev->next; - alias_info->taildev->next = dev; - alias_info->taildev = dev; - restore_flags(flags); - return dev; -} - - -/* - * Delete one main_dev alias (referred by its slot num) - */ - -static struct device *net_alias_dev_delete(struct device *main_dev, int slot, - int *err) -{ - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct device *dev; - unsigned n_aliases; - unsigned long flags; - struct net_alias_type *nat; - struct device *prevdev; - - /* FIXME: lock */ - *err = -ENODEV; - - if (main_dev == NULL) - return NULL; - - /* - * Does main_dev have aliases? - */ - - alias_info = main_dev->alias_info; - if (!alias_info) - return NULL; /* ENODEV */ - - n_aliases = alias_info->n_aliases; - - /* - * Find device that holds the same slot number (could also - * be strcmp() ala dev_get). - */ - - for (prevdev=main_dev, alias = NULL; - prevdev->next && n_aliases; prevdev = prevdev->next) - { - if (!(alias = prevdev->next->my_alias)) - { - printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n"); - continue; /* or should give up? */ - } - if (alias->slot == slot) - break; - alias = NULL; - n_aliases--; - } - - if (!alias) - return NULL; /* ENODEV */ - - dev = &alias->dev; - - /* - * Find alias hashed entry - */ - - for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; - aliasp = &(*aliasp)->next) - { - if(*aliasp == alias) - break; - } - - /* - * If not found (???), try a full search - */ - - if (*aliasp != alias) - { - if ((aliasp = net_alias_slow_findp(alias_info, alias))) - printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name); - else - { - printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name); - return NULL; /* ENODEV */ - } - } - nat = alias->nat; - - save_flags(flags); - cli(); - - /* - * Unbind alias from alias_type obj. - */ - - nat_unbind(nat, alias); - - /* - * Is alias at tail? - */ - - if ( dev == alias_info->taildev ) - alias_info->taildev = prevdev; - - /* - * Unlink and close device - */ - prevdev->next = dev->next; - dev_close(dev); - - /* - * Unlink alias - */ - - *aliasp = (*aliasp)->next; - if (--alias_info->n_aliases == 0) /* last alias */ - main_dev->alias_info = NULL; - - restore_flags(flags); - - /* - * Now free structures - */ - - kfree_s(alias, sizeof(struct net_alias)); - if (main_dev->alias_info == NULL) - kfree_s(alias_info, sizeof(struct net_alias_info)); - - /* - * Deletion ok (*err=0), NULL device returned. - */ - - *err = 0; - return NULL; -} - -/* - * Free all main device aliasing stuff - * will be called on dev_close(main_dev) - */ - -static void net_alias_free(struct device *main_dev) -{ - struct net_alias_info *alias_info; - struct net_alias *alias; - struct net_alias_type *nat; - struct device *dev; - unsigned long flags; - - /* - * Do I really have aliases? - */ - - if (!(alias_info = main_dev->alias_info)) - return; - - /* - * Fast device link "short-circuit": set main_dev->next to - * device after last alias - */ - - save_flags(flags); - cli(); - - dev = main_dev->next; - main_dev->next = alias_info->taildev->next; - main_dev->alias_info = NULL; - alias_info->taildev->next = NULL; - - restore_flags(flags); - - /* - * Loop over alias devices, free and dev_close() - */ - - while (dev) - { - if (net_alias_is(dev)) - { - alias = dev->my_alias; - if (alias->main_dev == main_dev) - { - /* - * unbind alias from alias_type object - */ - nat = alias->nat; - if (nat) - { - nat_unbind(nat, alias); - } /* else error/printk ??? */ - - dev_close(dev); - dev = dev->next; - - kfree_s(alias, sizeof(struct net_alias)); - continue; - } - else - printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n", - main_dev->name, alias->name); - } - else - { - printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n", - main_dev->name); - } - dev = dev->next; - } - - kfree_s(alias_info, sizeof(alias_info)); - return; -} - -/* - * dev_get() with added alias naming magic. - */ - -struct device *net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, - struct sockaddr *sa, void *data) -{ - struct device *dev; - char *sptr,*eptr; - int slot = 0; - int delete = 0; - - *err = -ENODEV; - if ((dev=dev_get(dev_name))) - return dev; - - /* - * Want alias naming magic? - */ - - if (!aliasing_ok) - return NULL; - - if (!dev_name || !*dev_name) - return NULL; - - /* - * Find the first ':' , must be followed by, at least, 1 char - */ - - sptr=strchr(dev_name,':'); - if (sptr==NULL || !sptr[1]) - return NULL; - -#if 0 - for (sptr=dev_name ; *sptr ; sptr++) - if(*sptr==':') - break; - if (!*sptr || !*(sptr+1)) - return NULL; -#endif - /* - * Seems to be an alias name, fetch main device - */ - - *sptr='\0'; - if (!(dev=dev_get(dev_name))) - return NULL; - *sptr++=':'; - - /* - * Fetch slot number - */ - - slot = simple_strtoul(sptr,&eptr,10); - if (slot >= NET_ALIAS_MAX_SLOT) - return NULL; - - /* - * If last char is '-', it is a deletion request - */ - - if (eptr[0] == '-' && !eptr[1] ) - delete++; - else if (eptr[0]) - return NULL; - - /* - * Well... let's work. - */ - - if (delete) - return net_alias_dev_delete(dev, slot, err); - else - return net_alias_dev_create(dev, slot, err, sa, data); -} - - -/* - * Rehash alias device with address supplied. - */ - -int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa) -{ - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct device *main_dev; - unsigned long flags; - struct net_alias_type *o_nat, *n_nat; - unsigned n_hash; - - /* - * Defensive ... - */ - - if (dev == NULL) - return -1; - if ( (alias = dev->my_alias) == NULL ) - return -1; - - if (!sa) - { - printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n"); - return -1; - } - - /* - * Defensive. should not happen. - */ - - if ( (main_dev = alias->main_dev) == NULL ) - { - printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name); - return -1; - } - - /* - * Defensive. should not happen. - */ - - if (!(alias_info=main_dev->alias_info)) - { - printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name); - return -1; - } - - /* - * Will the request also change device family? - */ - - o_nat = alias->nat; - if (!o_nat) - { - printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name); - return -1; - } - - /* - * Point to new alias_type obj. - */ - - if (o_nat->type == sa->sa_family) - n_nat = o_nat; - else - { - n_nat = nat_getbytype(sa->sa_family); - if (!n_nat) - { - printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family); - return -1; - } - } - - /* - * New hash key. if same as old AND same type (family) return; - */ - - n_hash = nat_hash_key(n_nat, sa); - if (n_hash == alias->hash && o_nat == n_nat ) - return 0; - - /* - * Find alias in hashed list - */ - - for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; - aliasp = &(*aliasp)->next) - { - if (*aliasp == alias) - break; - } - - /* - * Not found (???). try a full search - */ - - if(!*aliasp) - { - if ((aliasp = net_alias_slow_findp(alias_info, alias))) - { - printk(KERN_WARNING - "net_alias_rehash(%s): bad hashing recovered\n", alias->name); - } - else - { - printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name); - return -1; - } - } - - save_flags(flags); - cli(); - - /* - * If type (family) changed, unlink from old type object (o_nat) - * Will call o_nat->alias_done_1() - */ - - if (o_nat != n_nat) - nat_unbind(o_nat, alias); - - /* - * If diff hash key, change alias position in hashed list - */ - - if (n_hash != alias->hash) - { - *aliasp = (*aliasp)->next; - alias->hash = n_hash; - aliasp = &alias_info->hash_tab[n_hash]; - alias->next = *aliasp; - *aliasp = alias; - } - - /* - * If type (family) changed link to new type object (n_nat) - * will call n_nat->alias_init_1() - */ - - if (o_nat != n_nat) - nat_bind(n_nat, alias, sa); - - restore_flags(flags); - return 0; -} - - - - -/* - * Implements /proc/net/alias_types entry - * Shows net_alias_type objects registered. - */ - -int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy) -{ - off_t pos=0, begin=0; - int len=0; - struct net_alias_type *nat; - unsigned idx; - len=sprintf(buffer,"type name n_attach\n"); - for (idx=0 ; idx < 16 ; idx++) - { - for (nat = nat_base[idx]; nat ; nat = nat->next) - { - len += sprintf(buffer+len, "%-7d %-15s %-7d\n", - nat->type, nat->name,nat->n_attach); - pos=begin+len; - if(posoffset+length) - break; - } - } - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; -} - - -/* - * Implements /proc/net/aliases entry, shows alias devices. - * calls alias nat->alias_print_1 if not NULL and formats everything - * to a fixed rec. size without using local (stack) buffers - * - */ - -#define NET_ALIASES_RECSIZ 64 - -int net_alias_getinfo(char *buffer, char **start, off_t offset, - int length, int dummy) -{ - off_t pos=0, begin=0; - int len=0; - int dlen; - struct net_alias_type *nat; - struct net_alias *alias; - struct device *dev; - - len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address"); - for (dev = dev_base; dev ; dev = dev->next) - { - if (net_alias_is(dev)) - { - alias = dev->my_alias; - nat = alias->nat; - dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family); - - /* - * Call alias_type specific print function. - */ - - if (nat->alias_print_1) - dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen); - else - dlen += sprintf(buffer+len+dlen, "-"); - - /* - * Fill with spaces if needed - */ - - if (dlen < NET_ALIASES_RECSIZ) - memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen); - - /* - * Truncate to NET_ALIASES_RECSIZ - */ - - len += NET_ALIASES_RECSIZ; - buffer[len-1] = '\n'; - - pos=begin+len; - if(posoffset+length) - break; - } - } - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; -} - - -/* - * Notifier for devices events - */ - -int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct device *dev = ptr; - - if (event == NETDEV_DOWN) - { -#ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias: NETDEV_DOWN for %s received\n", dev->name); -#endif - if (net_alias_has(dev)) - net_alias_free(dev); - } - - if (event == NETDEV_UP) - { -#ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias: NETDEV_UP for %s received\n", dev->name); -#endif - dev->alias_info = 0; - } - - return NOTIFY_DONE; -} - - -/* - * Device aliases address comparison workhorse - * No checks for nat and alias_info, must be !NULL - */ - -extern __inline__ struct device *nat_addr_chk(struct net_alias_type *nat, - struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off) -{ - struct net_alias *alias; - for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)]; - alias; alias = alias->next) - { - if (alias->dev.family != sa->sa_family) - continue; - - /* - * Nat_dev_addr_chk_1 will call type specific address - * cmp function. - */ - - if (alias->dev.flags & flags_on && - !(alias->dev.flags & flags_off) && - nat_dev_addr_chk_1(nat,&alias->dev,sa)) - return &alias->dev; - } - return NULL; -} - -/* - * Nat_addr_chk enough for protocols whose addr is (fully) stored at - * pa_addr. Note that nat pointer is ignored because of static comparison. - */ - -extern __inline__ struct device *nat_addr_chk32(struct net_alias_type *nat, - struct net_alias_info *alias_info, int family, __u32 addr32, - int flags_on, int flags_off) -{ - struct net_alias *alias; - for (alias=alias_info->hash_tab[HASH(addr32,family)]; - alias; alias=alias->next) - { - if (alias->dev.family != family) - continue; - /* - * "hard" (static) comparison between addr32 and pa_addr. - */ - - if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && - addr32 == alias->dev.pa_addr) - return &alias->dev; - } - return NULL; -} - -/* - * Returns alias device with specified address AND flags_on AND flags_off, - * else NULL. - * Intended for main devices. - */ - -struct device *net_alias_dev_chk(struct device *main_dev, - struct sockaddr *sa,int flags_on, int flags_off) -{ - struct net_alias_info *alias_info = main_dev->alias_info; - struct net_alias_type *nat; - - /* - * Only if main_dev has aliases - */ - - if (!alias_info) - return NULL; - - /* - * Get alias_type object for sa->sa_family. - */ - - nat = nat_getbytype(sa->sa_family); - if (!nat) - return NULL; - - return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off); -} - -/* - * net_alias_dev_chk enough for protocols whose addr is (fully) stored - * at pa_addr. - */ - -struct device *net_alias_dev_chk32(struct device *main_dev, int family, - __u32 addr32, int flags_on, int flags_off) -{ - struct net_alias_info *alias_info = main_dev->alias_info; - - /* - * only if main_dev has aliases - */ - - if (!alias_info) - return NULL; - return nat_addr_chk32(NULL, alias_info, family, addr32, - flags_on, flags_off); -} - - -/* - * Select closest (main or alias) device to addresses given. If - * there is no further info available, return main_dev (for easier - * calling arrangement). - * - * Should be called early at xxx_rcv() time for device selection - */ - -struct device *net_alias_dev_rcv_sel(struct device *main_dev, - struct sockaddr *sa_src, struct sockaddr *sa_dst) -{ - int family; - struct net_alias_type *nat; - struct net_alias_info *alias_info; - struct device *dev; - - if (main_dev == NULL) - return NULL; - - /* - * If not aliased, don't bother any more - */ - - if ((alias_info = main_dev->alias_info) == NULL) - return main_dev; - - /* - * Find out family - */ - - family = (sa_src)? sa_src->sa_family : - ((sa_dst)? sa_dst->sa_family : AF_UNSPEC); - - if (family == AF_UNSPEC) - return main_dev; - - /* - * Get net_alias_type object for this family - */ - - if ( (nat = nat_getbytype(family)) == NULL ) - return main_dev; - - /* - * First step: find out if dst addr is main_dev's or one of its - * aliases' - */ - - if (sa_dst) - { - if (nat_dev_addr_chk_1(nat, main_dev,sa_dst)) - return main_dev; - - dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0); - - if (dev != NULL) - return dev; - } - - /* - * Second step: find the rcv addr 'closest' alias through nat - * method call - */ - - if ( sa_src == NULL || nat->dev_select == NULL) - return main_dev; - - dev = nat->dev_select(nat, main_dev, sa_src); - - if (dev == NULL || dev->family != family) - return main_dev; - - /* - * Dev ok only if it is alias of main_dev - */ - - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; - - /* - * Do not return NULL. - */ - - return (dev)? dev : main_dev; - -} - -/* - * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols. - */ - -struct device *net_alias_dev_rcv_sel32(struct device *main_dev, int family, - __u32 src, __u32 dst) -{ - struct net_alias_type *nat; - struct net_alias_info *alias_info; - struct sockaddr_in sin_src; - struct device *dev; - - if (main_dev == NULL) - return NULL; - - /* - * If not aliased, don't bother any more - */ - - if ((alias_info = main_dev->alias_info) == NULL) - return main_dev; - - /* - * Early return if dst is main_dev's address - */ - - if (dst == main_dev->pa_addr) - return main_dev; - - if (family == AF_UNSPEC) - return main_dev; - - /* - * Get net_alias_type object for this family - */ - - if ( (nat = nat_getbytype(family)) == NULL ) - return main_dev; - - /* - * First step: find out if dst address one of main_dev aliases' - */ - - if (dst) - { - dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0); - if (dev) - return dev; - } - - /* - * Second step: find the rcv addr 'closest' alias through nat - * method call - */ - - if ( src == 0 || nat->dev_select == NULL) - return main_dev; - - sin_src.sin_family = family; - sin_src.sin_addr.s_addr = src; - - dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src); - - if (dev == NULL || dev->family != family) - return main_dev; - - /* - * Dev ok only if it is alias of main_dev - */ - - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; - - /* - * Do not return NULL. - */ - - return (dev)? dev : main_dev; -} - - -/* - * Device event hook - */ - -static struct notifier_block net_alias_dev_notifier = -{ - net_alias_device_event, - NULL, - 0 -}; - -#ifndef ALIAS_USER_LAND_DEBUG -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry proc_net_alias_types = { - PROC_NET_ALIAS_TYPES, 11, "alias_types", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - net_alias_types_getinfo -}; -static struct proc_dir_entry proc_net_aliases = { - PROC_NET_ALIASES, 7, "aliases", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - net_alias_getinfo -}; -#endif -#endif - -/* - * Net_alias initialisation called from net_dev_init(). - */ - -__initfunc(void net_alias_init(void)) -{ - - /* - * Register device events notifier - */ - - register_netdevice_notifier(&net_alias_dev_notifier); - - /* - * Register /proc/net entries - */ - -#ifndef ALIAS_USER_LAND_DEBUG -#ifdef CONFIG_PROC_FS - proc_net_register(&proc_net_alias_types); - proc_net_register(&proc_net_aliases); -#endif -#endif - -} - -/* - * Net_alias type object registering func. - */ - -int register_net_alias_type(struct net_alias_type *nat, int type) -{ - unsigned hash; - unsigned long flags; - if (!nat) - { - printk(KERN_ERR "register_net_alias_type(): NULL arg\n"); - return -EINVAL; - } - nat->type = type; - nat->n_attach = 0; - hash = nat->type & 0x0f; - save_flags(flags); - cli(); - nat->next = nat_base[hash]; - nat_base[hash] = nat; - restore_flags(flags); - return 0; -} - -/* - * Net_alias type object unreg. - */ - -int unregister_net_alias_type(struct net_alias_type *nat) -{ - struct net_alias_type **natp; - unsigned hash; - unsigned long flags; - - if (!nat) - { - printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n"); - return -EINVAL; - } - - /* - * Only allow unregistration if it has no attachments - */ - - if (nat->n_attach) - { - printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n", - nat->n_attach); - return -EINVAL; - } - hash = nat->type & 0x0f; - save_flags(flags); - cli(); - for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next) - { - if (nat==(*natp)) - { - *natp = nat->next; - restore_flags(flags); - return 0; - } - } - restore_flags(flags); - printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type); - return -EINVAL; -} - diff --git a/net/core/scm.c b/net/core/scm.c index e5fa793a7..5a6d24c40 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -205,25 +205,25 @@ error: return err; } -void put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) +int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) { struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control; + struct cmsghdr cmhdr; int cmlen = CMSG_LEN(len); int err; if (cm==NULL || msg->msg_controllen < sizeof(*cm)) { msg->msg_flags |= MSG_CTRUNC; - return; + return 0; /* XXX: return error? check spec. */ } if (msg->msg_controllen < cmlen) { msg->msg_flags |= MSG_CTRUNC; cmlen = msg->msg_controllen; } - err = put_user(level, &cm->cmsg_level); - if (!err) - err = put_user(type, &cm->cmsg_type); - if (!err) - err = put_user(cmlen, &cm->cmsg_len); + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + err = copy_to_user(cm, &cmhdr, sizeof cmhdr); if (!err) err = copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)); if (!err) { @@ -231,6 +231,7 @@ void put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) msg->msg_control += cmlen; msg->msg_controllen -= cmlen; } + return err; } void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 06c321e4f..6baf37c03 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -176,7 +176,7 @@ struct sk_buff *alloc_skb(unsigned int size,int priority) skb->dst = NULL; skb->destructor = NULL; memset(skb->cb, 0, sizeof(skb->cb)); - skb->priority = SOPRI_NORMAL; + skb->priority = 0; atomic_inc(&net_skbcount); atomic_set(&skb->users, 1); diff --git a/net/core/sock.c b/net/core/sock.c index 65cee3b62..725474887 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -75,6 +75,7 @@ * protocol private data. * Steve Whitehouse: Added various other default routines * common to several socket families. + * Chris Evans : Call suser() check last on F_SETOWN * * To Fix: * @@ -101,6 +102,7 @@ #include #include #include +#include #include #include @@ -143,6 +145,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, int valbool; int err; struct linger ling; + struct ifreq req; int ret = 0; /* @@ -241,7 +244,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, break; case SO_PRIORITY: - if (val >= 0 && val < DEV_NUMBUFFS) + if (val >= 0 && val <= 7) sk->priority = val; else return(-EINVAL); @@ -317,6 +320,46 @@ int sock_setsockopt(struct socket *sock, int level, int optname, return -EINVAL; break; #endif + case SO_BINDTODEVICE: + /* Bind this socket to a particular device like "eth0", + * as specified in an ifreq structure. If the device + * is "", socket is NOT bound to a device. + */ + + if (!valbool) { + sk->bound_dev_if = 0; + } + else { + if (copy_from_user(&req, optval, sizeof(req)) < 0) + return -EFAULT; + + /* Remove any cached route for this socket. */ + if (sk->dst_cache) { + ip_rt_put((struct rtable*)sk->dst_cache); + sk->dst_cache = NULL; + } + + if (req.ifr_ifrn.ifrn_name[0] == '\0') { + sk->bound_dev_if = 0; + } + else { + struct device *dev = dev_get(req.ifr_ifrn.ifrn_name); + if (!dev) + return -EINVAL; + sk->bound_dev_if = dev->ifindex; + if (sk->daddr) { + int ret; + ret = ip_route_output((struct rtable**)&sk->dst_cache, + sk->daddr, sk->saddr, + sk->ip_tos, sk->bound_dev_if); + if (ret) + return ret; + } + } + } + return 0; + + /* We implement the SO_SNDLOWAT etc to not be settable (1003.1g 5.3) */ default: @@ -627,7 +670,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne produce annoying no free page messages still.... */ skb = sock_wmalloc(sk, size, 0 , GFP_BUFFER); if(!skb) - skb=sock_wmalloc(sk, fallback, 0, GFP_KERNEL); + skb=sock_wmalloc(sk, fallback, 0, sk->allocation); } /* @@ -669,7 +712,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne * In any case I'd delete this check at all, or * change it to: */ - if (atomic_read(&sk->wmem_alloc) + size >= sk->sndbuf) + if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf) #endif { sk->socket->flags &= ~SO_NOSPACE; @@ -896,8 +939,9 @@ int sock_no_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) * way to make sure that you can't send a sigurg to * another process. */ - if (!suser() && current->pgrp != -arg && - current->pid != arg) return(-EPERM); + if (current->pgrp != -arg && + current->pid != arg && + !suser()) return(-EPERM); sk->proc = arg; return(0); case F_GETOWN: @@ -967,7 +1011,6 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->allocation = GFP_KERNEL; sk->rcvbuf = sysctl_rmem_default*2; sk->sndbuf = sysctl_wmem_default*2; - sk->priority = SOPRI_NORMAL; sk->state = TCP_CLOSE; sk->zapped = 1; sk->socket = sock; -- cgit v1.2.3