/* Print ES-IS 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 "esis.h"
#include "isis.h"
#include "interface.h"
#include "addrtoname.h"

struct esis_fixed eif;
static char hex[] = "0123456789abcdef";

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

    if (length > NSAP_MAXLEN) {
	printf("esis 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%s", before, name, after);
}

/*
 * print ES-IS packets.
 */
void
esis_print(const u_char *cp, int length)
{
    register const u_char *p = cp, *a;
    register u_char cnt, len;
    register u_char code;

    (void)bzero((char *)&eif, sizeof(struct esis_fixed));

    if (length < ESIS_FIXED_SIZE)
	goto truncated;

    (void)bcopy(p, (char *)&eif, ESIS_FIXED_SIZE);
    PACK(eif.hold, eif.holdh, eif.holdl);
    PACK(eif.chk, eif.chkh, eif.chkl);
    p += ESIS_FIXED_SIZE;

    if (p > snapend) {
	printf("[|esis]");
	return;
    }

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

    /* print fixed part */

    length = eif.len - ESIS_FIXED_SIZE;
    if (eif.nlpi != NLPI_ESIS) {
	printf("not an esis packet ?");
	return;
    }
    if (eif.len > ESIS_MAX_LEN) {
	printf("esis illegal length %d", eif.len);
	return;
    }
    if (eif.ver != ESIS_VERSION) {
	printf("esis illegal version %d", eif.ver);
	return;
    }
    if (eif.res != 0)
	printf("esis reversed not zero %d", eif.res);

    switch (eif.type) {
      case ESIS_ESH_PDU:
	printf("esis ESH [hold %d]\n", eif.hold);
	length--;
	for (cnt = *p++; cnt > 0; cnt--) {
	    len = *p++;
	    a = p;
	    p += len;
	    length -= 1 + len;
	    if (length < 0)
		goto truncated;
	    if (p > snapend)
		return;
	    esis_npai_print(" SA ", cnt == 1 ? "" : ",\n", a, len);
	}
	break;
      case ESIS_ISH_PDU:
	printf("esis ISH [hold %d]\n", eif.hold);
	len = *p++;
	a = p;
	p += len;
	length -= 1 + len;
	if (length < 0)
	    goto truncated;
	if (p > snapend)
	    return;
	esis_npai_print(" NET ", "", a, len);
	break;
      case ESIS_RD_PDU:
	printf("esis RD [hold %d]\n", eif.hold);
	len = *p++;
	a = p;
	p += len;
	length -= 1 + len;
	if (length < 0)
	    goto truncated;
	if (p > snapend)
	    return;
	esis_npai_print(" DA ", ",\n", a, len);
	len = *p++;
	a = p;
	p += len;
	length -= 1 + len;
	if (length < 0)
	    goto truncated;
	if (p > snapend)
	    return;
	esis_npai_print(" SNPA ", *p == 0 ? "" : ",\n", a, len);
	len = *p++;
	length--;
	if (len == 0)
	    break;
	a = p;
	p += len;
	length -= len;
	if (length < 0)
	    goto truncated;
	if (p > snapend)
	    return;
	esis_npai_print(" NET ", "", a, len);
	break;
      default:
	printf("esis unknown PDU %d", eif.type);
	return;
    }

    if (length == 0)
	return;
    if (vflag)
	printf(" optlen=%d", length);
    else
	return;

    if (p + length > snapend)
	return;

    while (length > 0) {
	code = *p++;
	len = *p++;
	switch (code) {
	  case ESIS_OPT_SECU:
	    printf(" SECU %d", *p);
	    break;
	  case ESIS_OPT_QOS:
	    printf(" QOS %d", *p);
            break;
	  case ESIS_OPT_PRI:
	    printf(" PRI %d", *p);
            break;
	  case ESIS_OPT_AMSK:
	    a = p;
	    esis_npai_print("\nADDR MASK ", "", a, len);
	    break;
	  case ESIS_OPT_SMSK:
	    a = p;
	    esis_npai_print("\nSNPA MASK ", "", a, len);
            break;
	  case ESIS_OPT_ESCT:
	    printf(" ESCT %d", ((u_short)(p[0]) << 8) | ((u_short)(p[1])));
	    break;
	  case ESIS_OPT_PRO:
	    printf(" PROTO ");
	    for (cnt = len, a = p; cnt > 0; cnt--, a++) {
		switch (*a) {
		  case NLPI_CLNP:
		    printf("CLNP");
                    break;
		  case NLPI_DODIP:
                    printf("IP");
                    break;
		  default:
                    printf("%02x", *a);
		    break;
		}
		if (cnt > 1)
		    printf(", ");
	    }
	    break;
	  default:
	    printf(" unknown option %d/%d", code, len);
	    return;
	}
	length -= 2 + len;
	p += len;
    }

    return;

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