/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		Interface (streams) handling functions.
 *
 * Version:	@(#)dev.c	1.0.19	05/31/93
 *
 * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
 *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Mark Evans, <evansmp@uhura.aston.ac.uk>
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include "inet.h"
#include "dev.h"
#include "eth.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "tcp.h"
#include "skbuff.h"
#include "sock.h"
#include "arp.h"


static struct packet_type arp_packet_type = {
  NET16(ETH_P_ARP),
  0,		/* copy */
  arp_rcv,
  NULL,
  NULL		/* next */
};


static struct packet_type ip_packet_type = {
  NET16(ETH_P_IP),
  0,		/* copy */
  ip_rcv,
  NULL,
  &arp_packet_type
};
   

struct packet_type *ptype_base = &ip_packet_type;
static struct sk_buff *volatile backlog = NULL;
static unsigned long ip_bcast = 0;


/* Return the lesser of the two values. */
static unsigned long
min(unsigned long a, unsigned long b)
{
  if (a < b) return(a);
  return(b);
}


/* Determine a default network mask, based on the IP address. */
static unsigned long
get_mask(unsigned long addr)
{
  unsigned long dst;

  if (addr == 0) return(0);	/* special case */

  dst = ntohl(addr);
  if (IN_CLASSA(dst)) return(htonl(IN_CLASSA_NET));
  if (IN_CLASSB(dst)) return(htonl(IN_CLASSB_NET));
  if (IN_CLASSC(dst)) return(htonl(IN_CLASSC_NET));
  
  /* Something else, probably a subnet. */
  return(0);
}


int
ip_addr_match(unsigned long me, unsigned long him)
{
  int i;

  DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
  DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));

  if (me == him) return(1);
  for (i = 0; i < 4; i++, me >>= 8, him >>= 8) {
	if ((me & 0xFF) != (him & 0xFF)) {
		/*
		 * The only way this could be a match is for
		 * the rest of addr1 to be 0 or 255.
		 */
		if (me != 0 && me != 255) return(0);
		return(1);
	}
  }
  return(1);
}


/* Check the address for our address, broadcasts, etc. */
int
chk_addr(unsigned long addr)
{
  struct device *dev;
  unsigned long dst;

  DPRINTF((DBG_DEV, "chk_addr(%s) --> ", in_ntoa(addr)));
  dst = ntohl(addr);

  /* Accept both `all ones' and `all zeros' as BROADCAST. */
  if (dst == INADDR_ANY || dst == INADDR_BROADCAST) {
	DPRINTF((DBG_DEV, "BROADCAST\n"));
	return(IS_BROADCAST);
  }

  /* Accept all of the `loopback' class A net. */
  if ((dst & IN_CLASSA_NET) == 0x7F000000) {
	DPRINTF((DBG_DEV, "LOOPBACK\n"));

	/*
	 * We force `loopback' to be equal to MY_ADDR.
	 */
	return(IS_MYADDR);
	/* return(IS_LOOPBACK); */
  }

  /* OK, now check the interface addresses. */
  for (dev = dev_base; dev != NULL; dev = dev->next) {
	if (dev->pa_addr == 0) continue;

	/* Is it the exact IP address? */
	if (addr == dev->pa_addr) {
		DPRINTF((DBG_DEV, "MYADDR\n"));
		return(IS_MYADDR);
	}

	/* Nope. Check for a subnetwork broadcast. */
	if ((addr & dev->pa_mask) == (dev->pa_addr & dev->pa_mask)) {
		if ((addr & ~dev->pa_mask) == 0) {
			DPRINTF((DBG_DEV, "SUBBROADCAST-0\n"));
			return(IS_BROADCAST);
		}
		if (((addr & ~dev->pa_mask) | dev->pa_mask)
						== INADDR_BROADCAST) {
			DPRINTF((DBG_DEV, "SUBBROADCAST-1\n"));
			return(IS_BROADCAST);
		}
	}
  }

  DPRINTF((DBG_DEV, "NONE\n"));
  return(0);		/* no match at all */
}


