/*
    ettercap -- inet utilities -- Module for SunOS (solaris)

    Copyright (C) 2001  ALoR <alor@users.sourceforge.net>, NaGA <crwm@freemail.it>

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

    $Id: ec_inet_solaris.c,v 1.28 2002/05/13 20:57:46 alor Exp $
*/

#include "../../include/ec_main.h"

#include "../../include/ec_thread.h"
#include "../../include/ec_buffer.h"
#include "../../include/ec_inet.h"
#include "../../include/ec_inet_forge.h"
#include "../../include/ec_inet_structures.h"

#include <ctype.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <sys/sockio.h>

#include <sys/types.h>
#include <sys/time.h>
#ifdef HAVE_SYS_BUFMOD_H
#include <sys/bufmod.h>
#endif
#include <sys/dlpi.h>
#ifdef HAVE_SYS_DLPI_EXT_H
#include <sys/dlpi_ext.h>
#endif

#include <sys/stream.h>
#ifdef HAVE_SYS_BUFMOD_H
#include <sys/systeminfo.h>
#endif

#include <sys/stropts.h>
#include <inet/nd.h>
#include <inet/common.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <unistd.h>

struct ether_addr
{
   u_char  ether_addr_octet[6];
};

#ifndef DLPI_DEV_PREFIX
#define DLPI_DEV_PREFIX "/dev"
#endif

#define  MAXDLBUF 8192

char IpForward_status[2];      // old ipforward status

static u_int ctlbuf[MAXDLBUF];
static struct strbuf ctl = {
   MAXDLBUF,
   0,
   (char *)ctlbuf
};
char ebuf[100];

int bufsize, offset;
int SocketBuffer = -1;
int dlpi_in_use;

static char ETH_BROADCAST[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
static char ARP_BROADCAST[6] = {0x0,0x0,0x0,0x0,0x0,0x0};

// protos...

int Inet_FindIFace(char *iface);
int Inet_CorrectIface(char *iface);
int Inet_GetIfaceInfo(char *iface, int *MTU, char *MyMAC, u_long *IP, u_long *NetMask);
int Inet_SetPromisc(char *iface);
int Inet_OpenRawSock(char *iface);
void Inet_CloseRawSock(int sock);
int Inet_GetRawPacket(int sock, char *buffer, int MTU, short *type);
int Inet_SendRawPacket(int sock, char *buffer, int len);
void Inet_SetNonBlock(int sock);
void Inet_Restore_ifr(void);
void Inet_DisableForwarding(void);
void Inet_RestoreForwarding(void);
char *Inet_MacFromIP(unsigned long ip);
/* protos... */
static int dlattachreq(int, u_int, char *);
static int dlbindack(int, char *, char *);
static int dlbindreq(int, u_int, char *);
static int dlinfoack(int, char *, char *);
static int dlinforeq(int, char *);
static int dlokack(int, const char *, char *, char *);
static int send_request(int, char *, int, char *, char *);
static int recv_ack(int, int, const char *, char *, char *);
static int dlpromisconreq(int, u_int, char *);
#if defined(SOLARIS) && defined(HAVE_SYS_BUFMOD_H)
static char *get_release(u_int *, u_int *, u_int *);
#endif
#ifdef HAVE_SYS_BUFMOD_H
static int strioctl(int, int, int, char *);
#endif
#ifdef HAVE_DEV_DLPI
static int get_dlpi_ppa(int, const char *, int, char *);
#endif
static char * split_dname(char *device, int *unitp);

// ==============================================

int Inet_FindIFace(char *iface)     // adapded from pcap_lookupdev
{
   int fd, minunit, n;
   char *cp;
   struct ifreq *ifrp, *ifend, *ifnext, *mp;
   struct ifconf ifc;
   char *buf;
   struct ifreq ifr;
   unsigned int buf_size;

   fd = socket(AF_INET, SOCK_DGRAM, 0);
   if (fd < 0)
      ERROR_MSG("socket()");

   buf_size = 8192;

   for (;;) {
      buf = malloc (buf_size);
      if (buf == NULL)
         ERROR_MSG("malloc()");

      ifc.ifc_len = buf_size;
      ifc.ifc_buf = buf;
      memset (buf, 0, buf_size);
      if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 && errno != EINVAL)
         ERROR_MSG("ioctl(SIOCGIFCONF)");

      if (ifc.ifc_len < buf_size)
         break;

      free (buf);
      buf_size *= 2;
   }

   ifrp = (struct ifreq *)buf;
   ifend = (struct ifreq *)(buf + ifc.ifc_len);

   mp = NULL;
   minunit = 666;
   for (; ifrp < ifend; ifrp = ifnext)
   {
      const char *endcp;

#ifdef HAVE_SOCKADDR_SA_LEN
   n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
   if (n < sizeof(*ifrp))
      ifnext = ifrp + 1;
   else
      ifnext = (struct ifreq *)((char *)ifrp + n);
   if (ifrp->ifr_addr.sa_family != AF_INET)
      continue;
#else
   ifnext = ifrp + 1;
#endif

      strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));

      if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0)
      {
         if (errno == ENXIO)
            continue;
         ERROR_MSG("ioctl(SIOCGIFFLAGS)");
      }

      DEBUG_MSG("Inet_FindIFace -- check for [%s]", ifr.ifr_name);

      /* Must be up and not the loopback */
      if ((ifr.ifr_flags & IFF_UP) == 0 || (ifr.ifr_flags & IFF_LOOPBACK) != 0)
         continue;

      endcp = ifrp->ifr_name + strlen(ifrp->ifr_name);
      for (cp = ifrp->ifr_name; cp < endcp && !isdigit((int)*cp); ++cp)
         continue;

      if (isdigit ((int)*cp)) {
         n = atoi(cp);
      } else {
         n = 0;
      }
      if (n < minunit) {
         minunit = n;
         mp = ifrp;
      }
   }

   close(fd);

   if (mp == NULL)   // no device found
   {
      free(buf);
      return -1;
   }

   strlcpy(iface, mp->ifr_name, sizeof(Options.netiface));

   free(buf);

   DEBUG_MSG("Inet_FindIFace -- %s found !!", iface);

   return 0;

}


