/*
 * $RCSfile: mac_common.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993 Massachusetts Board of Regents
 *
 * $Log: mac_common.c,v $
 * Revision 1.2  1996/01/30 20:48:51  slm
 * Updated version.
 *
 * Revision 1.1  1995/07/29  02:37:36  slm
 * Initial revision
 *
 * Revision 1.5.1.2.1.1  1994/11/12  19:07:21  hkaram
 * New branch
 *
 * Revision 1.5.1.2  1994/06/03  18:02:51  menze
 * GENERIChost replaced by MAC48bithost
 *
 * Revision 1.5.1.1  1994/03/14  20:44:24  menze
 *   [ 1994/03/14          umass ]
 *   Interface name changes
 *
 * Revision 1.5  1994/02/05  00:34:22  menze
 *   [ 1994/02/03          menze ]
 *   HalfWord replaced by xk_u_int16
 *
 * Revision 1.4  1993/11/13  00:45:22  menze
 * Original version from UMass
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/fddi.h>
#include <net/if.h>
#include <net/raw.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include "x_stdio.h"
#include "xkernel.h"
#include "platform.h"   /* For queue and process routines */
#include "modeldep.h"
#include "llc802_2_host.h"
#include "mac_common.h"

struct interface
{
    char *name;
    interfaceType type;
};

union hdrtype
{
    struct fddi fddi;
    struct ether_header eth;
};

/*
 *     MAC addresses we know and love here at cs.umass.edu
 *
 *     08:00:69:04:01:64 fddi-predator
 *     08:00:69:04:05:9D fddi-rawdeal
 *     08:00:69:04:06:9e fddi-redheat
 *     08:00:69:04:06:c1 fddi-commando
 *     08:00:69:02:14:E6 predator
 *     08:00:69:06:61:A9 rawdeal   
 *     08:00:69:07:23:b3 redheat
 *     08:00:69:06:4B:59 commando
 */
#ifdef FDDI
/* fddi-rawdeal */
unsigned char src_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x04, 0x05, 0x9D };
#if FALSE /* or TRUE */
/* fddi-predator */
unsigned char rly_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x04, 0x01, 0x64 };
#else
/* fddi-commando */
unsigned char rly_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x04, 0x06, 0xC1 };
#endif
/* fddi-redheat */
unsigned char dst_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x04, 0x06, 0x9E };
#endif
#ifdef ETHER
/* rawdeal */
unsigned char src_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x06, 0x61, 0xA9 };
#if FALSE /* or TRUE */
/* predator */
unsigned char rly_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x02, 0x14, 0xE6 };
#else
/* commando */
unsigned char rly_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x06, 0x4B, 0x59 };
#endif
/* redheat */
unsigned char dst_mac_address[MAC_ADDRESS_SIZE] 
               = { 8, 0, 0x69, 0x07, 0x23, 0xB3 };
#endif

int                 tracemaccommon;

PRIVATE MAC48bithost BroadcastHost = BCAST_MAC48BIT_ADDRESS;
PRIVATE struct interface interface[7] =
{
  /* Indigo FDDI */
  SGI_FDDI_GIO32, INDIGO_FDDI_INTERFACE,
  /* Indigo Ethernet */
  SGI_IEEE8023_ECLIPSE, INDIGO_ETHERNET_INTERFACE,
  /* Power series 4D/4x0 FDDI */
  SGI_FDDI_VME, POWER_SERIES_FDDI_INTERFACE,
  /* Power series 4D/4x0 Ethernet */
  SGI_IEEE8023_POWER4D, POWER_SERIES_ETHERNET_INTERFACE,
  /* Challenge FDDI */
  SGI_FDDI_VME, CHALLENGE_FDDI_INTERFACE,
  /* Challenge Ethernet */
  SGI_IEEE8023_CHALLENGE, CHALLENGE_ETHERNET_INTERFACE,
  /* the null interface */
  NULL, NULL_INTERFACE
};

PRIVATE int       interfaceNameToIndex _ANSI_ARGS_(( char * ));
PRIVATE PROCEDURE computeTypeField _ANSI_ARGS_(( ETHtype, ETHtype,
                                                 ETHtype *, ETHtype * ));

int 
beRoot( noargs )
{
  int return_value;
  
  return_value = (int)getuid();
  if (( setruid( 0 )) != 0)
  {
      perror((char *)0);
      Kabort("beRoot -- can't become root");
  }
  return( return_value );
}

