/* Print cisco ISO-IGRP packets */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#ifdef __STDC__
#include <stdlib.h>
#endif
#include <unistd.h>

#include "ieee.h"
#include "clnp.h"
#include "iso-igrp.h"
#include "interface.h"
#include "addrtoname.h"

struct iso_igrp_fixed iif;
struct iso_igrp_hello iih;
struct iso_igrp_metric iim;
struct iso_igrp_prefval iiv;
struct iso_igrp_param iip;
static char hex[] = "0123456789abcdef";

/*
 * print ISO-IGRP Network Protocol Address Information.
 */
static void
iso_igrp_npai_print(char *before, const u_char *address, int length)
{
    char name[(NSAP_MAXLEN+1)*2];
    register char *c;

    if (length > NSAP_MAXLEN) {
	printf("iso-igrp NPAI too long %d", length);
	return;
    }
    for (c = name; length > 0; length--) {
	*c++ = hex[*address >> 4];
	*c++ = hex[*address++ & 0x0f];
    }
    *c = '\0';
    printf("%s%s", before, name);
}

/*
 * print ISO-IGRP metrics.
 */
static void
iso_igrp_metric_print(void)
{
    iim.delay = (iim.dly[0] << 16) | (iim.dly[1] << 8) | iim.dly[2];
    iim.bandwidth = (iim.bw[0] << 16) | (iim.bw[1] << 8) | iim.bw[2];
    iim.mtu = (iim.m[0] << 8) | iim.m[1];
    iim.metric = iim.bandwidth + iim.delay;
    if (iim.metric > 0xffffff)
	iim.metric = 0xffffff;
    printf("d=%d b=%d r=%d l=%d M=%d mtu=%d in %d hops",
	   10*iim.delay, iim.bandwidth == 0 ? 0 : 10000000 / iim.bandwidth,
	   iim.reliability, iim.load, iim.metric,
	   iim.mtu, iim.hopcount);
}

/*
 * print ISO-IGRP area list info.
 */
static void
iso_igrp_arealist_print(const u_char *p, int length)
{
    register u_char len;
    int first = 0;

    printf("\n in area:");
    while (length > 0) {
	len = *p++;
	iso_igrp_npai_print(first ? ", " : " ", p, len);
	first = 1;
	p += len;
	length -= 1 + len;
    }
}

/*
 * print ISO-IGRP area route info.
 */
static void
iso_igrp_arearoute_print(const u_char *p, int length)
{
    while (length > 0) {
	(void)bzero((char *)&iim, sizeof(struct iso_igrp_metric));
	(void)bcopy(p, (char *)&iim, ISO_IGRP_METRIC_SIZE);
	p += ISO_IGRP_METRIC_SIZE;
	length -= ISO_IGRP_METRIC_SIZE;
	printf("\n area route ");
	iso_igrp_metric_print();
	iso_igrp_npai_print(" for area ", p, ISO_IGRP_AREA_SIZE);
	p += ISO_IGRP_AREA_SIZE;
	length -= ISO_IGRP_AREA_SIZE;
    }
}

/*
 * print ISO-IGRP station route info.
 */
static void
iso_igrp_stationroute_print(const u_char *p, int length)
{
    while (length > 0) {
	(void)bzero((char *)&iim, sizeof(struct iso_igrp_metric));
	(void)bcopy(p, (char *)&iim, ISO_IGRP_METRIC_SIZE);
	p += ISO_IGRP_METRIC_SIZE;
	length -= ISO_IGRP_METRIC_SIZE;
	printf("\n station route ");
	iso_igrp_metric_print();
	iso_igrp_npai_print(" ", p, ISO_IGRP_STATION_SIZE);
	p += ISO_IGRP_STATION_SIZE;
	length -= ISO_IGRP_STATION_SIZE;
    }
}

/*
 * print ISO-IGRP prefix route info.
 */
static void
iso_igrp_prefixroute_print(const u_char *p, int length)
{
    register u_char len;

    while (length > 0) {
	(void)bzero((char *)&iiv, sizeof(struct iso_igrp_prefval));
	(void)bcopy(p, (char *)&iiv, ISO_IGRP_PREFVAL_SIZE);
	PACK(iiv.hold, iiv.holdh, iiv.holdl);
	p += ISO_IGRP_PREFVAL_SIZE;
	length -= ISO_IGRP_PREFVAL_SIZE;
	(void)bzero((char *)&iim, sizeof(struct iso_igrp_metric));
	(void)bcopy(p, (char *)&iim, ISO_IGRP_METRIC_SIZE);
	p += ISO_IGRP_METRIC_SIZE;
	length -= ISO_IGRP_METRIC_SIZE;
	printf("\n prefix route (hold=%d) ", iiv.hold);
	iso_igrp_metric_print();
	len = *p++;
	iso_igrp_npai_print("\n  ", p, len);
	p += len;
	length -= 1 + len;
	len = *p++;
	iso_igrp_npai_print(" via ", p, len);
	p += len;
	length -= 1 + len;
    }
}

