/*     
 * $RCSfile: ethpkt.c,v $
 *
 * x-kernel v3.3
 *
 * This file access the raw ethernet device using a SOCK_PACKET
 * socket.  This method is specific to Linux.  The strategy is copied
 * for the Solaris /dev/nit xkernel layer.
 *
 * It helps to patch the Linux kernel to not require root privelege to
 * create a SOCK_PACKET socket.  Perhaps only members of 'wheel' would
 * be more reasonable.
 *
 * This could also be done with the /dev/xkin, /dev/xkout kernel
 * additions.  But I think this approach is faster and easier to
 * distribute since it requires no kernel patches.
 *
 * $Log: ethpkt.c,v $
 * Revision 1.3  1997/01/28 22:29:24  rrp
 * made updates for port to scout threads.  Socket is now non-blocking.
 *
 * Revision 1.2  1997/01/25  01:25:24  mjk
 * Added ETHPKT_ALLOW mode, to compliment ETHPKT_BLOCK.  This was
 * suggested by Rajeesh to allow simpler configuration when using
 * non-standard ethernet types, and for shared networks.  E.g. use it
 * to block AppleTalk traffic to ease the load on the x-kernel.
 *
 * Revision 1.1  1996/05/31 22:55:47  rrp
 * Initial revision
 *
 * Revision 1.4  1996/05/03  21:49:39  mjk
 * released
 *
 * Revision 1.3  1996/04/24  23:53:56  mjk
 * passed a simple udptest (-trips 5).  Needs more testing but looks
 * good.
 *
 * Revision 1.2  1996/04/24  17:27:00  mjk
 * Switched from asynch io to blocking treads for reads.  Works but
 * recvfrom still reads truncated packets.
 *
 * Revision 1.1  1996/04/23  17:58:27  mjk
 * Initial revision
 *
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <fcntl.h>
#include "xkernel.h"
#include "eth.h"
#include "eth_i.h"
#include "machine.h"
#include "inputProcess.h"
#include "romopt.h"
#include "scout_thread.h"


/* 
 * Defines 
 */

#define ETHPKT_NUMTHREADS	16	/* # of xk event rx handlers 	*/
					/* max rx packet size 		*/
#define ETHPKT_BUFSIZE		(MAX_ETH_DATA_SZ + 100)

#define ETHPKT_DEFDEVNAME	"eth0"	/* default device name 		*/

#define ETHPKT_TXMAPSZ		16
#define ETHPKT_RXMAPSZ		ETHPKT_TXMAPSZ
#define ETHPKT_BLOCKMAPSZ	32
#define ETHPKT_ALLOWMAPSZ	32

#define ETHPKT_ALLOW		0
#define ETHPKT_BLOCK		1



/* 
 * Typedefs 
 */

typedef struct {		/* Protocol State 			*/
  ETHhost	ethaddr;		/* ethernet address		*/
  int		sock;			/* socket file descriptor	*/
  char		devname[IFNAMSIZ];	/* net interface device name	*/
  int			filtermode;	/* block or allow ? 		*/
  int			remap;		/* remap predicate 		*/
  Map			txmap;		/* tx ethtype map 		*/
  Map			rxmap;		/* rx ethtype map 		*/
  Map			blockmap;	/* filtered ethtype map		*/
  Map			allowmap;	/* permityed ethtype map 	*/
  BufferPool		*buffers;	/* incomming data buffer pool	*/
  int			promisc; 	/* promiscuous mode predicate 	*/
  int			dumplen; 	/* size of hex dump 		*/
  Semaphore		mutex;		/* mutex for socket io		*/
} PState;





/* 
 * Prototypes 
 */
void			ethpkt_init(Protl);
void msg2buf(Msg *, char *);
static XkReturn	ethpkt_openenable(Protl, Protl, Protl, Part *);
static XkHandle	ethpkt_push(Sessn, Msg *);
static int		ethpkt_control(Protl, int, char *, int);

static XkReturn	ethpkt_set_promiscuous(Protl, int);
static long		ethpkt_msgload(void *, char *, long);
static void		ethpkt_internal_demux(InputBuffer *);

#ifdef XK_DEBUG
static void		ethpkt_dump(Sessn, FILE *, const char *,
				    unsigned char *, int);
#endif

static void		*ethpkt_rxthread(void *arg);
static InputBuffer	*ethpkt_getpkt(Sessn);

