/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmsd/ll2.c
 *
 *	Layer 2 monitoring for HNMS IO module.
 *
 *	Jude George
 *	NAS Facility, NASA Ames Research Center
 *
 *	Copyright (c) 1994 Jude George
 *
 *	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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
\*/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/errno.h>

#include "stdhnms.h"

/*\
 *  Default location of tcpdump, and the args we pass it.  We listen
 *  to ICMP echo replies and to a small pseudorandom subset of the
 *  TCP traffic (for discovery).
\*/
#define TCPDUMP_DEF		"/usr/local/etc/tcpdump"
#define ARGS			" -nql 'icmp[0:1] = 0 or tcp[7:1]&0x7=7'"

extern int			errno;

static FILE			*fp;
static int			discovery = 0;

/*\
 *  Initialize the promiscuous MAC-layer monitor.
\*/
int LL2_init()
{
    int			rval, fd;
    char		*cp, *tcpdump;
    char		cmd[MAXPATHLEN];

    cp = getenv("HNMS_PROMISCUOUS");
    if (cp)
	discovery = 1;

    tcpdump = getenv("TCPDUMP");
    if (!tcpdump)
	tcpdump = TCPDUMP_DEF;

    sprintf(cmd, "%s%s", tcpdump, ARGS);

    fp = popen(cmd, "r");

    if (!fp)
	return -1;
    else {
	fd = fileno(fp);
	rval = fcntl(fd, F_SETFL, O_NONBLOCK);
	if (rval < 0)
	    return -1;
	return 0;
    }
}

/*\
 *  Depending on the L2 code (NIT/BPF/etc) used in tcpdump we may not
 *  pick up pings sent to our own IP address.  So we "fake" a ping response.
\*/
static void IO_fix_localhost()
{
    static int			resolved = 0;
    static struct hostent	*hp;
    static unsigned long	ip_address = 0;
    static char			hostname[MAXHOSTNAMELEN];

    if (!resolved) {
	resolved = 1;
	gethostname(hostname, MAXHOSTNAMELEN);
	hp = gethostbyname(hostname);
	if (!hp)
	    return;
	ip_address = *(unsigned long *)(hp->h_addr_list[0]);
	if (ip_address)
	    IO_set_announce_cache(ip_address);
    }
    if (ip_address)
	IO_set_status_cache(ip_address);
}

/*\
 *  Check if ICMP packets are available from the MAC-layer monitor.
 *  If so, scan them for SOURCE IP addresses.
\*/
int LL2_listen()
{
    static char		buf[STRBUFLEN];
    static char		time[STRBUFLEN];
    int			rval;
    unsigned int	c1, c2, c3, c4;
    unsigned int	ip_address;
    unsigned int	current_time;
    static unsigned int	start_time = 0, prt = 0, ll2_ok = 0;

    if (start_time == 0) {
	start_time = get_int_time();
	prt = PARAM_get_int(oid_hnmsModuleIoRampTime);
    }

    while (fgets(buf, STRBUFLEN - 1, fp) != NULL) {
	rval = sscanf(buf, "%s %d.%d.%d.%d", time, &c1, &c2, &c3, &c4);
	if (rval != 5)
	    continue;
	ll2_ok = 1;
	ip_address = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
	if (c1 > 1 && c1 < 224 && c1 != 127) {
	    if (discovery)
		IO_set_announce_cache(ip_address);
	    IO_set_status_cache(ip_address);
	    HNMS_debug(DEBUG_OTHER, "LL2: received packet from %x\n",
		       ip_address);
	}
    }
    if (fp != NULL)
	if (ferror(fp))
	    if (errno != EAGAIN) {
		HNMS_debug(DEBUG_ERRORS,
			   "LL2: error from tcpdump pipe.  exiting.\n"),
		exit(1);
	    }
    if (ll2_ok == 0) {
	current_time = get_int_time();
	if ((current_time - start_time) > prt) {
	    HNMS_debug(DEBUG_ERRORS,
		       "LL2: allowed ramp time = %d seconds; ", prt);
	    HNMS_debug(DEBUG_ERRORS,
		       "LL2: tcpdump silent since startup.  exiting.\n");
	    exit(1);
	}
    }
    IO_fix_localhost();
    return 0;
}