/*
 * print ISO-IGRP params info.
 */
static void
iso_igrp_params_print(const u_char *p, int length)
{
    if (length < ISO_IGRP_PARAM_SIZE)
	return;
    (void)bzero((char *)&iip, sizeof(struct iso_igrp_param));
    (void)bcopy(p, (char *)&iip, ISO_IGRP_PARAM_SIZE);
    printf("\n params: cycle=%d, flush=%d, blind=%d, hello=%d, k[1-5]=%d,%d,%d,%d,%d",
	   iip.cycle, iip.flush, iip.blind, iip.hello,
	   iip.k1, iip.k2, iip.k3, iip.k4, iip.k5);
}

/*
 * print cisco ISO-IGRP packets.
 */
void
iso_igrp_print(const u_char *p, int length)
{
    register const u_char *a;
    register u_char len, code;

    (void)bzero((char *)&iif, sizeof(struct iso_igrp_fixed));

    if (length < ISO_IGRP_FIXED_SIZE)
	goto truncated;

    (void)bcopy(p, (char *)&iif, ISO_IGRP_FIXED_SIZE);
    PACK(iif.len, iif.lenh, iif.lenl);
    PACK(iif.chk, iif.chkh, iif.chkl);
    p += ISO_IGRP_FIXED_SIZE;

    if (p > snapend) {
	printf("[|iso-igrp]");
	return;
    }

    if ((int)iif.len > length)
	goto truncated;

    /* print fixed part */

    length = iif.len - ISO_IGRP_FIXED_SIZE;
    if (iif.nlpi != NLPI_IGRP) {
	printf("not an iso-igrp packet ?");
	return;
    }
    switch (iif.opcode) {
      case ISO_IGRP_OPCODE_STATION:
	printf("iso-igrp Upd L1");
	break;
      case ISO_IGRP_OPCODE_AREA:
	printf("iso-igrp Upd L2");
        break;
      case ISO_IGRP_OPCODE_HELLO:
	printf("iso-igrp HELLO");
	break;
      case ISO_IGRP_OPCODE_QUERY:
	printf("iso-igrp QUERY");
	break;
      default:
	printf("iso-igrp (unknown)");
	return;
    }

    /* print source NSAP address */

    len = *p++;
    a = p;
    p += len;
    length -= 1 + len;
    if (length < 0)
	goto truncated;
    if (p > snapend)
	return;
    iso_igrp_npai_print(" from ", a, len);

    /* print opcode dependent info */

    if (iif.opcode == ISO_IGRP_OPCODE_HELLO) {
	(void)bzero((char *)&iih, sizeof(struct iso_igrp_hello));
	if (length < ISO_IGRP_HELLO_SIZE)
	    goto truncated;
	(void)bcopy(p, (char *)&iih, ISO_IGRP_HELLO_SIZE);
	PACK(iih.hold, iih.holdh, iih.holdl);
	p += ISO_IGRP_HELLO_SIZE;
	if (p > snapend)
	    return;
	switch (iih.type) {
	  case ISO_IGRP_LEVEL0:
	    /* old version */
	    break;
	  case ISO_IGRP_LEVEL1_ONLY:
	    printf(" L1");
	    break;
	  case ISO_IGRP_LEVEL2_ONLY:
	    printf(" L2");
	    break;
	  case ISO_IGRP_LEVEL1_LEVEL2:
	    printf(" L1&2");
	    break;
	  default:
	    printf(" unknown type %d", iih.type);
	    break;
	}
	printf(" hold=%d", iih.hold);
    } else {
	/* other opcodes */
	printf(" edition=%d", iif.edit);
    }

    if (!vflag)
	return;

    while (length > 0) {
	code = *p++;
	len = *p++;
	if (len < 2)
	    return;
	len -= 2;
	a = p;
	p += len;
	length -= len;
	if (length < 0)
	    return;
	if (p > snapend)
	    return;

	switch (code) {
	  case ISO_IGRP_AREALIST:
	    iso_igrp_arealist_print(a, len);
	    break;
	  case ISO_IGRP_AREAROUTE:
	    iso_igrp_arearoute_print(a, len);
	    break;
	  case ISO_IGRP_STATIONROUTE:
	    iso_igrp_stationroute_print(a, len);
            break;
          case ISO_IGRP_PREFIXROUTE:
	    iso_igrp_prefixroute_print(a, len);
            break;
	  case ISO_IGRP_PARAMS:
	    iso_igrp_params_print(a, len);
	    break;
	  default:
	    printf("\n Unknown info code %d", code);
	    return;
	}
    }

    return;

  truncated:
    printf("truncated-iso-igrp %d", length);
}