PROCEDURE 
beMeAgain( userId )
  int userId;
{
  if (( setruid( userId )) != 0)
  {
      perror((char *)0);
      Kabort("beMeAgain -- can't set user ID");
  }
}

PRIVATE PROCEDURE
computeTypeField( ETHtype minEthernetType, ETHtype maxEthernetType,
                  ETHtype *typeFieldMask, ETHtype *typeFieldValue )

{
    unsigned long min = minEthernetType;
    unsigned long max = maxEthernetType;
    unsigned long maskHi = 0xFFFF0000;
    unsigned long maskLo = 0x0000FFFF;
    unsigned long maskShifted;
    unsigned int shift, i;

    i = 1;
    while( i <= 16 )
    {
      maskShifted = (maskHi >> i);
      if( (min & maskShifted) != (max & maskShifted) )
      {
        break;
      }
      else
      {
        i++;
      }
    }

    shift = i - 1;
    *typeFieldMask = (maskHi >> shift) & maskLo;
    *typeFieldValue = (min & *typeFieldMask);
}

PRIVATE int 
interfaceNameToIndex( theName )

    char *theName;
{
    int index;
    int i;

    index = BAD_INTERFACE_INDEX;

    i = 0;
    while( interface[i].name != NULL )
    {
      if( strcmp( interface[i].name, theName) == 0 )
      {
        index = i;
        break;
      }
      i++;
    }

    return( index );
}