/*
 * Retrieve our own address.
 * Because the loopback address (127.0.0.1) is already recognized
 * automatically, we can use the loopback interface's address as
 * our "primary" interface.  This is the addressed used by IP et
 * al when it doesn't know which address to use (i.e. it does not
 * yet know from or to which interface to go...).
 */
unsigned long
my_addr(void)
{
  struct device *dev;

  for (dev = dev_base; dev != NULL; dev = dev->next) {
	if (dev->flags & IFF_LOOPBACK) return(dev->pa_addr);
  }
  return(0);
}


/* Add a protocol ID to the list.  This will change soon. */
void
dev_add_pack(struct packet_type *pt)
{
  struct packet_type *p1;

  pt->next = ptype_base;

  /* See if we need to copy it. */
  for (p1 = ptype_base; p1 != NULL; p1 = p1->next) {
	if (p1->type == pt->type) {
		pt->copy = 1;
		break;
	}
  }
  ptype_base = pt;
}


/* Remove a protocol ID from the list.  This will change soon. */
void
dev_remove_pack(struct packet_type *pt)
{
  struct packet_type *lpt, *pt1;

  if (pt == ptype_base) {
	ptype_base = pt->next;
	return;
  }

  lpt = NULL;
  for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) {
	if (pt1->next == pt ) {
		cli();
		if (!pt->copy && lpt) lpt->copy = 0;
		pt1->next = pt->next;
		sti();
		return;
	}

	if (pt1->next -> type == pt ->type) {
		lpt = pt1->next;
	}
  }
}


/* Find an interface in the list. This will change soon. */
struct device *
dev_get(char *name)
{
  struct device *dev;

  for (dev = dev_base; dev != NULL; dev = dev->next) {
	if (strcmp(dev->name, name) == 0) return(dev);
  }
  return(NULL);
}


/* Find an interface that can handle addresses for a certain address. */
struct device *
dev_check(unsigned long addr)
{
  struct device *dev;

  for (dev = dev_base; dev; dev = dev->next)
	if ((dev->flags & IFF_UP) && (dev->flags & IFF_POINTOPOINT) &&
	    (addr == dev->pa_dstaddr))
		return dev;
  for (dev = dev_base; dev; dev = dev->next)
	if ((dev->flags & IFF_UP) && !(dev->flags & IFF_POINTOPOINT) &&
	    addr == (dev->flags & IFF_LOOPBACK ? dev->pa_addr : dev->pa_addr &
	    dev->pa_mask))
		break;
  /* no need to check broadcast addresses */
  return dev;
}


/* Prepare an interface for use. */
int
dev_open(struct device *dev)
{
  int ret = 0;

  if (dev->open) ret = dev->open(dev);
  if (ret == 0) dev->flags |= (IFF_UP | IFF_RUNNING);

  return(ret);
}


/* Completely shutdown an interface. */
int
dev_close(struct device *dev)
{
  if (dev->flags != 0) {
	dev->flags = 0;
	if (dev->stop) dev->stop(dev);
	rt_flush(dev);
	dev->pa_addr = 0;
	dev->pa_dstaddr = 0;
	dev->pa_brdaddr = 0;
	dev->pa_mask = 0;
  }

  return(0);
}


