/*
 * $RCSfile: netmask.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: netmask.c,v $
 * Revision 1.2  1996/01/29 19:49:20  slm
 * Updated copyright and version.
 *
 * Revision 1.1  1995/07/28  21:41:44  slm
 * Initial revision
 *
 * Revision 1.11.3.3  1994/12/02  17:46:30  hkaram
 * Added casts
 * ,
 *
 * Revision 1.11.3.2  1994/11/22  20:52:59  hkaram
 * Added casts to MapResolve calls
 *
 * Revision 1.11.3.1  1994/10/27  20:52:00  hkaram
 * New branch
 *
 * Revision 1.11  1994/01/27  21:54:21  menze
 *   [ 1994/01/13          menze ]
 *   Now uses library routines for rom options
 */

/*
 * Maintains a table of network masks.
 */

#include "xkernel.h"
#include "ip_host.h"
#include "netmask.h"
#include "romopt.h"
#ifndef XKMACHKERNEL
#include "x_stdio.h"
#endif /* XKMACHKERNEL */

int tracenetmask;

IPhost IP_LOCAL_BCAST = { 255, 255, 255, 255 };

#define CLASSA(ad) (~((ad).a) & 0x80)
#define CLASSB(ad) (((ad).a & 0x80) && (~((ad).a) & 0x40))
#define CLASSC(ad) (((ad).a & 0x80) && ((ad).a & 0x40) && (~((ad).a) & 0x20))

static IPhost classCmask = { 0xff,0xff,0xff,0 };
static IPhost classBmask = { 0xff,0xff,0,0 };
static IPhost classAmask = { 0xff,0,0,0 };
static IPhost ipNull = { 0, 0, 0, 0 };

#define MAX_NET_MASKS 50
#define IPNETMAPSIZE  17  /* should 2 * max number of masks in use */

static Map netMaskMap;

#ifdef __STDC__

static int  bcastHost(IPhost *, IPhost *);
static XkReturn netMaskOpt(char **, int, int, VOID *);

#else

static int  bcastHost();
static XkReturn netMaskOpt();

#endif

static RomOpt romOptions[] = {
    { "", 3, netMaskOpt }
};

int
netMaskAdd(iph, newMask)
IPhost *iph;
IPhost *newMask;
{
    IPhost  net, defMask, *mask;

    xAssert(netMaskMap);
    netMaskDefault(&defMask, iph);
    IP_AND(net, *iph, defMask);
    xTrace2(netmask, TR_EVENTS, "netmask adding mask %s for net %s",
	    ipHostStr(newMask), ipHostStr(&net));
    if (mapResolve(netMaskMap, &net, (void **)&mask) == XK_SUCCESS) {
	xTrace1(netmask, TR_EVENTS,
		"netmask add is overriding previous mask %s", ipHostStr(mask));
	xFree((char *)mask);
	mapRemoveKey(netMaskMap, &net);
    }
    mask = X_NEW(IPhost);
    *mask = *newMask;
    if (mapBind(netMaskMap, &net, mask) == ERR_BIND)
	return -1;
    return 0;
}

static XkReturn
netMaskOpt(str, nFields, ln, arg)
char **str;
int  nFields;
int  ln;
VOID *arg;
{
    IPhost net, mask;

    if (str2ipHost(&net, str[1]) == XK_FAILURE ||
	 str2ipHost(&mask, str[2]) == XK_FAILURE) {
	return XK_FAILURE;
    }
    if (netMaskAdd(&net, &mask)) {
#ifndef XKMACHKERNEL
	sprintf(errBuf,
		"netmask: error adding initial mask %s -> %s to map",
		ipHostStr(&net), ipHostStr(&mask));
	xError(errBuf);
#else
	printf("netmask: error adding initial mask %s -> %s to map",
	       ipHostStr(&net), ipHostStr(&mask));
#endif /* XKMACHKERNEL */
    }
    return XK_SUCCESS;
}

void
netMaskInit()
{
    netMaskMap = mapCreate(IPNETMAPSIZE, sizeof(IPhost));
    findRomOpts("netmask", romOptions, sizeof(romOptions)/sizeof(RomOpt), 0);
}


void
netMaskDefault(mask, host)
IPhost *mask, *host;
{
    /* Use default network mask */
    if (CLASSC(*host))
	*mask = classCmask;
    else if (CLASSB(*host))
	*mask = classBmask;
    else    /* CLASS A */
	*mask = classAmask;
}

void
netMaskFind(mask, host)
IPhost *mask;
IPhost *host;
{
    IPhost *mp;
    IPhost defMask, net;

    xAssert(netMaskMap);
    netMaskDefault(&defMask, host);
    IP_AND(net, *host, defMask);
    if (mapResolve(netMaskMap, &net, (void **)&mp) == XK_FAILURE)
	*mask = defMask;
    else
	*mask = *mp;
}

static int
bcastHost(h, m)
IPhost *h, *m;
{
    IPhost mComp;
    IPhost res;

    IP_COMP(mComp, *m);
    /* Look for a host component that is all zeroes */
    IP_AND(res, *h, mComp);
    if (IP_EQUAL(res, ipNull))
	return 1;
    /* Look for a host component that is all ones */
    IP_OR(res, *h, *m);
    if (IP_EQUAL(res, IP_LOCAL_BCAST))
	return 1;
    return 0;
}

int
netMaskIsBroadcast(h)
IPhost *h;
{
    IPhost m;

    netMaskFind(&m, h);
    if (bcastHost(h, &m))
	return 1;
    /*
     * See if this is a broadcast address using the default mask
     * (i.e., the original mask was for a subnet)
     */
    netMaskDefault(&m, h);
    if (bcastHost(h, &m))
	return 1;
    /* See if the original address is all ones */
    if (IP_EQUAL(*h, IP_LOCAL_BCAST))
	return 1;
    /* Address is not broadcast */
    return 0;
}

/*
 * Are h1 and h2 on the same subnet?
 */
int
netMaskSubnetsEqual(h1, h2)
IPhost *h1, *h2;
{
    IPhost m1, m2;

    netMaskFind(&m1, h1);
    netMaskFind(&m2, h2);
    if (! IP_EQUAL(m1, m2)) {
	return 0;
    }
    IP_AND(m1, *h1, m1);
    IP_AND(m2, *h2, m2);
    return IP_EQUAL(m1, m2);
}

/*
 * Are h1 and h2 on the same net?
 */
int
netMaskNetsEqual(h1, h2)
IPhost *h1, *h2;
{
    IPhost m, net1, net2;

    netMaskDefault(&m, h1);
    IP_AND(net1, *h1, m);
    IP_AND(net2, *h2, m);
    return IP_EQUAL(net1, net2);
}