int  openrecv_rs( 
     char *interfaceName,         /* device interface name */
     int protocol,                /* RAWPROTO_SNOOP or RAWPROTO_DRAIN */
     int numEthernetTypes,        /* number of Ethernet types */
     ETHtype ethernetType[],      /* ... to capture off the network */
     xk_u_int16 socket_port,        /* local socket port number */
     MAC48bithost *mac_address )   /* ... ours */
{
     int maxEthernetTypes = 1000  /* was ... SNOOP_MAXFILTERS / 2 */;
     ETHtype minEthernetType, maxEthernetType;
     bool second_filter_ok;
     int s;                       /* socket number */
     interfaceType type;
     struct sockaddr_raw raw_if;
     struct snoopfilter snp0;
     struct snoopfilter snp1;
     union hdrtype *headerPtr;
     struct drainmap map;
     int i;

     /*
      * Verify interface name and number of Ethernet types
      */
     i = interfaceNameToIndex( interfaceName );
     if( i != BAD_INTERFACE_INDEX )
     {
         type = interface[i].type;
     }
     else
     {
         xTrace1(maccommon, TR_ERRORS,
                 "bad interface name (%s)", interfaceName);
         Kabort("openrecv_rs: out of here");
     }
     if( (numEthernetTypes < 1) ||
         (numEthernetTypes > maxEthernetTypes) )
     {
         xTrace1(maccommon, TR_ERRORS,
                 "too few or too many Ethernet types (%d)", numEthernetTypes);
         Kabort("openrecv_rs: out of here");
     }
     /*
      * Compute minimum and maximum Ethernet types
      */
     minEthernetType = maxEthernetType = ethernetType[0];
     for( i = 1; i < numEthernetTypes; i++ )
     {
       if( ethernetType[i] < minEthernetType )
       {
         minEthernetType = ethernetType[i];
       }
       else if( ethernetType[i] > maxEthernetType )
       {
         maxEthernetType = ethernetType[i];
       }
     }
     if( (minEthernetType < MIN_FREE_ETHERNET_TYPE) )
     {
       xTrace2(maccommon, TR_GROSS_EVENTS,
	       "WARNING: might be using an assigned Ethernet type %d (%XH)",
	       minEthernetType, minEthernetType);
     }
     if( (maxEthernetType > MAX_FREE_ETHERNET_TYPE) )
     {
       xTrace2(maccommon, TR_GROSS_EVENTS,
	      "WARNING: might be using an assigned Ethernet type %d (%XH)",
	      maxEthernetType, maxEthernetType );
     }

     /*
      * Open the raw socket
      */
     if( (s = socket( PF_RAW, SOCK_RAW, protocol )) == -1 )
     {
	 perror((char *)0);
         xTrace0(maccommon, TR_ERRORS, "can't open raw socket to interface");
         Kabort("openrecv_rs: out of here");
     }

     raw_if.sr_family = AF_RAW;
     strncpy(raw_if.sr_ifname,interfaceName,sizeof raw_if.sr_ifname);
     if( protocol == RAWPROTO_SNOOP )
     {
         if( (socket_port < SNOOP_MINPORT) ||
             (socket_port > SNOOP_MAXPORT) )
         {
           xTrace2(maccommon, TR_ERRORS,
                   "snoop port must be in the range [%d,%d]",
                   SNOOP_MINPORT, SNOOP_MAXPORT );
           close( s );
           Kabort("openrecv_rs: out of here");
         }
         raw_if.sr_port = socket_port;
     }
     else if( protocol == RAWPROTO_DRAIN )
     {
         /*
          * Use this with port mapping ...
          */
         raw_if.sr_port = socket_port;
         /*
          * We do this if we only want to receive one Ethernet type ...
          *
          * raw_if.sr_port = ethernetType;
          */
     }
     else
     {
         xTrace1(maccommon, TR_ERRORS,
                 "bad protocol argument (%d)", protocol );
         close( s );
         Kabort("openrecv_rs: out of here");
     }
     if( bind(s,&raw_if,sizeof raw_if) < 0 ) 
     {
         perror( (char *)NULL );
         xTrace0(maccommon, TR_ERRORS, "couldn't bind the raw socket");
         close( s );
         Kabort("openrecv_rs: out of here");
     }

     if( protocol == RAWPROTO_SNOOP )
     {
       int on;
       int filter;
       ETHtype typeFieldMask, typeFieldValue;

       computeTypeField( minEthernetType, maxEthernetType,
                         &typeFieldMask, &typeFieldValue );
       xTrace2(maccommon, TR_MAJOR_EVENTS, 
               "Ethernet type (mask,value) = (%X,%X)",
               typeFieldMask, typeFieldValue);
 
       /* Zero out filters first */
       bzero( (char *)&snp0, sizeof snp0 );
       bzero( (char *)&snp1, sizeof snp1 );

       if( (type == POWER_SERIES_FDDI_INTERFACE) ||
           (type == CHALLENGE_FDDI_INTERFACE) )
       {
         /*
          * Filter 0 - our own address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_mask, struct fddi );
         memset( (void *)headerPtr->fddi.fddi_mac.mac_da.b, (int)0x0FF, 
                 MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_match, struct fddi );
         bcopy( (void *)mac_address, 
                (void *)headerPtr->fddi.fddi_mac.mac_da.b, MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldValue);
         snp0.sf_port = socket_port;
         /*
          * Filter 1 - the broadcast address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_mask, struct fddi );
         memset( (void *)headerPtr->fddi.fddi_mac.mac_da.b, (int)0x0FF,
                 MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_match, struct fddi );
         bcopy( (void *)&BroadcastHost, 
                (void *)headerPtr->fddi.fddi_mac.mac_da.b, MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldValue);
         snp1.sf_port = socket_port;
       }
       else if( type == INDIGO_FDDI_INTERFACE )
       {
#ifdef   WORKS_FDDI_301
         /*
          * Filter 0 - just check the Ethernet type.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_mask, struct fddi );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_match, struct fddi );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldValue);
         snp0.sf_port = socket_port;
#else    /* Doesn't WORK, but should */
         /*
          * Filter 0 - our own address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_mask, struct fddi );
         memset( (void *)headerPtr->fddi.fddi_mac.mac_da.b, (int)0x0FF, 
                 MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_match, struct fddi );
         bcopy( (void *)mac_address, 
                (void *)headerPtr->fddi.fddi_mac.mac_da.b, MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldValue);
         snp0.sf_port = socket_port;
         /*
          * Filter 1 - the broadcast address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_mask, struct fddi );
         memset( (void *)headerPtr->fddi.fddi_mac.mac_da.b, (int)0x0FF,
                 MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_match, struct fddi );
         bcopy( (void *)&BroadcastHost, 
                (void *)headerPtr->fddi.fddi_mac.mac_da.b, MAC_ADDRESS_SIZE );
         headerPtr->fddi.fddi_llc.ullc.c.etype = htons(typeFieldValue);
         snp1.sf_port = socket_port;
#endif
       }
       else
       {
         /*
          * type must be an Ethernet interface ...
          * Filter 0 - our own address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_mask, 
                                               struct ether_header );
         memset( (void *)headerPtr->eth.ether_dhost, (int)0x0FF, 
                 MAC_ADDRESS_SIZE );
         headerPtr->eth.ether_type = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp0.sf_match, 
                                               struct ether_header );
         bcopy( (void *)mac_address, 
                (void *)headerPtr->eth.ether_dhost, MAC_ADDRESS_SIZE );
         headerPtr->eth.ether_type = htons(typeFieldValue);
         snp0.sf_port = socket_port;
         /*
          * Filter 1 - the broadcast address.
          */
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_mask, 
                                               struct ether_header );
         memset( (void *)headerPtr->eth.ether_dhost, (int)0x0FF, 
                 MAC_ADDRESS_SIZE );
         headerPtr->eth.ether_type = htons(typeFieldMask);
         headerPtr = (union hdrtype *)RAW_HDR( snp1.sf_match, 
                                               struct ether_header );
         bcopy( (void *)&BroadcastHost, 
                (void *)headerPtr->eth.ether_dhost, MAC_ADDRESS_SIZE );
         headerPtr->eth.ether_type = htons(typeFieldValue);
         snp1.sf_port = socket_port;
       }
       /*
        * Install the filters
        */  
       if (ioctl(s,SIOCADDSNOOP,&snp0) != 0 )
       {
         perror((char *)0);
         xTrace1(maccommon, TR_ERRORS,
                 "ioctl (%s): snp0 could not go promiscuous", interfaceName);
    	 close(s);
         Kabort("openrecv_rs: out of here");
       }
#ifdef WORKS_FDDI_301
       second_filter_ok = (type != INDIGO_FDDI_INTERFACE);
#else  /* Doesn't WORK, but should */
       second_filter_ok = TRUE;
#endif
       if( second_filter_ok )
       {
         if (ioctl(s,SIOCADDSNOOP,&snp1) != 0 )
         {
           perror((char *)0);
           filter = 0;
           ioctl( s, SIOCDELSNOOP, &filter );   /* delete last filter */
           xTrace1(maccommon, TR_ERRORS,
    	           "ioctl (%s): snp1 could not go promiscuous", interfaceName);
    	   close(s);
           Kabort("openrecv_rs: out of here");
         }
       }
       /*
        * Enable snooping
        */
       on = 1;
       if( ioctl(s,SIOCSNOOPING,&on) < 0 )
       {
         perror((char *)0);
         xTrace0(maccommon, TR_ERRORS,
                 "can't go promiscuous turning on snoop");
         closerecv_rs( s, protocol, interfaceName );
         Kabort("openrecv_rs: out of here");
       }

       xTrace1(maccommon, TR_GROSS_EVENTS,
         "Interface %s configured to snoop packets with Ethernet types:",
         interfaceName);
       xTrace4(maccommon, TR_GROSS_EVENTS, "  %d (%XH) through %d (%XH)", 
               typeFieldValue, typeFieldValue,
               (typeFieldValue|((ETHtype)~typeFieldMask)), 
               (typeFieldValue|((ETHtype)~typeFieldMask)));
     }
     else
     {
       map.dm_minport = minEthernetType;
       map.dm_maxport = maxEthernetType;
       map.dm_toport = socket_port;
       if( ioctl( s, SIOCDRAINMAP, &map ) < 0 )
       {
         perror( (char *)NULL );
         xTrace3(maccommon, TR_ERRORS,
                 "can't map Ethernet types [%d,%d] to port %d",
                 minEthernetType, maxEthernetType, socket_port );
         closerecv_rs( s, protocol, interfaceName );
         Kabort("openrecv_rs: out of here");
       }
       xTrace1(maccommon, TR_GROSS_EVENTS,
         "Interface %s configured to drain packets with Ethernet types:",
         interfaceName );
       xTrace4(maccommon, TR_GROSS_EVENTS, "  %d (%XH) through %d (%XH)", 
               minEthernetType, minEthernetType, 
               maxEthernetType, maxEthernetType );
     }
     return (int)s;
}