int Inet_CorrectIface(char *iface)
{
   int sock;
   struct ifreq ifr;

   DEBUG_MSG("Inet_CorrectIface\t\tIface: %s", iface);

   sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (sock < 0)
      ERROR_MSG("socket()");

   memset(&ifr, 0, sizeof(ifr));
   strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
   if ( ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)             // check for iface
   {
      close(sock);
      return -1;
   }

   if (!(ifr.ifr_flags & IFF_UP ))                       // check for flag UP
   {
      close(sock);
      return -1;
   }

   if (ifr.ifr_flags & IFF_LOOPBACK )                    // check for loopback
   {
      Options.normal = 1;
      Error_msg("Ettercap can't be run on loopback device");
   }

   if ( ioctl(sock, SIOCGIFADDR, &ifr) < 0 )             // check for alias
   {
      close(sock);
      return -1;
   }

   close(sock);

   return 0;

}



int Inet_GetIfaceInfo(char *iface, int *MTU, char *MyMAC, unsigned long *IP, unsigned long *NetMask)
{
   int sock, dlpi;
   struct ifreq ifr;

   sock = socket(PF_INET, SOCK_DGRAM, 0);

   dlpi = Inet_OpenRawSock(iface);

   memset(&ifr, 0, sizeof(ifr));
   strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));

   if (MTU != NULL)
   {
      if ( ioctl(sock, SIOCGIFMTU, &ifr) < 0)
      {
         DEBUG_MSG("Inet_GetIfaceInfo -- MTU FAILED... assuming 1500");
         *MTU = 1500;
      }
      else
// FIXME
         //*MTU = ifr.ifr_mtu;
         *MTU = 1500;
   }

   if (MyMAC != NULL)
   {
      char buf[2048];
      union DL_primitives *dlp;

      dlp = (union DL_primitives*) buf;

      dlp->physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ;
      dlp->physaddr_req.dl_addr_type = DL_CURR_PHYS_ADDR;

      if (send_request(dlpi, (char *)dlp, DL_PHYS_ADDR_REQ_SIZE, "physaddr", ebuf) < 0)
         Error_msg("send_request(DL_PHYS_ADDR_REQ_SIZE) | %s \n", ebuf);

      if (recv_ack(dlpi, DL_PHYS_ADDR_ACK_SIZE, "physaddr", (char *)dlp, ebuf) < 0)
         Error_msg("recv_ack(DL_PHYS_ADDR_ACK_SIZE) | %s \n", ebuf);

      memcpy( MyMAC,(struct ether_addr *) ((char *) dlp + dlp->physaddr_ack.dl_addr_offset), ETHER_ADDR_LEN);

      Inet_CloseRawSock(dlpi);
   }

   if (IP != NULL)
   {
      if ( ioctl(sock, SIOCGIFADDR, &ifr) < 0 )
         ERROR_MSG("ioctl(SIOCGIFADDR)");
      memcpy((char *)IP, ifr.ifr_addr.sa_data+2, 4);
   }

   if (NetMask != NULL)
   {
      if ( ioctl(sock, SIOCGIFNETMASK, &ifr) < 0 )
         ERROR_MSG("ioctl(SIOCGIFNETMASK)");
      memcpy((char *)NetMask, ifr.ifr_addr.sa_data+2, 4);
      if (strcmp(Options.netmask, ""))       // specified on command line
         *NetMask = inet_addr(Options.netmask);
   }

   close(sock);

   return 0;

}