/* Send (or queue for sending) a packet. */
void
dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
  struct sk_buff *skb2;
  int where = 0;		/* used to say if the packet should go	*/
				/* at the front or the back of the	*/
				/* queue.				*/

  DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
							skb, dev, pri));

  if (dev == NULL) {
	printk("dev.c: dev_queue_xmit: dev = NULL\n");
	return;
  }
  
  skb->dev = dev;
  if (skb->next != NULL) {
	/* Make sure we haven't missed an interrupt. */
	dev->hard_start_xmit(NULL, dev);
	return;
  }

  if (pri < 0) {
	pri = -pri-1;
	where = 1;
  }

  if (pri >= DEV_NUMBUFFS) {
	printk("bad priority in dev_queue_xmit.\n");
	pri = 1;
  }

  if (dev->hard_start_xmit(skb, dev) == 0) {
	return;
  }

  /* Put skb into a bidirectional circular linked list. */
  DPRINTF((DBG_DEV, "dev_queue_xmit dev->buffs[%d]=%X\n",
					pri, dev->buffs[pri]));

  /* Interrupts should already be cleared by hard_start_xmit. */
  cli();
  if (dev->buffs[pri] == NULL) {
	dev->buffs[pri] = skb;
	skb->next = skb;
	skb->prev = skb;
  } else {
	if (where) {
		skb->next = (struct sk_buff *) dev->buffs[pri];
		skb->prev = (struct sk_buff *) dev->buffs[pri]->prev;
		skb->prev->next = skb;
		skb->next->prev = skb;
		dev->buffs[pri] = skb;
	} else {
		skb2 = (struct sk_buff *) dev->buffs[pri];
		skb->next = skb2;
		skb->prev = skb2->prev;
		skb->next->prev = skb;
		skb->prev->next = skb;
	}
  }
  skb->magic = DEV_QUEUE_MAGIC;
  sti();
}


/*
 * Fetch a packet from a device driver.
 * This function is the base level entry point for all drivers that
 * want to send a packet to the upper (protocol) levels.  It takes
 * care of de-multiplexing the packet to the various modules based
 * on their protocol ID.
 *
 * Return values:	1 <- exit I can't do any more
 *			0 <- feed me more (i.e. "done", "OK"). 
 */
int
dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
{
  static int dropping = 0;
  struct sk_buff *skb = NULL;
  unsigned char *to;
  int amount, left;
  int len2;

  if (dev == NULL || buff == NULL || len <= 0) return(1);
  if (dropping && backlog != NULL) {
	return(1);
  }
  if (dropping) printk("INET: dev_rint: no longer dropping packets.\n");
  dropping = 0;

  if (flags & IN_SKBUFF) {
	skb = (struct sk_buff *) buff;
  } else {
	skb = (struct sk_buff *) kmalloc(sizeof(*skb) + len, GFP_ATOMIC);
	if (skb == NULL) {
		printk("dev_rint: packet dropped (no memory) !\n");
		dropping = 1;
		return(1);
	}
	skb->lock = 0;
	skb->mem_len = sizeof(*skb) + len;
	skb->mem_addr = (struct sk_buff *) skb;

	/* First we copy the packet into a buffer, and save it for later. */
	to = (unsigned char *) (skb + 1);
	left = len;
	len2 = len;
	while (len2 > 0) {
		amount = min(len2, (unsigned long) dev->rmem_end -
						(unsigned long) buff);
		memcpy(to, buff, amount);
		len2 -= amount;
		left -= amount;
		buff += amount;
		to += amount;
		if ((unsigned long) buff == dev->rmem_end)
			buff = (unsigned char *) dev->rmem_start;
	}
  }
  skb->len = len;
  skb->dev = dev;
  skb->sk = NULL;

  /* Now add it to the backlog. */
  cli();
  if (backlog == NULL) {
	skb->prev = skb;
	skb->next = skb;
	backlog = skb;
  } else {
	skb->prev = (struct sk_buff *) backlog->prev;
	skb->next = (struct sk_buff *) backlog;
	skb->next->prev = skb;
	skb->prev->next = skb;
  }
  sti();
   
  /* If any packet arrived, mark it for processing. */
  if (backlog != NULL) mark_bh(INET_BH);

  /* OK, all done. */
  return(0);
}


/* This routine causes all interfaces to try to send some data. */
void
dev_transmit(void)
{
  struct device *dev;

  for (dev = dev_base; dev != NULL; dev = dev->next) {
	if (!dev->tbusy) {
		dev_tint(dev);
	}
  }
}