int  opensend_rs( 
     char *interfaceName,         /* device interface name */
     int protocol,                /* RAWPROTO_SNOOP or RAWPROTO_DRAIN */
     xk_u_int16 socket_port )       /* local socket port number */
{
     int s;
     struct sockaddr_raw raw_if;

     if( interfaceNameToIndex( interfaceName ) == BAD_INTERFACE_INDEX )
     {
         xTrace1(maccommon, TR_ERRORS,
                 "bad interface name (%s)", interfaceName);
         Kabort("opensend_rs: out of here");
     }

     /*
      * Open the raw socket
      */
     if( (s = socket( PF_RAW, SOCK_RAW, protocol )) == -1 )
     {
	 perror((char *)0);
	 xTrace0(maccommon, TR_ERRORS, "can't open raw socket to interface");
         Kabort("opensend_rs: out of here");
     }

     raw_if.sr_family = AF_RAW;
     strncpy(raw_if.sr_ifname,interfaceName,sizeof raw_if.sr_ifname);
     if( protocol == RAWPROTO_SNOOP )
     {
         if( (socket_port < SNOOP_MINPORT) ||
             (socket_port > SNOOP_MAXPORT) )
         {
           xTrace2(maccommon, TR_ERRORS,
                   "snoop port must be in the range [%d,%d]",
                   SNOOP_MINPORT, SNOOP_MAXPORT );
           close( s );
           Kabort("opensend_rs: out of here");
         }
         raw_if.sr_port = socket_port;
     }
     else if( (protocol == RAWPROTO_DRAIN) || (protocol == RAWPROTO_RAW) )
     {
         raw_if.sr_port = socket_port;
     }
     else
     {
         xTrace1(maccommon, TR_ERRORS,
                 "bad protocol argument (%d)", protocol );
         close( s );
         Kabort("opensend_rs: out of here");
     }
     if( bind(s,&raw_if,sizeof raw_if) < 0 ) 
     {
         perror( (char *)NULL );
         xTrace0(maccommon, TR_ERRORS, "couldn't bind the raw socket");
         close( s );
         Kabort("opensend_rs: out of here");
     }

     if( protocol == RAWPROTO_SNOOP )
     {
       xTrace1(maccommon, TR_GROSS_EVENTS,
               "Snoop interface %s configured for sending", interfaceName);
     }
     else if( protocol == RAWPROTO_DRAIN )
     { 
       xTrace1(maccommon, TR_GROSS_EVENTS,
               "Drain interface %s configured for sending", interfaceName);
     }
     else
     { 
       xTrace1(maccommon, TR_GROSS_EVENTS,
               "Raw interface %s configured for sending", interfaceName);
     }
     return (int)s;
}