static XkReturn   ethpkt_rom_remap(Protl, char **, int, int, void *);
static XkReturn   ethpkt_rom_block(Protl, char **, int, int, void *);
static XkReturn   ethpkt_rom_allow(Protl, char **, int, int, void *);
static XkReturn   ethpkt_rom_device(Protl, char **, int, int, void *);
static XkReturn   ethpkt_rom_dump(Protl, char **, int, int, void *);



/* 
 * Globals 
 */

int			traceethpktp;	/* trace variable 		*/

					/* ethernet broadcast address 	*/
static ETHhost		bcastaddr = BCAST_ETH_AD; 

					/* rom options			*/
static ProtlRomOpt	ethpkt_opts[] = {
  { "block",	3, ethpkt_rom_block },
  { "allow",	3, ethpkt_rom_allow },
  { "device",	3, ethpkt_rom_device },
  { "remap",	4, ethpkt_rom_remap },
  { "dump",	3, ethpkt_rom_dump }
};


/* 
 * Inline Functions (gcc only)
 */


static inline int
min(int a, int b) {
  return a < b ? a : b;
} /* min */


/* msg2buf is no longer supplied in the message tool, so it is included here. */
/*
 * Copy fragments of the message into a contiguous buffer.  Buffer must be
 * long enough.
 */
void
msg2buf(Msg *msg, char *dst_buf)
{
    MsgWalk cxt;
    void    *buf;
    int     len;

    xTrace2(msg, TR_FUNCTIONAL_TRACE, "msg2Buf(msg=%#lx,dst_buf=%#lx)",
            (u_long)msg, (u_long)dst_buf);

    msgWalkInit(&cxt, msg);
    while ((buf = msgWalkNext(&cxt, &len)) != 0) {
        bcopy(buf, dst_buf, len);
        dst_buf += len;
    } /* while */
    msgWalkDone(&cxt);

    xTrace0(msg, TR_FUNCTIONAL_TRACE, "msg2Buf returns");
} /* msg2buf */


/* 
 * Public Functions
 */

void
ethpkt_init(Protl self) 
{
  PState		*ps;
  struct ifreq		ifr;
  int			flags;


  xTraceP0(self, TR_GROSS_EVENTS, "init");
  ps = X_NEW(PState);
  self->state = ps;
				/* recvfrom and sendto are not MT safe
                                   so we use the following mutex for
                                   each SOCK_PACT. */
  mutexInit(&ps->mutex);


				/* Set defaults prior to parsing ROM
                                   file 				*/
  strcpy(ps->devname, ETHPKT_DEFDEVNAME);
  ps->remap = 0;
  ps->txmap = mapCreate(ETHPKT_TXMAPSZ, sizeof(unsigned short));
  ps->rxmap = mapCreate(ETHPKT_RXMAPSZ, sizeof(unsigned short));
  ps->blockmap = mapCreate(ETHPKT_BLOCKMAPSZ, sizeof(unsigned short));
  ps->allowmap = mapCreate(ETHPKT_ALLOWMAPSZ, sizeof(unsigned short));
  ps->filtermode = ETHPKT_BLOCK;
  ps->dumplen = INT_MAX;
  
				/* Parse ROM file 			*/
  findProtlRomOpts(self, ethpkt_opts,
		  (sizeof ethpkt_opts) / sizeof(ProtlRomOpt), NULL);  

  

  if ( (ps->sock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) == -1 ) {
    xError("cannot open packet level socket");
    exit(1);
  }
  xTraceP1(self, TR_FULL_TRACE, "opened packet socket %d", ps->sock);

				/* make socket non-blocking 	*/
  if ( (flags = fcntl(ps->sock, F_GETFL, 0)) < 0 ) {
    xError("cannot get socket flags");
    exit(1);
  }
  flags |= O_NONBLOCK;
  if ( fcntl(ps->sock, F_SETFL, flags) < 0 ) {
    xError("cannot set socket flags");
    exit(1);
  }

				/* defualt to non-promicuous mode 	*/
  if ( ethpkt_set_promiscuous(self, 0) == XK_FAILURE ) {
    xError("cannot turn off promiscuous mode");
    exit(1);
  }

				/* read the ethernet address 		*/
  strcpy(ifr.ifr_name, ps->devname);
  if ( ioctl(ps->sock, SIOCGIFHWADDR, &ifr) == -1 ) {
    xTraceP0(self, TR_ERRORS, "SIOCGIFHWADDR ioctl failed");
    exit(1);
  }
  memcpy(&ps->ethaddr, &ifr.ifr_hwaddr.sa_data, sizeof(ETHhost));
  xTraceP1(self, TR_GROSS_EVENTS, "interface address %s",
	   ethHostStr(&ps->ethaddr));

  
  ps->buffers = xkBufferPoolInit(ETHPKT_NUMTHREADS, ETHPKT_BUFSIZE,
				 ethpkt_internal_demux);


  self->openenable	= ethpkt_openenable;
  self->push		= ethpkt_push;
  self->controlprotl	= ethpkt_control;
  self->up 		= NULL;

} /* ethpkt_init */