/*
 * This function gets called periodically, to see if we can
 * process any data that came in from some interface.
 */
void
inet_bh(void *tmp)
{
  struct sk_buff *skb;
  struct packet_type *ptype;
  unsigned short type;
  unsigned char flag = 0;
  static volatile int in_bh = 0;

  /* Check && mark our BUSY state. */
  cli();
  if (in_bh != 0) {
	sti();
	return;
  }
  in_bh = 1;
  sti();

  /* Can we send anything now? */
  dev_transmit();
  
  /* Any data left to process? */
  cli();
  while (backlog != NULL) {
	skb = (struct sk_buff *) backlog;
	if (skb->next == skb) {
		backlog = NULL;
	} else {
		backlog = skb->next;
		skb->next->prev = skb->prev;
		skb->prev->next = skb->next;
	}
	sti();

       /*
	* Bump the pointer to the next structure.
	* This assumes that the basic 'skb' pointer points to
	* the MAC header, if any (as indicated by its "length"
	* field).  Take care now!
	*/
       skb->h.raw = (unsigned char *) (skb + 1) + skb->dev->hard_header_len;
       skb->len -= skb->dev->hard_header_len;

       /*
	* Fetch the packet protocol ID.  This is also quite ugly, as
	* it depends on the protocol driver (the interface itself) to
	* know what the type is, or where to get it from.  The Ethernet
	* interfaces fetch the ID from the two bytes in the Ethernet MAC
	* header (the h_proto field in struct ethhdr), but drivers like
	* SLIP and PLIP have no alternative but to force the type to be
	* IP or something like that.  Sigh- FvK
	*/
       type = skb->dev->type_trans(skb, skb->dev);

	/*
	 * We got a packet ID.  Now loop over the "known protocols"
	 * table (which is actually a linked list, but this will
	 * change soon if I get my way- FvK), and forward the packet
	 * to anyone who wants it.
	 */
	for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) {
		if (ptype->type == type) {
			struct sk_buff *skb2;

			if (ptype->copy) {	/* copy if we need to	*/
				skb2 = (struct sk_buff *) kmalloc(skb->mem_len, GFP_ATOMIC);
				if (skb2 == NULL) continue;
				memcpy(skb2, (const void *) skb, skb->mem_len);
				skb2->mem_addr = skb2;
				skb2->lock = 0;
				skb2->h.raw = (unsigned char *)(
				    (unsigned long) skb2 +
				    (unsigned long) skb->h.raw -
				    (unsigned long) skb
				);
			} else {
				skb2 = skb;
			}

			/* This used to be in the 'else' part, but then
			 * we don't have this flag set when we get a
			 * protocol that *does* require copying... -FvK
			 */
			flag = 1;

			/* Kick the protocol handler. */
			ptype->func(skb2, skb->dev, ptype);
		}
	}

	/*
	 * That's odd.  We got an unknown packet.  Who's using
	 * stuff like Novell or Amoeba on this network??
	 */
	if (!flag) {
		DPRINTF((DBG_DEV,
			"INET: unknown packet type 0x%04X (ignored)\n", type));
		skb->sk = NULL;
		kfree_skb(skb, FREE_WRITE);
	}

	/* Again, see if we can transmit anything now. */
	dev_transmit();
	cli();
  }
  in_bh = 0;
  sti();
}


/*
 * This routine is called when an device driver (i.e. an
 * interface) is * ready to transmit a packet.
 */