PROCEDURE closerecv_rs( raw_if_socket, protocol, interfaceName )

   int raw_if_socket;      /* socket number */
   int protocol;           /* RAWPROTO_SNOOP or RAWPROTO_DRAIN */
   char *interfaceName;    /* device interface name */
{
   int numFilters;
   int filter;
   interfaceType type;
   int i;
   int off = 0;

   if( protocol == RAWPROTO_SNOOP )
   {
       /*
        * Verify interface name and number of Ethernet types
        */
       i = interfaceNameToIndex( interfaceName );
       if( i == BAD_INTERFACE_INDEX )
       {
           numFilters = 0;
       }
       else
       {
         type = interface[i].type;
         if( type == INDIGO_FDDI_INTERFACE )
         {
#ifdef WORKS_FDDI_301
             numFilters = 1;
#else
             numFilters = 2;
#endif
         }
         else
         {
             numFilters = 2;
         }
       }
       /* 
        * Disable snooping
        */
       ioctl( raw_if_socket, SIOCSNOOPING, &off );
       /*
        * Remove the filter masks
        */
       for( filter = 0; filter < numFilters; filter++ )
       {
         ioctl( raw_if_socket, SIOCDELSNOOP, &filter );
       }
   }
   else if( protocol != RAWPROTO_DRAIN )
   {
       xTrace1(maccommon, TR_ERRORS,
               "closerecv_rs: bad protocol argument (%d)", protocol );
   }
   close( raw_if_socket );
}

PROCEDURE closesend_rs( raw_if_socket, protocol )

    int raw_if_socket;   /* socket number */
    int protocol;        /* RAWPROTO_SNOOP or RAWPROTO_DRAIN */
{
    close( raw_if_socket );
}

PROCEDURE size_ifbufs( raw_if_socket, send_buff_size, recv_buff_size )

    int raw_if_socket;
    int send_buff_size, recv_buff_size;
{    
    int tx = send_buff_size;
    int rx = recv_buff_size;

    if (setsockopt(raw_if_socket, SOL_SOCKET, SO_SNDBUF, (char *)&tx,
		   sizeof(tx)) != 0) {
        perror("size_ifbufs(): ");
	xTrace1(maccommon, TR_ERRORS,
		"Could not set size of send buffer to %d", tx);
    }
    if (setsockopt(raw_if_socket, SOL_SOCKET, SO_RCVBUF, (char *)&rx,
		   sizeof(rx)) != 0) {
        perror("size_ifbufs(): ");
	xTrace1(maccommon, TR_ERRORS,
		"Could not set size of receive buffer to %d", rx);
    }
}