/* 
 * Public Protocol Methods 
 */


static XkReturn
ethpkt_openenable(Protl self, Protl hlpRcv, Protl hlpType, Part *p)
{
  Thread child;
  struct ThreadOptions options;

  if ( self->up ) {
    xError("ethpkt_openenable called multiple times!");
    return XK_FAILURE;
  }
  
  self->up = hlpRcv;

				/* Start rxer thread 			*/
  options = *threadDefaultOptions;
  options.arg = (void *)self;

  child = threadCreate((ThreadFunc)ethpkt_rxthread, &options);
  if ( !child ) {
    xError("can not create rx thread");
    return XK_FAILURE;
  }
  threadWakeup(child);
  
  return XK_SUCCESS;
} /* ethpth_openable */


static int
ethpkt_control(Protl self, int opcode, char *buf, int buflen)
{
  PState	*ps = (PState *)self->state;

  switch ( opcode ) {

    case GETMYHOST:
      checkLen(buflen, sizeof(ETHhost));
      memcpy(buf, &ps->ethaddr, sizeof(ETHhost));
      return sizeof(ETHhost);

    case ETH_SETPROMISCUOUS:
      ethpkt_set_promiscuous(self, 1);
      return 0;

    default:
      return -1;
  }
} /* ethpkt_control */


static XkHandle
ethpkt_push(Sessn self, Msg *packet)
{
  PState		*ps 	= (PState *)self->state;
  ETHhdr		*header	= msgGetAttr(packet, 0);
  struct sockaddr	addr;
  char			buf[ETHPKT_BUFSIZE];
  int			pktlen;
  int			sentlen;
  unsigned short	*type;	/* ethernet packet type 		*/
  int                   sent = 0;


  if ( !msgGetAttr(packet, 0) ) {
    xTraceP0((Protl)self, TR_ERRORS, "push -- no header attached");
    return XMSG_ERR_HANDLE;
  }

  if ( (pktlen = msgLength(packet)) > ETHPKT_BUFSIZE ) {
    xTraceP1((Protl)self, TR_ERRORS, "push -- msgLength(%d) too big", 
             msgLength(packet));
    return XMSG_ERR_HANDLE;
  }

				/* Create a flat buffer with the
                                   complete ethernet packet */
  memcpy(buf, header, sizeof(ETHhdr));
  msg2buf(packet, &buf[sizeof(ETHhdr)]);
  pktlen += sizeof(ETHhdr);	/* add header to length 		*/
  if ( pktlen < ETH_ZLEN )	/* pad to minimum length (w/o FCS)	*/
    pktlen = ETH_ZLEN;

				/* Change the ethernet packet type so
                                   it is invisible to native tcp/ip
                                   layer. */
  if ( ps->remap ) {
    if ( mapResolve(ps->txmap, &((ETHhdr *)buf)->type, (void **) &type) 
         == XK_SUCCESS ) {
      xTraceP2((Protl)self, TR_EVENTS, "remapping ethertype %04x to %04x",
	       ((ETHhdr *)buf)->type, *type);
      ((ETHhdr *)buf)->type = *type;
    }
    else
      xTraceP1((Protl)self, TR_ERRORS, "cannot remap ethertype %04x",
	       ((ETHhdr *)buf)->type);
  }
    
				/* Get the raw device name and pass it
                                   the complete ethernet packet 	*/
  strcpy(addr.sa_data, ps->devname);

				/* Spin until sendto succeeds, or at least
                                   fails for reasons other than it would
                                   block.  Don't bother to sleep between
				   attempts. */
  while (!sent) {
				/* Protect MT UnSafe socket operation 	*/
    mutexLock(&ps->mutex);
    sentlen = sendto(ps->sock, buf, pktlen, 0, &addr, sizeof addr);
    mutexUnlock(&ps->mutex);

    if (sentlen == -1 && errno == EWOULDBLOCK) {
      Delay(10);  /* sleep for 10 millisec before re-trying */
    } else {
      sent = 1;
    }
  }

  if ( sentlen == -1 ) {
    xTraceP0((Protl)self, TR_ERRORS, "push: sendto failed");
    return XMSG_ERR_HANDLE;
  }

  xTraceP1((Protl)self, TR_MORE_EVENTS, "msg sent %d bytes", sentlen);

#ifdef XK_DEBUG
  xIfTraceP((Protl)self, TR_EVENTS) {
    ethpkt_dump(self, stdout, "TX", buf, sentlen);
  }
#endif  
				/* This could never happen since
                                   sendto should block... */
  if ( sentlen != pktlen ) {
    xError("ethpkt: incomplete datagram sent");
    xTraceP2((Protl)self, TR_ERRORS, "sent %d out of %d bytes",sentlen,pktlen);
    return XMSG_ERR_HANDLE;
  }


  return XMSG_NULL_HANDLE;
} /* ethpkt_push */



