summaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
commit967c65a99059fd459b956c1588ce0ba227912c4e (patch)
tree8224d013ff5d255420713d05610c7efebd204d2a /net/core
parente20c1cc1656a66a2773bca4591a895cbc12696ff (diff)
Merge with Linux 2.1.72, part 1.
Diffstat (limited to 'net/core')
-rw-r--r--net/core/Makefile8
-rw-r--r--net/core/dev.c1301
-rw-r--r--net/core/dev_mcast.c21
-rw-r--r--net/core/iovec.c92
-rw-r--r--net/core/net_alias.c1464
-rw-r--r--net/core/scm.c15
-rw-r--r--net/core/skbuff.c2
-rw-r--r--net/core/sock.c55
8 files changed, 747 insertions, 2211 deletions
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 <rzsfl@rz.uni-sb.de>
* Alan Cox <gw4pts@gw4pts.ampr.org>
* David Hinds <dhinds@allegro.stanford.edu>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
* Changes:
* Alan Cox : device private ioctl copies fields back.
@@ -61,24 +62,20 @@
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
-#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/notifier.h>
-#include <net/ip.h>
-#include <net/route.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <net/arp.h>
+#include <linux/rtnetlink.h>
#include <net/slhc.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <net/br.h>
-#include <linux/net_alias.h>
+#include <net/pkt_sched.h>
#include <linux/init.h>
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
@@ -90,6 +87,7 @@
extern int plip_init(void);
#endif
+
const char *if_port_text[] = {
"unknown",
"BNC",
@@ -101,12 +99,6 @@ const char *if_port_text[] = {
};
/*
- * 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.
*/
@@ -334,6 +319,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(ct<DEV_NUMBUFFS)
- {
- struct sk_buff *skb;
- while((skb=skb_dequeue(&dev->buffs[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
/*
@@ -823,12 +727,6 @@ void net_bh(void)
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)
* and the main protocol list which is hashed perfectly for normal protocols.
@@ -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; i<NPROTO; i++) {
+ int done;
+
+ if (gifconf[i] == NULL)
+ continue;
- memset(&ifr, 0, sizeof(struct ifreq));
- strcpy(ifr.ifr_name, dev->name);
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ 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 <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
-#include <linux/net_alias.h>
/*
@@ -70,19 +69,6 @@ void dev_mc_upload(struct device *dev)
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, <jjciarla@raiz.uncu.edu.ar>
- * Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
- *
- * 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 <src,dst>
- * 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 <linux/config.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/notifier.h>
-#include <linux/if.h>
-#include <linux/inet.h>
-#include <linux/in.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-
-#include <linux/net_alias.h>
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#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(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- }
- }
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
-}
-
-
-/*
- * 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(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- }
- }
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
-}
-
-
-/*
- * 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 <src,dst> 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 <linux/mm.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -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;