void
dev_tint(struct device *dev)
{
  int i;
  struct sk_buff *skb;

  for (i = 0; i < DEV_NUMBUFFS; i++) {
        cli();
	while (dev->buffs[i] != NULL) {
		skb = (struct sk_buff *) dev->buffs[i];
		if (skb->magic != DEV_QUEUE_MAGIC) {
			printk("INET: dev: skb with bad magic-%X:", skb->magic);
			printk("squashing queue\n");
			dev->buffs[i] = NULL;
			continue;
		}

		skb->magic = 0;

		if (skb->next == skb) {
			dev->buffs[i] = NULL;
		} else {
			/* Extra consistency check. */
			if (skb->next == NULL
#ifdef CONFIG_MAX_16M
			    || (unsigned long)(skb->next) > 16*1024*1024
#endif
			) {
				printk("INET: dev: *** bug bad skb->next,");
				printk(", squashing queue\n");
				dev->buffs[i] = NULL;
			} else {
				dev->buffs[i]= skb->next;
				skb->prev->next = skb->next;
				skb->next->prev = skb->prev;
			}
		}

		skb->next = NULL;
		skb->prev = NULL;
		sti();

		/* This will send it through the process again. */
		dev->queue_xmit(skb, dev, -i - 1);
		if (dev->tbusy) return;
		cli();
	}
  }
  sti();
}


