/* $Header: nipsrv.c,v 1.1 87/09/22 16:07:36 mar Exp $
 *
 * Network Information Protocol module of netconfig
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <net/nit.h>
#include <net/nip.h>
#include "netconfig.h"


#define mkina(w, x, y, z) ((long) ((w << 24) | (x << 16) | (y << 8) | z))


main(argc, argv)
int	argc;
char	**argv;
{
    struct nipinfo ni;

    ni.ni_netaddr = mkina(18, 72, 0, 0);
    ni.ni_netmask = mkina(255, 255, 0, 0);
    ni.ni_broadcast = mkina(18, 72, 0, 0);
    ni.ni_lowest = mkina(0, 0, 0, 0);
    ni.ni_highest = mkina(0, 0, 0, 0);
    ni.ni_recommend = mkina(0, 0, 0, 0);
    ni.ni_gateway = mkina(18, 72, 0, 1);
    ni.ni_flags = NI_NETADDR|NI_NETMASK|NI_BROADCAST|NI_GATEWAY;

    if (argc > 1) {
	struct nipreq nr;
	int s;

	nr.nip_state = strcmp(argv[1], "off");
	nr.nip_data.np_netaddress = ni.ni_netaddr;
	nr.nip_data.np_netmask = ni.ni_netmask;
	nr.nip_data.np_broadcast = ni.ni_broadcast;
	nr.nip_data.np_lowest = ni.ni_lowest;
	nr.nip_data.np_highest = ni.ni_highest;
	nr.nip_data.np_recommend = ni.ni_recommend;
	nr.nip_data.np_gateway = ni.ni_gateway;
	if ((s = socket(AF_NIT, SOCK_RAW, 0)) < 0)
	  perror("socket");
	if (ioctl(s, SIOCSNIP, &nr) < 0)
	  perror("ioctl");
	printf("Done\n");
	exit(0);
    }

    while (1) {
	nipsrv("qe0", &ni);
	printf("restarting\n");
    }
}


unsigned short cksum();


nipsrv(ifc, ni)
char	*ifc;
struct nipinfo *ni;
{
    int s, i;
    struct sockaddr_nit snit;
    struct sockaddr se;
    struct nit_ioc nic;
    struct nip_pkt pkt;
    unsigned char buf[sizeof(struct nit_hdr) + sizeof(struct ether_header) + sizeof(struct nip_pkt)];
    struct nip_pkt *np;
    struct ether_header *eh, *eah;
    struct nit_hdr *nh;

    /* open and configure socket */
    if ((s = socket(AF_NIT, SOCK_RAW, 0)) < 0) {
	perror("socket");
	return(0);
    }
    snit.snit_family = AF_NIT;
    strncpy(snit.snit_ifname, ifc, NITIFSIZ);
    if (bind(s, &snit, sizeof(snit)) < 0) {
	perror("bind");
	return(0);
    }
    nic.nioc_bufspace = BUFSIZE;
    nic.nioc_chunksize = sizeof(struct nit_hdr);
    nic.nioc_typetomatch = ETHERTYPE_NIP;
    nic.nioc_snaplen = sizeof(struct nip_pkt) + sizeof(struct nip_pkt);
    nic.nioc_flags = 0;
    if (ioctl(s, SIOCSNIT, &nic) < 0) {
	perror("ioctl");
	return(0);
    }

    /* wait for requests */
    while (1) {
	/* get & validate packet */
	if ((i = read(s, buf, sizeof(buf))) < 0) {
	    perror("read");
	    return(0);
	}

	nh = (struct nit_hdr *) &buf[0];
	eh = (struct ether_header *) &buf[sizeof(struct nit_hdr)];
	np = (struct nip_pkt *) &buf[sizeof(struct nit_hdr) + sizeof(struct ether_header)];

	if ((nh->nh_state != NIT_CATCH) ||
	    (i < sizeof(buf)) ||
	    (nh->nh_datalen < sizeof(struct nip_pkt)) ||
	    (np->np_version != htons(NIP_VERSION)) ||
	    (np->np_opcode != htons(NIP_REQUEST)) ||
	    (cksum((unsigned short *) np, sizeof(struct nip_pkt)) != CKSUMOK))
	  continue;

	printf("got a request, sending response\n");

	/* Compose and send response */
	bzero(&pkt, sizeof(pkt));
	pkt.np_opcode = htons(NIP_RESPONSE);
	pkt.np_version = htons(NIP_VERSION);
	if (ni->ni_flags & NI_NETADDR)
	  pkt.np_netaddress = ni->ni_netaddr;
	if (ni->ni_flags & NI_NETMASK)
	  pkt.np_netmask = ni->ni_netmask;
	if (ni->ni_flags & NI_BROADCAST)
	  pkt.np_broadcast = ni->ni_broadcast;
	if (ni->ni_flags & NI_LOWEST)
	  pkt.np_lowest = ni->ni_lowest;
	if (ni->ni_flags & NI_HIGHEST)
	  pkt.np_highest = ni->ni_highest;
	if (ni->ni_flags & NI_RECOMMEND)
	  pkt.np_recommend = ni->ni_recommend;
	if (ni->ni_flags & NI_GATEWAY)
	  pkt.np_gateway = ni->ni_gateway;
	pkt.np_checksum = ~cksum((unsigned short *) &pkt.np_source[0], sizeof(pkt));
	se.sa_family = AF_UNSPEC;
	eah = (struct ether_header *) &se.sa_data[0];
	for (i = 0; i < 6; i++)
	  eah->ether_dhost[i] = eh->ether_shost[i];
	eah->ether_type = ETHERTYPE_NIP;
	if (sendto(s, &pkt, sizeof(pkt), 0, &se, sizeof(se)) < 0) {
	    perror("sendto");
	    return(0);
	}
    }
}



unsigned short cksum(start, length)
unsigned short *start;
int	length;
{
    unsigned long acc = 0;
    length /= 2;
    while (length--)
      acc += *start++;
    acc = (acc & 0xffff) + (acc >> 16);
    acc = (acc & 0xffff) + (acc >> 16);
    return((unsigned short) acc);
}