/* 
 * Private Protocol Methods 
 */

static XkReturn
ethpkt_set_promiscuous(Protl self, int on) 
{
  PState	*ps = (PState *)self->state;
  struct ifreq	ifr;

  strcpy(ifr.ifr_name, ps->devname);
  if ( ioctl(ps->sock, SIOCGIFFLAGS, &ifr) == -1 ) {
    xTraceP0(self, TR_ERRORS, "ioctl SIOCGIFFLAGS failed");
    return XK_FAILURE;
  }

  ps->promisc = on;
  if ( on )
    ifr.ifr_flags |= IFF_PROMISC;
  else
    ifr.ifr_flags &= ~IFF_PROMISC;

  xTraceP1(self, TR_MAJOR_EVENTS, "set promiscuous mode %s",
	   on ? "on" : "off");
  
  if ( ioctl(ps->sock, SIOCSIFFLAGS, &ifr) == -1 ) {
    xTraceP0(self, TR_ERRORS, "ioctl SIOCSIFFLAGS failed");
    return XK_FAILURE;
  }

  return XK_SUCCESS;
} /* ethpkt_set_promiscuous */



static void
ethpkt_internal_demux(InputBuffer *buffer)
{
  ETHhdr	header;
  Msg		*msg	= &buffer->msg;
  Sessn		self	= buffer->self;
  void		*hdr_buf;
#ifdef XK_DEBUG  
  char		buf[ETHPKT_BUFSIZE];
#endif
 
  hdr_buf = msgPop(msg, sizeof(ETHhdr));
  if ( !hdr_buf )  {
    xTraceP0((Protl)self, TR_SOFT_ERRORS,
	     "demux: incoming message too small!");
    msgDestroy(msg);
    return;
  }
  ethpkt_msgload(&header, hdr_buf, sizeof(ETHhdr));
  if ( !self->up ) {
    xTraceP0((Protl)self, TR_ERRORS, "demux: no upper protocol!");
    msgDestroy(msg);
    return;
  }

				/* dump packet header			*/
  xTraceP4((Protl)self, TR_MAJOR_EVENTS, "demux: %04x %s > %s %d bytes",
	   ntohs(header.type),
	   ethHostStr(&header.src), ethHostStr(&header.dst),
	   msgLength(msg));

  msgSetAttr(msg, 0, &header, sizeof(ETHhdr));

#ifdef XK_DEBUG
  xIfTraceP((Protl)self, TR_EVENTS) {
    msg2buf(msg, buf);
    ethpkt_dump(self, stdout, "RX", buf, msgLength(msg));
  }
#endif  
  
  xDemux(xGetUp(self), self, msg);	/* send to eth protocol	*/
} /* ethpkt_internal_demux */


static long
ethpkt_msgload(void *header, char *buf, long buflen)
{
  xAssert(buflen == sizeof(ETHhdr));
  memcpy(header, buf, sizeof(ETHhdr));
  return sizeof(ETHhdr);
} /* ethpkt_msgload */


#ifdef XK_DEBUG
static void
ethpkt_dump(Sessn self, FILE *fout, const char *label,
	    unsigned char *pkt, int len)
{
  PState	*ps = (PState *)self->state;
  int		i, j;

  xAssert(fout);
  xAssert(pkt);
  xAssert(label);
  xAssert(len >= 0);

	  /* Dumping is in the debug hex/ascii style and is setup for
	     80 columns.  This puts 16 bytes per line. */

  len = min(len, ps->dumplen);
	    
  for (i=0; i<len; i+=16) {
    char	buffer[80];
    char	*p = &buffer[0];
    char	*q = &buffer[52];
    
    memset(buffer, (int)' ', sizeof buffer);
    
    for (j=0; j<16 && (j+i) < len; j++) {
      unsigned char	byte = pkt[i+j];

      if ( j == 8 )
	p += sprintf(p, "- ");
      
      p += sprintf(p, "%02x ", byte);
      
      if ( isprint(byte) )
	*q++ = byte;
      else
	*q++ = '.';
    }

    *p = ' ';
    *q = '\0';
    xTraceP2((Protl)self, TR_GROSS_EVENTS, "%s: %s", label, buffer);
  }
} /* ethpkt_dump */
#endif


