/* Copyright 1987 by the Massachusetts Institute of Technology */
#include <notice.h>

/* Network Information Protocol, used to dynamically assign IP addresses
 * by Mark Rosenstein, September 1987
 *
 * $Source: /afs/net.mit.edu/project/cgw/src/gw/nip/RCS/nip.c,v $
 * $Revision: 1.4 $
 * $Date: 90/12/07 17:10:46 $
 * $State: Exp $
 * $Author: jon $
 * $Locker:  $

 * $Log:	nip.c,v $
 * Revision 1.4  90/12/07  17:10:46  jon
 * allow a static nip gateway address instead of always using our address
 * 
 * Revision 1.3  88/08/17  20:55:33  jon
 * Remove htonl calls since ip addresses are always stored in network byte
 * order to start with.
 * 
 * Revision 1.2  87/10/12  01:52:15  jon
 * Uses INLNBRD to figure out broadcast address, uses external variables (set
 * in netcnf.c) for upper and lower address bounds, cleans up use of arp 
 * caches, registers "nipflg" (mostly so fnip will certainly work).
 * 
 * Revision 1.1  87/10/07  21:40:13  jon
 * Initial revision
 * 
 */

/* Currently this assumes ethernet as the link level and IP as the network 
   layer about which we return information.  This seems wrong to me, it 
   shouldn't be hard to generalize along the lines of arp. -- JR 10/09/87
*/

#ifndef lint
static char *rcsid_nip_c = "$Header: /afs/net.mit.edu/project/cgw/src/gw/nip/RCS/nip.c,v 1.4 90/12/07 17:10:46 jon Exp $";
#endif  lint

#include <types.h>
#include <sys.h>
#include <sysext.h>
#include "../src/const.h"
#include "../src/param.h"
#include "../src/defs.h"
#include "../src/macs.h"
#include "../src/net.h"
#include "../src/ext.h"

#include "../in/in.h"
#undef MSGFLG
#include "../in/inext.h"
#include "../arp/arp.h"
#include "../arp/arp_ext.h"
#include "../nets/ether.h"
#include "nip.h"

#define ADDRBUFLEN 40

ext inaddr nip_lowest_address;
ext inaddr nip_highest_address;

ext int use_static_nip_gateway;
ext byte static_nip_gateway_address[];

static char nipbp[] = "NIP bad packet from %s\n";
static char nipgp[] = "NIP request from %s\n";
static char nipdsc[] = "NIP error %d while sending to %s, discarded\n";
static char nipnoprot[] = "NIP no protocol %x from net %d intfc %s/%d\n";

NIP ()
{
  log_register_flag ("nip", &MSGFLG);
}

nip_in(iob)
iorb *iob;
{
    void do_nip();

    addtsk(syshnd, NIPPRI, do_nip, iob);
}

do_nip(iob)
iorb *iob;
{
    net *netp;
    arp_desc *ad;
    reg arp_ce *ce;
    reg struct nip_pkt *pkt;
    inaddr addr;
    char addrbuf[ADDRBUFLEN];
    reg int i;
    char *e10_paddr ();
    char *(*print_hardware_addr)();

    pkt = mknippkt(iob->i_addr);
    netp = mknet(iob->i_usr2);

    /* loop through looking for the right protocol, for now we only look
       for IP */
    ad = arp_ntbl[netp->n_net];
    while (ad) {
	if (ad->arpd_protocol == P_IN /* pkt->np_pro */ )
	  break;
	ad = ad->arpd_link;
    }
    if (ad == NULL) {
	niflog(L_INFC)
	  dolog(nipnoprot, /* pkt->np_pro */ P_IN, mksnet(netp));
	print_hardware_addr = e10_paddr;
      }
    else
      print_hardware_addr = ad->arpd_phaddr;

    /* verify packet */
    if ((iob->i_bxfr < sizeof(struct nip_pkt)) ||
	(pkt->np_version != htons(NIP_VERSION_1)) ||
	(pkt->np_opcode != htons(NIP_REQUEST)) ||
	(cksum(pkt, sizeof(struct nip_pkt)/2, 0) + 1 != 0)) {
	niflog(L_INFC)
	  dolog(nipbp, (*print_hardware_addr)(ADDRBUFLEN, &addrbuf[0],
					  pkt->np_source));
	freebuf(iob);
	return;
    }

    niflog(L_INFC)
      	dolog(nipgp, (*print_hardware_addr)(ADDRBUFLEN, &addrbuf[0],
					pkt->np_source));

    /* fill in info. we are the gateway! */
    if (use_static_nip_gateway)
      pkt->np_gateway = *(unsl *) &static_nip_gateway_address[0];
    else
      pkt->np_gateway = *(unsl *) &(iniatlst[netp->n_net]->inia_addr[0]);
    pkt->np_netmask = *(unsl *) &(iniatlst[netp->n_net]->inia_smask[0]);
    pkt->np_netaddress = (pkt->np_gateway) & (pkt->np_netmask);
    pkt->np_broadcast = pkt->np_netaddress | INLNBRD;
    pkt->np_lowest  = pkt->np_netaddress | nip_lowest_address.i_long;
    pkt->np_highest = pkt->np_netaddress | nip_highest_address.i_long;

    /* see if we have authoratative info */
    pkt->np_recommend = 0;
    if (ad != NULL) {
      for (i = 0; i < ad->arpd_hsize; i++) {
	for (ce = ad->arpd_hash[i]; ce; ce = ce->arpc_link) {
	  if (bcmp(pkt->np_source, ARP_CACHEENT_HRD(ad, ce), 6) == 0) {
	    pkt->np_recommend = *(unsl *) ARP_CACHEENT_PRO(ad, ce);
	    goto gotit;
	  }
	}
      }
    }
 gotit:

    /* fill in response */
    pkt->np_opcode = htons(NIP_RESPONSE);
    pkt->np_checksum = 0;
    pkt->np_checksum = ~cksum(pkt, sizeof(struct nip_pkt)/2, 0);
    iob->i_breq = iob->i_bxfr;

    /* send response, reusing same packet buffer */
    if ((i = (*netp->n_send)(iob, netp, ET_NIP, pkt->np_source)) != D_OK) {
	niflog(L_TRCO)
	  dolog(nipdsc, i,
		(*print_hardware_addr)(ADDRBUFLEN, &addrbuf[0], pkt->np_source));
	freebuf(iob);
	return;
    }
}