void Inet_SetNonBlock(int sock)
{
   DEBUG_MSG("Inet_SetNonBlock \t WRAPPERED TO NULL");

}


void Inet_CloseRawSock(int sock)
{

   DEBUG_MSG("Inet_CloseRawSock \t wrappered to NULL");

   //close(sock);    // the bpf is close when ettercap exits

}




int Inet_OpenRawSock(char *iface)      // adapted from libpcap source code
{
   register char *cp;
   int fd;
   int ppa;
   register dl_info_ack_t *infop;
   struct timeval to;
#ifdef HAVE_SYS_BUFMOD_H
   u_int32 flag, ss;
   char *release;
   u_int osmajor, osminor, osmicro;
#endif
   u_long buf[MAXDLBUF];
   char dname[100];
#ifndef HAVE_DEV_DLPI
   char dname2[100];
#endif

   if (dlpi_in_use != 0) {
      DEBUG_MSG("Inet_OpenRawSock %s", iface);
      DEBUG_MSG("Inet_OpenRawSock \t dlpi_in_use = %d ", dlpi_in_use);
      return dlpi_in_use;
   }

#ifdef HAVE_DEV_DLPI
   /*
   ** Remove any "/dev/" on the front of the device.
   */
   cp = strrchr(iface, '/');
   if (cp == NULL)
      cp = iface;
   else
      cp++;
   strlcpy(dname, cp, sizeof(dname));

   DEBUG_MSG("Inet_OpenRawSock -- HAVE_DEV_DLPI [%s]", dname);

   /*
    * Split the name into a device type and a unit number.
    */

   cp = split_dname(dname, &ppa);
   if (cp == NULL)
      Error_msg("ec_inet_solaris:%d HAVE_DEV_DLPI: %s missing or bad unit number\n", __LINE__, iface);

	*cp = '\0';

   /*
    * Use "/dev/dlpi" as the device.
    */

   cp = "/dev/dlpi";
   if ((fd = open(cp, O_RDWR)) < 0)
      ERROR_MSG("open()");

   /*
    * Get a table of all PPAs for that device, and search that
    * table for the specified device type name and unit number.
    */
   ppa = get_dlpi_ppa(fd, dname, ppa, ebuf);
   if (ppa < 0)
      ERROR_MSG("get_dlpi_ppa()");

#else
   /*
   ** Determine device and ppa
   */
   cp = split_dname(iface, &ppa);
   if (cp == NULL)
      Error_msg("ec_inet_solaris:%d dlpi: %s missing or bad unit number\n", __LINE__, iface);

   if (*iface == '/')
      strlcpy(dname, iface, sizeof(dname));
   else
      snprintf(dname, sizeof(dname), "%s/%s", DLPI_DEV_PREFIX, iface);

   strlcpy(dname2, dname, sizeof(dname2));
   *(dname + strlen(dname) - strlen(cp)) = '\0';

   DEBUG_MSG("Inet_OpenRawSock -- opening [%s]", dname);

   /* Try device without unit number */
   if ((fd = open(dname, O_RDWR)) < 0)
   {
      if (errno != ENOENT)
         ERROR_MSG("open()");

      DEBUG_MSG("Inet_OpenRawSock -- open(%s) failed... trying [%s]", dname, dname2);

      /* Try again with unit number */
      if ((fd = open(dname2, O_RDWR)) < 0)
         ERROR_MSG("open()");

      /* XXX Assume unit zero */
      ppa = 0;
   }
#endif

   /*
   ** Attach if "style 2" provider
   */
   if (dlinforeq(fd, ebuf) < 0 || dlinfoack(fd, (char *)buf, ebuf) < 0)
      ERROR_MSG("dlinforeq() || dlinfoack()");


   infop = &((union DL_primitives *)buf)->info_ack;
   if (infop->dl_provider_style == DL_STYLE2 && (dlattachreq(fd, ppa, ebuf) < 0 ||
       dlokack(fd, "attach", (char *)buf, ebuf) < 0))
      ERROR_MSG("dlattachreq()");

   /*
   ** Bind
   */
   if (dlbindreq(fd, 0, ebuf) < 0 || dlbindack(fd, (char *)buf, ebuf) < 0)
      ERROR_MSG("dlbindreq()");


   if (dlpromisconreq(fd, DL_PROMISC_PHYS, ebuf) < 0 || dlokack(fd, "promisc_phys", (char *)buf, ebuf) < 0)
      ERROR_MSG("dlpromisconreq(DL_PROMISC_PHYS)");

   if (dlpromisconreq(fd, DL_PROMISC_MULTI, ebuf) < 0 || dlokack(fd, "promisc_multi", (char *)buf, ebuf) < 0)
      DEBUG_MSG("WARNING: DL_PROMISC_MULTI failed (%s)", ebuf);


	if ( (dlpromisconreq(fd, DL_PROMISC_SAP, ebuf) < 0 || dlokack(fd, "promisc_sap", (char *)buf, ebuf) < 0))
      ERROR_MSG("dlpromisconreq(DL_PROMISC_SAP)");

   /*
   ** Determine link type
   */
   if (dlinforeq(fd, ebuf) < 0 || dlinfoack(fd, (char *)buf, ebuf) < 0)
      ERROR_MSG("dlinforeq()");


   infop = &((union DL_primitives *)buf)->info_ack;
   switch (infop->dl_mac_type) {
      case DL_CSMACD:
      case DL_ETHER:
         offset = 2;
         break;

      default:
         Error_msg("Interface not supported ( only DLT_EN10MB) | %d", infop->dl_mac_type);
   }

#ifdef DLIOCRAW
   /*
   ** This is a non standard SunOS hack to get the ethernet header.
   */
   if (strioctl(fd, DLIOCRAW, 0, NULL) < 0)
      ERROR_MSG("strioctl(DLIOCRAW)");

#endif

#ifdef HAVE_SYS_BUFMOD_H
   /*
   ** Another non standard call to get the data nicely buffered
   */
   if (ioctl(fd, I_PUSH, "bufmod") != 0)
      ERROR_MSG("ioclt(I_PUSH)");

   /*
   ** Now that the bufmod is pushed lets configure it.
   **
   ** There is a bug in bufmod(7). When dealing with messages of
   ** less than snaplen size it strips data from the beginning not
   ** the end.
   **
   ** This bug is supposed to be fixed in 5.3.2. Also, there is a
   ** patch available. Ask for bugid 1149065.
   */

   ss = MAXDLBUF;

   release = get_release(&osmajor, &osminor, &osmicro);
   if (osmajor == 5 && (osminor <= 2 || (osminor == 3 && osmicro < 2)) && getenv("BUFMOD_FIXED") == NULL) {
      fprintf(stderr, "WARNING: bufmod is broken in SunOS %s; ignoring snaplen.\n", release);
      ss = 0;
   }


   if (ss > 0 && strioctl(fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0)
      ERROR_MSG("strioctl(SBIOCSSNAP)");

   /*
   ** Set up the bufmod flags
   */
   if (strioctl(fd, SBIOCGFLAGS, sizeof(flag), (char *)&flag) < 0)
      ERROR_MSG("strioctl(SBIOCGFLAGS)");

   flag |= SB_NO_DROPS;
   if (strioctl(fd, SBIOCSFLAGS, sizeof(flag), (char *)&flag) != 0)
      ERROR_MSG("strioctl(SBIOCSFLAGS)");

   /*
    * Set the bufmod timeout
    */

   to.tv_sec = 1 / 1000;
   to.tv_usec = (1 * 1000) % 1000000;
   if (strioctl(fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0)
      ERROR_MSG("strioctl(SBIOCSTIME)");

#endif

   /*
   ** As the last operation flush the read side.
   */
   if (ioctl(fd, I_FLUSH, FLUSHR) != 0)
      ERROR_MSG("ioctl(I_FLUSH)");

   bufsize = MAXDLBUF * sizeof(u_int32);             // global var...

   DEBUG_MSG("Inet_OpenRawSock \t fd = %d", fd);

   dlpi_in_use = fd;   // for later use in the Set_Promisc

   return (fd);

}



int Inet_GetRawPacket(int sock, char *buffer, int MTU, short *type)
{
   register u_char *bp, *ep, *pk;
   #ifdef HAVE_SYS_BUFMOD_H
      register struct sb_hdr *sbp;
      #ifdef LBL_ALIGN
         struct sb_hdr sbhdr;
      #endif
   #endif
   int flags;
   struct strbuf data;
   int len = 0, pktlen = 0, caplen;
   char *buf;
   static char MyMAC[6]={0x65,0x74,0x74,0x65,0x72,0x63};


   if (SocketBuffer == -1) {                  // only the first time
      SocketBuffer = Buffer_Create(1.0e5);   // 100 K buffer
      DEBUG_MSG("Inet_GetRawPacket creates the buffer for the first time -- buf id = %d", SocketBuffer);
   }

   Buffer_Get(SocketBuffer, &pktlen, sizeof(u_int));
   len = Buffer_Get(SocketBuffer, buffer, pktlen );

   if (type != NULL) {
       if (!strncmp(MyMAC,"etterc",6))    // only the first time...
           Inet_GetIfaceInfo(Options.netiface, NULL, MyMAC, NULL, NULL);
       if (!memcmp(MyMAC,buffer,6))
           *type = 0; // PACKET_HOST
       else
           *type = 1; // !PACKET_HOST
   }

   if (len > 0) return len;                     // there was pending fata.

   buf = (u_char *)calloc(bufsize + offset, sizeof(char));     // buffer is global

   flags = 0;
   data.buf = (char *)buf + offset;
   data.maxlen = MAXDLBUF;
   data.len = 0;
   if ( getmsg(sock, &ctl, &data, &flags) < 0 ) {
      if (errno != EINTR) ERROR_MSG("getmsg()");
   }

   bp = buf + offset;

 /* Loop through packets */
   ep = bp + data.len;

#ifdef HAVE_SYS_BUFMOD_H
   while (bp < ep) {
   #ifdef LBL_ALIGN
      if ((long)bp & 3) {
         sbp = &sbhdr;
         memcpy(sbp, bp, sizeof(*sbp));
      }
      else
   #endif
      sbp = (struct sb_hdr *)bp;

      pk = bp + sizeof(*sbp);
      bp += sbp->sbh_totlen;
      caplen = sbp->sbh_msglen;
#else
      caplen = min(MAXDLBUF, data.len);
      pk = bp;
      bp += caplen;
#endif

      Buffer_Put(SocketBuffer, &caplen, sizeof(u_int) );
      Buffer_Put(SocketBuffer, pk, caplen );

#ifdef HAVE_SYS_BUFMOD_H
   }
#endif

   Buffer_Get(SocketBuffer, &pktlen, sizeof(u_int));
   len = Buffer_Get(SocketBuffer, buffer, pktlen );

   if (type != NULL) {
      if (!memcmp(MyMAC,buffer,6))
         *type = 0; // PACKET_HOST
      else
         *type = 1; // !PACKET_HOST
   }

   free(buf);
   return len;
}



int Inet_SendRawPacket(int sock, char *buffer, int len)
{
   struct  EnetHeaderInfo
   {
      struct ether_addr   DestEtherAddr;
      u_short             EtherFrameType;
   };

   struct EnetHeaderInfo ArpHeader = { {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, ETH_P_ARP };

   struct strbuf data, ctl;
   union DL_primitives *dlp;
   int c;
   struct EnetHeaderInfo *EnetHeaderInfoP;

   dlp = (union DL_primitives*) ctlbuf;
   dlp->unitdata_req.dl_primitive        = DL_UNITDATA_REQ;
   dlp->unitdata_req.dl_priority.dl_min  = 0;
   dlp->unitdata_req.dl_priority.dl_max  = 0;
   dlp->unitdata_req.dl_dest_addr_length = (sizeof(struct ether_addr) + sizeof(u_short));
   dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;

   EnetHeaderInfoP = (struct EnetHeaderInfo *)(ctlbuf + DL_UNITDATA_REQ_SIZE);
   memcpy(EnetHeaderInfoP, (char *)&(ArpHeader), (sizeof(struct ether_addr) + sizeof(u_short)));

   /* Send it */
   ctl.len = DL_UNITDATA_REQ_SIZE + sizeof (struct EnetHeaderInfo);
   ctl.buf = (char *)dlp;

   data.maxlen = len;
   data.len    = len;
   data.buf    = buffer;

   c = putmsg(sock, NULL, &data, 0);
   if (c == -1)
      ERROR_MSG("putmsg()");

   return (len);

}



int Inet_SetPromisc(char *iface)
{
   /*
    * we have already set the promisc mode when we opened the device
    */

   DEBUG_MSG("Inet_SetPromisc \t fd = %d -- WRAPPERED TO NULL (already set)", dlpi_in_use);

   return 0;
}



void Inet_Restore_ifr(void)
{

   /*
    *  this function is not needed !!
    *  when a dlpi is closed, the interface is restored
    */

}



void Inet_DisableForwarding(void)
{
   struct strioctl strIo;
	int fd;

	char buf[65536];
	char *cp;

   cp = "ip_forwarding";
	memset(buf, '\0', sizeof(buf));
	sprintf(buf, "%s", cp);

	if ((fd = open("/dev/ip", O_RDWR)) < 0)
		ERROR_MSG("open failed for /dev/ip");

	strIo.ic_cmd = ND_GET;
	strIo.ic_timout = 0;
	strIo.ic_len = sizeof(buf);
	strIo.ic_dp = buf;

	/* Call IOCTL to return status */

	if ( (ioctl(fd, I_STR, (char *)&strIo)) == -1 ) {
		ERROR_MSG("ioctl(I_STR)");
	}

	if (strIo.ic_cmd == ND_GET) {
		strcpy(IpForward_status, buf);
	}

   DEBUG_MSG("Inet_DisableForwarding -- previous value = %s", IpForward_status);

	memset(buf, '\0', sizeof(buf));
	sprintf(buf, "%s", cp);

	buf[strlen(buf)+1] = '0'; 	/* the format is "element"\0"value"\0 */

	strIo.ic_cmd = ND_SET;
	strIo.ic_timout = 0;
	strIo.ic_len = sizeof(buf);
	strIo.ic_dp = buf;

	if ( (ioctl(fd, I_STR, (char *)&strIo)) == -1 ) {
		ERROR_MSG("ioctl(I_STR)");
	}

   DEBUG_MSG("Inet_DisableForwarding -- NEW value = 0");

	close(fd);

	atexit(Inet_RestoreForwarding);

}



void Inet_RestoreForwarding(void)
{
   struct strioctl strIo;
	int fd;

	char buf[65536];
	char *cp;

   cp = "ip_forwarding";
	memset(buf, '\0', sizeof(buf));
	sprintf(buf, "%s", cp);

	sprintf(buf + strlen(buf)+1, "%s", IpForward_status); 	/* the format is "element"\0"value"\0 */

   DEBUG_MSG("Inet_RestoreForwarding -- restoring to value = %s", IpForward_status);

	if ((fd = open("/dev/ip", O_RDWR)) < 0)
		ERROR_MSG("open failed for /dev/ip");

	strIo.ic_cmd = ND_SET;
	strIo.ic_timout = 0;
	strIo.ic_len = sizeof(buf);
	strIo.ic_dp = buf;

	/* Call IOCTL to return status */

	if ( (ioctl(fd, I_STR, (char *)&strIo)) == -1 ) {
		ERROR_MSG("ioctl(I_STR)");
	}

	close(fd);

}


char *Inet_MacFromIP(unsigned long ip)
{
	int sockfd;
	static struct arpreq arpreq;
	struct sockaddr_in *sin;

   DEBUG_MSG("Inet_MacFromIP");

	bzero(&arpreq, sizeof(struct arpreq));
	arpreq.arp_pa.sa_family = AF_INET;

	sin = (struct sockaddr_in *)&arpreq.arp_pa;
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = ip;


	if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
		ERROR_MSG("socket()");

	if (ioctl(sockfd, SIOCGARP, (caddr_t)&arpreq) < 0) {

      struct recv_packet recvpck;
      char MyMAC[6];
      u_long MyIP;
      int MTU, sock;
      TIME_DECLARE;

      DEBUG_MSG("Inet_MacFromIP -- try to find it");

      sock = Inet_OpenRawSock(Options.netiface);

      Inet_GetIfaceInfo(Options.netiface, &MTU, MyMAC, &MyIP, NULL);

      if (ip == MyIP) {
         DEBUG_MSG("Inet_MacFromIP -- try to find me... ;)");

         memcpy(&arpreq.arp_ha.sa_data, MyMAC, ETHER_ADDR_LEN);
         Inet_CloseRawSock(sock);
         close(sockfd);
         return (char *) arpreq.arp_ha.sa_data;
      }

      recvpck.buf = Inet_Forge_packet( MTU + ALIGN_ETH_TO_WORD );
      recvpck.aligned = recvpck.buf + ALIGN_ETH_TO_WORD;

      Inet_Forge_ethernet( recvpck.aligned, MyMAC, ETH_BROADCAST, ETH_P_ARP );

      Inet_Forge_arp(recvpck.aligned + ETH_HEADER, ARPOP_REQUEST,
                         MyMAC, MyIP,
                         ARP_BROADCAST, ip );

      Inet_SendRawPacket(sock, recvpck.aligned, ETH_HEADER + ARP_HEADER);

		memset(recvpck.aligned, 0, MTU);

      fcntl(sock, F_SETFL, O_NONBLOCK);
      TIME_START;

      do
      {
         int len;
         short pkttype;
         ETH_header *ethpkt;
         ARP_header *arppkt;

         len = Inet_GetRawPacket(sock, recvpck.aligned, MTU, &pkttype);

         ethpkt = (ETH_header *)recvpck.aligned;
         arppkt = (ARP_header *)(ethpkt + 1);

         TIME_FINISH;

         if (len > 0 && pkttype == PACKET_HOST && ethpkt->type == htons(ETH_P_ARP) && arppkt->opcode == htons(ARPOP_REPLY))
         {
            if ( *(unsigned long *)arppkt->source_ip == ip )
            {
               memcpy(&arpreq.arp_ha.sa_data, &arppkt->source_add, ETHER_ADDR_LEN);
               Inet_Forge_packet_destroy( recvpck.buf );
               Inet_CloseRawSock(sock);
               close(sockfd);
               return (char *) arpreq.arp_ha.sa_data;
            }
         }
      } while ( TIME_ELAPSED < 0.5 );

		Inet_Forge_packet_destroy( recvpck.buf );
      Inet_CloseRawSock(sock);
      close(sockfd);
      return ETH_BROADCAST;  // workaround for non local ip
   }

   close(sockfd);
   return (char *) arpreq.arp_ha.sa_data;

}


/*
 *
 *    *** DLPI FUNCTIONS ***
 */

static int
send_request(int fd, char *ptr, int len, char *what, char *ebuf)
{
   struct strbuf ctl;
	int flags;

   ctl.maxlen = 0;
   ctl.len = len;
   ctl.buf = ptr;

	flags = 0;

   if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) {
      sprintf(ebuf, "send_request: putmsg \"%s\": %s", what, strerror(errno));
      DEBUG_MSG("\tDLPI : send_request -- ERROR %s", ebuf);
      return (-1);
   }
   return (0);
}

static int
recv_ack(int fd, int size, const char *what, char *bufp, char *ebuf)
{
   union DL_primitives *dlp;
   struct strbuf ctl;
   int flags;

   ctl.maxlen = MAXDLBUF;
   ctl.len = 0;
   ctl.buf = bufp;

   flags = 0;
   if (getmsg(fd, &ctl, (struct strbuf*)NULL, &flags) < 0) {
      sprintf(ebuf, "recv_ack: %s getmsg: %s", what, strerror(errno));
      DEBUG_MSG("\tDLPI : recv_ack -- ERROR %s", ebuf);
      return (-1);
   }

    dlp = (union DL_primitives *)ctl.buf;
    switch (dlp->dl_primitive) {
        case DL_INFO_ACK:
        case DL_PHYS_ADDR_ACK:
        case DL_BIND_ACK:
        case DL_OK_ACK:
#ifdef DL_HP_PPA_ACK
        case DL_HP_PPA_ACK:
#endif
        /*
         *  These are OK
         */
        break;

        case DL_ERROR_ACK:
            switch (dlp->error_ack.dl_errno)
            {
                case DL_BADPPA:
                    sprintf(ebuf, "recv_ack: %s bad ppa (device unit)", what);
                    break;
                case DL_SYSERR:
                    sprintf(ebuf, "recv_ack: %s: %s", what, strerror(dlp->error_ack.dl_unix_errno));
                    break;
                case DL_UNSUPPORTED:
                    sprintf(ebuf, "recv_ack: %s: Service not supplied by provider", what);
                    break;
                default:
                    sprintf(ebuf, "recv_ack: %s error 0x%x", what, (u_int)dlp->error_ack.dl_errno);
                    break;
            }
            return (-1);

        default:
            sprintf(ebuf, "recv_ack: %s unexpected primitive ack 0x%x ", what, (u_int)dlp->dl_primitive);
            return (-1);
    }

    if (ctl.len < size)
    {
        sprintf(ebuf, "recv_ack: %s ack too small (%d < %d)", what, ctl.len, size);
        return (-1);
    }
    return (ctl.len);
}

/*
static int
dlpromiscoffreq(int fd, u_int level, char *ebuf)
{
    dl_promiscon_req_t req;

    req.dl_primitive = DL_PROMISCOFF_REQ;
    req.dl_level     = level;

    return (send_request(fd, (char *)&req, sizeof(req), "promiscoff", ebuf));
}
*/

static int
dlpromisconreq(int fd, u_int level, char *ebuf)
{
    dl_promiscon_req_t req;

    req.dl_primitive = DL_PROMISCON_REQ;
    req.dl_level     = level;

    return (send_request(fd, (char *)&req, sizeof(req), "promiscon", ebuf));
}

static int
dlattachreq(int fd, u_int ppa, char *ebuf)
{
    dl_attach_req_t req;

    req.dl_primitive = DL_ATTACH_REQ;
    req.dl_ppa       = ppa;

    return (send_request(fd, (char *)&req, sizeof(req), "attach", ebuf));
}

static int
dlbindreq(int fd, u_int sap, char *ebuf)
{

    dl_bind_req_t req;

    memset((char *)&req, 0, sizeof(req));
    req.dl_primitive = DL_BIND_REQ;
#ifdef DL_HP_RAWDLS
    req.dl_max_conind = 1;  /* XXX magic number */
    /*
     *  22 is INSAP as per the HP-UX DLPI Programmer's Guide
     */
    req.dl_sap = 22;
    req.dl_service_mode = DL_HP_RAWDLS;
#else
    req.dl_sap = sap;
#ifdef DL_CLDLS
    req.dl_service_mode = DL_CLDLS;
#endif
#endif
    return (send_request(fd, (char *)&req, sizeof(req), "bind", ebuf));
}


static int
dlbindack(int fd, char *bufp, char *ebuf)
{
    return (recv_ack(fd, DL_BIND_ACK_SIZE, "bind", bufp, ebuf));
}


static int
dlokack(int fd, const char *what, char *bufp, char *ebuf)
{
    return (recv_ack(fd, DL_OK_ACK_SIZE, what, bufp, ebuf));
}


static int
dlinforeq(int fd, char *ebuf)
{
    dl_info_req_t req;

    req.dl_primitive = DL_INFO_REQ;

    return (send_request(fd, (char *)&req, sizeof(req), "info", ebuf));
}

static int
dlinfoack(int fd, char *bufp, char *ebuf)
{
    return (recv_ack(fd, DL_INFO_ACK_SIZE, "info", bufp, ebuf));
}

#ifdef HAVE_SYS_BUFMOD_H
static int
strioctl(int fd, int cmd, int len, char *dp)
{
   struct strioctl str;
   int rc;

   str.ic_cmd = cmd;
   str.ic_timout = -1;
   str.ic_len = len;
   str.ic_dp = dp;
   rc = ioctl(fd, I_STR, &str);

   if (rc < 0)
      return (rc);
   else
      return (str.ic_len);
}

static char *
get_release(u_int *majorp, u_int *minorp, u_int *microp)
{
   char *cp;
   static char buf[32];

   *majorp = 0;
   *minorp = 0;
   *microp = 0;
   if (sysinfo(SI_RELEASE, buf, sizeof(buf)) < 0)
      return ("?");
   cp = buf;
   if (!isdigit((int)*cp))
      return (buf);
   *majorp = strtol(cp, &cp, 10);
   if (*cp++ != '.')
      return (buf);
   *minorp =  strtol(cp, &cp, 10);
   if (*cp++ != '.')
      return (buf);
   *microp =  strtol(cp, &cp, 10);
   return (buf);
}
#endif


static char *
split_dname(char *device, int *unitp)
{
	char *cp;
	char *eos;
	int unit;

	/*
	 * Look for a number at the end of the device name string.
	 */
	cp = device + strlen(device) - 1;

	if (*cp < '0' || *cp > '9')
		return (NULL);

	/* Digits at end of string are unit number */
	while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9')
		cp--;

	unit = strtol(cp, &eos, 10);
	if (*eos != '\0')
		return (NULL);

	*unitp = unit;
	return (cp);
}



/* EOF */

// vim:ts=3:expandtab