/* 
 * Private ROM file methods
 */


static XkReturn 
ethpkt_rom_remap(Protl self, char **argv, int argc, int line, void *arg) 
{
  PState	*ps = (PState *)self->state;
  unsigned	realid;
  unsigned	fakeid;

  if ( sscanf(argv[2], "%x", &realid) == 1 &&
       sscanf(argv[3], "%x", &fakeid) == 1 )
  {
    unsigned short *rid	= (unsigned short *)xMalloc(sizeof(unsigned short));
    unsigned short *fid	= (unsigned short *)xMalloc(sizeof(unsigned short));

    *rid = (unsigned short)htons(realid);
    *fid = (unsigned short)htons(fakeid);
    
    mapBind(ps->txmap, rid, fid);
    mapBind(ps->rxmap, fid, rid);
    ps->remap++;		/* enable remaping 			*/
    xTraceP2(self, TR_MAJOR_EVENTS,
	     "ethertype %04x remapped to %04x", realid, fakeid);
    return XK_SUCCESS;
  }
  return XK_FAILURE;
} /* ethpkt_read_remap */


static XkReturn 
ethpkt_rom_block(Protl self, char **argv, int argc, int line, void *arg) 
{
  PState	*ps = (PState *)self->state;
  unsigned	id;
  int		i;

  for ( i=2; i<argc; i++) {
    if ( sscanf(argv[i], "%x", &id) == 1 ) {
      unsigned short rid	= htons(id);
    
      mapBind(ps->blockmap, &rid, NULL);
      xTraceP1(self, TR_MAJOR_EVENTS,
	       "ethertype %04x blocked", id);
      ps->filtermode = ETHPKT_BLOCK;
    }
    else
      return XK_FAILURE;
  }
  return XK_SUCCESS;
} /* ethpkt_rom_block */


static XkReturn 
ethpkt_rom_allow(Protl self, char **argv, int argc, int line, void *arg) 
{
  PState	*ps = (PState *)self->state;
  unsigned	id;
  int		i;

  for ( i=2; i<argc; i++) {
    if ( sscanf(argv[i], "%x", &id) == 1 ) {
      unsigned short rid	= htons(id);
    
      mapBind(ps->allowmap, &rid, NULL);
      xTraceP1(self, TR_MAJOR_EVENTS,
	       "ethertype %04x allowed", id);
      ps->filtermode = ETHPKT_ALLOW;
    }
    else
      return XK_FAILURE;
  }
  return XK_SUCCESS;
} /* ethpkt_rom_allow */


static XkReturn 
ethpkt_rom_device(Protl self, char **argv, int argc, int line, void *arg)
{
  PState	 *ps = (PState *)self->state;

  if ( argc == 3 ) {
    strncpy(ps->devname, argv[2], min(strlen(*argv),IFNAMSIZ));
    xTraceP1(self, TR_MAJOR_EVENTS, "etherdevice is %s", ps->devname);
    return XK_SUCCESS;
  }
  return XK_FAILURE;
} /* ethpkt_rom_device */


static XkReturn
ethpkt_rom_dump(Protl self, char **argv, int argc, int line, void *arg)
{
  PState	 *ps = (PState *)self->state;

  if ( argc == 3 && sscanf(argv[2], "%d", &ps->dumplen) == 1 ) {
    xTraceP1(self, TR_MAJOR_EVENTS, "dumplen is %d", ps->dumplen);
    return XK_SUCCESS;
  }
  return XK_FAILURE;
} /* ethpkt_rom_dump */




/* 
 * Private non-xKernel driver methods
 */

				/* The following routines are called
                                   outside of xKernel control so
                                   careful use of xk_master_lock() and
                                   xk_master_unlock() is required. */