/* Perform a SIOCGIFCONF call. */
static int
dev_ifconf(char *arg)
{
  struct ifconf ifc;
  struct ifreq ifr;
  struct device *dev;
  char *pos;
  int len;

  /* Fetch the caller's info block. */
  verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
  memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
  len = ifc.ifc_len;
  pos = ifc.ifc_buf;

  /* Loop over the interfaces, and write an info block for each. */
  for (dev = dev_base; dev != NULL; dev = dev->next) {
	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;

	/* Write this block to the caller's space. */
	memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
	pos += sizeof(struct ifreq);
	len -= sizeof(struct ifreq);
	if (len < sizeof(struct ifreq)) break;
  }

  /* 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;
  memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
  return(pos - arg);
}


/* Called from the PROCfs module. */
int
dev_get_info(char *buffer)
{
  return(dev_ifconf(buffer));
}


/* Perform the SIOCxIFxxx calls. */
static int
dev_ifsioc(void *arg, unsigned int getset)
{
  struct ifreq ifr;
  struct device *dev;
  int ret;

  /* Fetch the caller's info block. */
  verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
  memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));

  /* See which interface the caller is talking about. */
  if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-EINVAL);

  switch(getset) {
	case SIOCGIFFLAGS:
		ifr.ifr_flags = dev->flags;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFFLAGS:
		ret = dev->flags;
		dev->flags = ifr.ifr_flags & (
			IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
			IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
			IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
		if ((ret & IFF_UP) && ((dev->flags & IFF_UP) == 0)) {
			ret = dev_close(dev);
		} else {
			if (((ret & IFF_UP) == 0) && (dev->flags & IFF_UP)) {
				ret = dev_open(dev);
			} else ret = 0;
		}
		break;
	case SIOCGIFADDR:
		(*(struct sockaddr_in *)
		  &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
		(*(struct sockaddr_in *)
		  &ifr.ifr_addr).sin_family = dev->family;
		(*(struct sockaddr_in *)
		  &ifr.ifr_addr).sin_port = 0;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFADDR:
		dev->pa_addr = (*(struct sockaddr_in *)
				 &ifr.ifr_addr).sin_addr.s_addr;
		dev->family = ifr.ifr_addr.sa_family;
		dev->pa_mask = get_mask(dev->pa_addr);
		dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
		ret = 0;
		break;
	case SIOCGIFBRDADDR:
		(*(struct sockaddr_in *)
		  &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
		(*(struct sockaddr_in *)
		  &ifr.ifr_broadaddr).sin_family = dev->family;
		(*(struct sockaddr_in *)
		  &ifr.ifr_broadaddr).sin_port = 0;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFBRDADDR:
		dev->pa_brdaddr = (*(struct sockaddr_in *)
				    &ifr.ifr_broadaddr).sin_addr.s_addr;
		ret = 0;
		break;
	case SIOCGIFDSTADDR:
		(*(struct sockaddr_in *)
		  &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
		(*(struct sockaddr_in *)
		  &ifr.ifr_broadaddr).sin_family = dev->family;
		(*(struct sockaddr_in *)
		  &ifr.ifr_broadaddr).sin_port = 0;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFDSTADDR:
		dev->pa_dstaddr = (*(struct sockaddr_in *)
				    &ifr.ifr_dstaddr).sin_addr.s_addr;
		ret = 0;
		break;
	case SIOCGIFNETMASK:
		(*(struct sockaddr_in *)
		  &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
		(*(struct sockaddr_in *)
		  &ifr.ifr_netmask).sin_family = dev->family;
		(*(struct sockaddr_in *)
		  &ifr.ifr_netmask).sin_port = 0;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFNETMASK:
		dev->pa_mask = (*(struct sockaddr_in *)
				 &ifr.ifr_netmask).sin_addr.s_addr;
		ret = 0;
		break;
	case SIOCGIFMETRIC:
		ifr.ifr_metric = dev->metric;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFMETRIC:
		dev->metric = ifr.ifr_metric;
		ret = 0;
		break;
	case SIOCGIFMTU:
		ifr.ifr_mtu = dev->mtu;
		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
		ret = 0;
		break;
	case SIOCSIFMTU:
		dev->mtu = ifr.ifr_mtu;
		ret = 0;
		break;
	case SIOCGIFMEM:
		printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
		ret = -EINVAL;
		break;
	case SIOCSIFMEM:
		printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
		ret = -EINVAL;
		break;
	default:
		ret = -EINVAL;
  }
  return(ret);
}


/* This function handles all "interface"-type I/O control requests. */
int
dev_ioctl(unsigned int cmd, void *arg)
{
  struct iflink iflink;
  struct ddi_device *dev;
  int ret;

  switch(cmd) {
	case IP_SET_DEV:
		printk("INET: Warning: old-style ioctl(IP_SET_DEV) called!\n");
		return(-EINVAL);
	case SIOCGIFCONF:
		(void) dev_ifconf((char *) arg);
		ret = 0;
		break;
	case SIOCGIFFLAGS:
	case SIOCSIFFLAGS:
	case SIOCGIFADDR:
	case SIOCSIFADDR:
	case SIOCGIFDSTADDR:
	case SIOCSIFDSTADDR:
	case SIOCGIFBRDADDR:
	case SIOCSIFBRDADDR:
	case SIOCGIFNETMASK:
	case SIOCSIFNETMASK:
	case SIOCGIFMETRIC:
	case SIOCSIFMETRIC:
	case SIOCGIFMTU:
	case SIOCSIFMTU:
	case SIOCGIFMEM:
	case SIOCSIFMEM:
		if (!suser()) return(-EPERM);
		ret = dev_ifsioc(arg, cmd);
		break;
	case SIOCSIFLINK:
		if (!suser()) return(-EPERM);
		memcpy_fromfs(&iflink, arg, sizeof(iflink));
		dev = ddi_map(iflink.id);
		if (dev == NULL) return(-EINVAL);

		/* Now allocate an interface and connect it. */
		printk("AF_INET: DDI \"%s\" linked to stream \"%s\"\n",
						dev->name, iflink.stream);
		ret = 0;
		break;
	default:
		ret = -EINVAL;
  }

  return(ret);
}


/* Initialize the DEV module. */
void
dev_init(void)
{
  struct device *dev, *dev2;

  /* Add the devices.
   * If the call to dev->init fails, the dev is removed
   * from the chain disconnecting the device until the
   * next reboot.
   */
  dev2 = NULL;
  for (dev = dev_base; dev != NULL; dev=dev->next) {
	if (dev->init && dev->init(dev)) {
		if (dev2 == NULL) dev_base = dev->next;
		  else dev2->next = dev->next;
	} else {
		dev2 = dev;
	}
  }

  /* Set up some IP addresses. */
  ip_bcast = in_aton("255.255.255.255");
}