static void *
ethpkt_rxthread(void *arg)
{
  Sessn			self    = (Sessn)arg;
  PState		*ps	= (PState *)self->state;

  xk_master_lock();		/* Lock xKernel				*/

  while ( 1 ) {  /* loop "forever" */
    InputBuffer		*buffer = ethpkt_getpkt(self);

    if ( buffer ) {
      ETHhost		dstaddr = ((ETHhdr *)buffer->data)->dst;
      unsigned short	type	= ((ETHhdr *)buffer->data)->type;
      unsigned short	*ntype	= NULL;
      
      buffer->self = self;

				/* Force non-promiscuous mode if set	*/
      if ( !ps->promisc &&
	   !ETH_ADS_EQUAL(dstaddr, ps->ethaddr) &&
	   !ETH_ADS_EQUAL(dstaddr, bcastaddr) &&
	   !ETH_ADS_MCAST(dstaddr) )
      {
	xkBufferPoolReleaseBlock(buffer);
      }
      else {
				/* is this type blocked? 		*/
	if ( ps->filtermode == ETHPKT_BLOCK &&
	     mapResolve(ps->blockmap, &type, NULL) != XK_FAILURE )
	{
	  xTraceP1((Protl)self, TR_EVENTS,
		   "ethertype %04x blocked by type filter",
		   ntohs(type));
	  xkBufferPoolReleaseBlock(buffer);
	}
				/* is this type allowed? 		*/
	else if ( ps->filtermode == ETHPKT_ALLOW &&
		  mapResolve(ps->allowmap, &type, NULL) == XK_FAILURE )
	{
	  xTraceP1((Protl)self, TR_EVENTS,
		   "ethertype %04x disallowed by type filter",
		   ntohs(type));
	  xkBufferPoolReleaseBlock(buffer);
	}
				/* process the packet	 		*/
	else {
				/* Try to remap the ethertype 		*/
	  if ( mapResolve(ps->rxmap, &type, (void **) &ntype) == XK_SUCCESS ) {
	    ((ETHhdr *)buffer->data)->type = *ntype;
	  }
	
	  xTraceP1((Protl)self, TR_FULL_TRACE, "sem count (pre signal) == %d",
		   buffer->sem.count);

	  semSignal(&buffer->sem);
	  xk_master_unlock();	/* UnLock xKernel 			*/
	  xk_master_lock();	/* Lock xKernel 			*/
	}
      }
    }
  } /* while */
  xk_master_unlock();		/* UnLock xKernel 			*/
      
  return NULL;
} /* ethpkt_rxthread */


static InputBuffer *
ethpkt_getpkt(Sessn self) 
{
  PState		*ps	= (PState *)self->state;
  InputBuffer		*buffer;
  struct sockaddr	addr;
  int                   addrlen = sizeof addr;
  int			buflen;
  int                   packet = 0;
  char			bitbucket[ETHPKT_BUFSIZE];
  char			*buf;	

	  /* Because this is a POSIX thread make sure we aquire the
	     master lock before sharing operations with the xkernel
	     events.

	     This includes all socket io since each ETHPKT instance
	     used a mutex to protect the MT UnSafe sendto/recvfrom
	     operations.

	     This must be called with the master lock acquired. */

  if (buffer = xkBufferPoolNextBlock(ps->buffers) ) {
    buf = buffer->data;
  } else {
    buf = bitbucket;

    xError("ethpkt ERROR: Can't get buffer, dropping incoming packet");
    xIfTraceP((Protl)self, TR_SOFT_ERRORS) { 
        xkBufferPoolDump(ps->buffers);
    }
  }

  while (!packet) {
    mutexLock(&ps->mutex);
    xk_master_unlock();		/* UnLock xKernel 			*/
    buflen = recvfrom(ps->sock, buf, ETHPKT_BUFSIZE, 0, &addr, &addrlen);
    mutexUnlock(&ps->mutex);
    xk_master_lock();		/* Lock xKernel 			*/

    if (buflen == -1 && errno == EWOULDBLOCK) {
      Delay(10);  /* sleep for 10 millisec before re-trying */
    } else {
      packet = 1;
    }
  }

  if (buf != bitbucket) {
				/* If the read failed or the data was
                                   not from the our device release the
                                   buffer pack to the pool. */
    if ( buflen <= 0 || strcmp(addr.sa_data, ps->devname) ) {
      xkBufferPoolReleaseBlock(buffer);
      buffer = NULL;		/* no data 				*/
    }
    else {
      msgTruncate(&buffer->msg, buflen);
    }
  }
  
  return buffer;		/* return complete ethernet packet 	*/
} /* ethpkt_getpkt */



/* ethpkt.c ends here */
 
