/* Print Inter Domain Routeing Protocol (ISO DIS 10747) BISPDU */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#ifdef __STDC__
#include <stdlib.h>
#endif
#include <unistd.h>

#include "ieee.h"
#include "clnp.h"
#include "isis.h"
#include "idrp.h"
#include "interface.h"
#include "addrtoname.h"

struct idrp_packet pkt;
struct idrp_path_attr ipa;
static char hex[] = "0123456789abcdef";

/*
 * print address (NSAP, NET, RDI)
 */
static const u_char *
idrp_addr_print(const u_char *p, int len)
{
    if (len > NSAP_MAXLEN) {
	printf("idrp address too long %d", len);
	p += len;
	return p;
    }
    while (--len > 0) {
	putchar(hex[*p >> 4]);
	putchar(hex[*p++ & 0x0f]);
    }
    return p;
}

/*
 * print SNPA (length in semi-octets)
 */
static const u_char *
idrp_snpa_print(const u_char *p, int len)
{
    while (--len >= 0) {
	putchar(hex[*p >> 4]);
	if (len == 0)
	    return (p + 1);
	else
	    putchar(hex[*p++ & 0x0f]);
    }
    return p;
}

/*
 * print attribute names
 */
static const u_char *
idrp_attrname_print(const u_char *p, int midfld)
{
    register const u_char *a;
    register u_char type = *p++;
    u_char lenh, lenl;
    register u_short len;

    switch (type) {
      /* type sepcific distinguishing attributes */
      case IDRP_PAT_DELAY:
	printf("TRANSIT_DELAY%s", midfld ? ", " : "");
	return p;
      case IDRP_PAT_R_ERR:
	printf("RESIDUAL_ERROR%s", midfld ? ", " : "");
        return p;
      case IDRP_PAT_EXPS:
	printf("EXPENSE%s", midfld ? ", " : "");
        return p;
      case IDRP_PAT_CAPA:
	printf("CAPACITY%s", midfld ? ", " : "");
        return p;
      case IDRP_PAT_PRI:
	printf("PRIORITY%s", midfld ? ", " : "");
        return p;

      /* type-value-specific distinguishing attributes */
      case IDRP_PAT_S_QOS:
      case IDRP_PAT_D_QOS:
	lenh = *p++;
	lenl = *p++;
	PACK(len, lenh, lenl);
	a = p;
	p += len;
	if (type == IDRP_PAT_S_QOS)
	    printf("\n  SOURCE_SPECIFIC_QOS");
	else
	    printf("\n  DESTINATION_SPECIFIC_QOS");
	if (vflag) {
	    len = *a++;
	    printf(" NSAP=");
	    a = idrp_addr_print(a, len);
	    /* TO_DO */
	}
	if (midfld)
	    printf(", ");
	return p;
      case IDRP_PAT_S_SEC:
      case IDRP_PAT_D_SEC:
	lenh = *p++;
	lenl = *p++;
	PACK(len, lenh, lenl);
	a = p;
	p += len;
	if (type == IDRP_PAT_S_SEC)
	    printf("\n  SOURCE_SPECIFIC_SECURITY");
	else
	    printf("\n  DESTINATION_SPECIFIC_SECURITY");
	if (vflag) {
	    len = *a++;
	    printf(" NSAP=");
	    a = idrp_addr_print(a, len);
	    /* TO_DO */
	}
	if (midfld)
	    printf(", ");
	return p;

      default:
	printf("\n Illegal distinguishing attribute %d", type);
	return p;
    }
    /*NOTREACHED*/
}

/*
 * print a path attribute
 */
static const u_char *
idrp_attribute_print(const u_char *p)
{
    register const u_char *a;
    register u_char flags, type, code, cnt, l;
    register u_short len, slen;
    u_char lenh, lenl;
    u_long err;

    flags = *p++;
    type = *p++;
    lenh = *p++;
    lenl = *p++;
    PACK(len, lenh, lenl);
    a = p;
    p += len;

    printf("\n %s%s%s ",
	   flags & IDRP_PAF_OPT ? "O" : "",
	   flags & IDRP_PAF_TRANS ? "T" : "",
	   flags & IDRP_PAF_PART ? "P" : "");

    switch (type) {
      /* ROUTE_ID */
      case IDRP_PAT_RT_ID:
	printf("ROUTE_ID=");
	printf("<%02x.%02x.%02x.%02x>", a[0], a[1], a[2], a[3]);
	return p;
	
      /* EXT_INFO */
      case IDRP_PAT_EXT_I:
	printf("EXT_INFO");
	return p;

      /* RD_PATH */
      case IDRP_PAT_RD_PA:
	printf("RD_PATH:");
	cnt = len;
	while (len > 0) {
	    code = *a++;
	    lenh = *a++;
	    lenl = *a++;
	    PACK(slen, lenh, lenl);
	    len -= 2 + slen;

	    switch (code) {
	      case IDRP_RD_SET:
		printf("\n  RD SET:");
		break;
	      case IDRP_RD_SEQ:
		printf("\n  RD SEQUENCE:");
		break;
	      case IDRP_ENTRY_SEQ:
		printf("\n  ENTRY SEQUENCE:");
		break;
	      case IDRP_ENTRY_SET:
		printf("\n  ENTRY SET:");
		break;
	      default:
		printf(" Unknown Segment Type %d", code);
		break;
	    }

	    while (slen > 0) {
		l = *a++;
		printf("\n   ");
		a = idrp_addr_print(a, l);
	    }
	}
	return p;

      /* NEXT_HOP */
      case IDRP_PAT_N_HOP:
	printf("NEXT_HOP");
	code = *a++;
	if (code == IDRP_NH_SERVA)
	    printf(" (Server Allowed) :");
	else
	    printf(" :");

	while (a < p) {
	    type = *a++;
	    l = *a++;
	    code = 0;
	    if ((type == IDRP_PT_9577) && (l == 1))
		code = *a++;
	    if (code == NLPI_CLNP) {
		/* CLNP */
		l = *a++;
		printf("\n  NET=");
		a = idrp_addr_print(a, l);
		if (vflag) {
		    cnt = *a++;
		    while (--cnt > 0) {
			l = *a++;
			printf("\n  SNPA=");
			a = idrp_snpa_print(a, l);
		    }
		} else
		    return p;
	    } else if (code == NLPI_DODIP) {
		/* IP */
		struct in_addr in;

		l = *a++;
		if (l != sizeof(struct in_addr))
		    return p;
		bcopy(a, (caddr_t)&in, sizeof(struct in_addr));
		printf("\n  IP=%s", inet_ntoa(in));
		a += sizeof(struct in_addr);
		if (vflag) {
                    cnt = *a++;
                    while (--cnt > 0) {
                        l = *a++;
                        printf("\n  SNPA=");
                        a = idrp_snpa_print(a, l);
                    }
                } else
                    return p;
	    } else
		return p;
	}
	/*NOTREACHED*/
	return p;

      /* DIST_LIST */
      case IDRP_PAT_DL_IN:
      case IDRP_PAT_DL_EX:
	if (type == IDRP_PAT_DL_IN)
	    printf("DIST_LIST_INCL :");
	else
	    printf("DIST_LIST_EXCL :");
	cnt = *a++;
	while (--cnt > 0) {
	    l = *a++;
	    printf("\n  RDI=");
	    a = idrp_addr_print(a, l);
	}
	return p;

      /* MULTI_EXIT_DISC */
      case IDRP_PAT_EXIT:
	printf("MULTI_EXIT_DISC");
	if (len != 1)
	    return p;
	code = *a++;
	printf(" %d", code);
	return p;

      /* LOCAL_PREF */
      case IDRP_PAT_PREF:
	printf("LOCAL_PREF");
	if (len != 1)
            return p;
	code = *a++;
	printf(" %d", code);
	return p;

      /* TRANSIT_DELAY */
      case IDRP_PAT_DELAY:
	printf("TRANSIT_DELAY");
	if (len != 2)
	    return p;
	lenh = *a++;
	lenl = *a++;
	PACK(slen, lenh, lenl);
	printf(" %d (*2ms)", slen);
	return p;

      /* RESIDUAL_ERROR */
      case IDRP_PAT_R_ERR:
	printf("RESIDUAL_ERROR");
	if (len != 4)
	    return p;
	lenh = *a++;
	lenl = *a++;
	cnt = *a++;
	l = *a++;
	PACK4(err, lenh, lenl, cnt, l);
	printf(" %d (/4,294,967,295)", err);
	return p;

      /* EXPENSE */
      case IDRP_PAT_EXPS:
	printf("EXPENSE");
	if (len != 2)
	    return p;
        lenh = *a++;
        lenl = *a++;
        PACK(slen, lenh, lenl);
	printf(" %d", slen);
	return p;

      /* SPECIFIC_QOS */
      case IDRP_PAT_S_QOS:
      case IDRP_PAT_D_QOS:
	if (type == IDRP_PAT_S_QOS)
	    printf("SOURCE_SPECIFIC_QOS");
	else
	    printf("DESTINATION_SPECIFIC_QOS");
	if (vflag) {
	    l = *a++;
	    printf("\n  NSAP=");
	    a = idrp_addr_print(a, l);
	    /* TO_DO */
	}
	return p;

      /* HIERARCHICAL_RECORDING */
      case IDRP_PAT_H_REC:
	printf("HIERARCHICAL_RECORDING");
	if (len != 1)
	    return p;
	code = *a++;
	printf(" %d", code);
	return p;

      /* RD_HOP_COUNT */
      case IDRP_PAT_RD_HC:
	printf("RD_HOP_COUNT");
	if (len != 1)
            return p;
        code = *a++;
        printf(" %d", code);
        return p;

      /* SPECIFIC_SECURITY */
      case IDRP_PAT_S_SEC:
      case IDRP_PAT_D_SEC:
	if (type == IDRP_PAT_S_SEC)
	    printf("SOURCE_SPECIFIC_SECURITY");
	else
	    printf("DESTINATION_SPECIFIC_SECURITY");
	l = *a++;
	printf("\n  NSAP=");
	a = idrp_addr_print(a, l);
	/* TO_DO */
	return p;

      /* CAPACITY */
      case IDRP_PAT_CAPA:
	printf("CAPACITY");
	if (len != 1)
	    return p;
	code = *a++;
	printf(" %d", code);
	return p;

      /* PRIORITY */
      case IDRP_PAT_PRI:
	printf("PRIORITY");
        if (len != 1)
            return p;
        code = *a++;
        printf(" %d", code);
        return p;

      default:
	printf("Unknown Path Attribute (type=%d, length=%d)", type, len);
	return p;
    }
#ifdef lint
    /*NOTREACHED*/
    return p;
#endif
}

/*
 * print a Network Layer Reachability Information
 */
static const u_char *
idrp_NLRI_print(const u_char *p)
{
    register const u_char *a;
    register u_char ptype, plen, len;
    register u_short alen;
    u_char pcode, alenh, alenl;

    ptype = *p++;
    plen = *p++;
    pcode = 0;

    if ((ptype == IDRP_PT_9577) && (plen == 1)) {
	pcode = *p++;
	if (pcode == NLPI_CLNP)
	    printf("protocol CLNP");
	else if (pcode == NLPI_DODIP)
	    printf("protocol DOD IP");
	else
	    printf("NLPI %02x", pcode);
    } else if (ptype == IDRP_PT_9577) {
	printf("NLPI ");
	p = idrp_addr_print(p, plen);
    } else if ((ptype == IDRP_PT_8802) && (plen == 1)) {
	printf("LSAP %02x", *p++);
    } else if (ptype == IDRP_PT_8802) {
	printf("LSAP ");
	p = idrp_addr_print(p, plen);
    } else {
	printf("Unknown proto type %d", ptype);
	p += plen;
    }

    alenh = *p++;
    alenl = *p++;
    PACK(alen, alenh, alenl);
    a = p;
    p += alen;
    if (vflag && (pcode != 0))
	while (alen > 0) {
	    len = *p++;
	    alen -= len;
	    if (pcode == NLPI_CLNP) {
		printf("\n  ");
		a = idrp_addr_print(a, len);
	    } else if ((pcode == NLPI_DODIP) &&
		       (len == sizeof(struct in_addr))) {
		struct in_addr in;

		bcopy(a, (caddr_t)&in, sizeof(struct in_addr));
		printf(" %s", inet_ntoa(in));
		a += sizeof(struct in_addr);
		if (alen > 0)
		    printf(",");
	    } else
		break;
	}
    return p;
}

/*
 * print a BISPDU
 */
void
idrp_print(const u_char *cp, int length)
{
    register const u_char *p = cp;
    register int len, cnt;

    (void)bzero((char *)&pkt, sizeof(struct idrp_packet));
    (void)bzero((char *)&ipa, sizeof(struct idrp_path_attr));

    if (vflag)
	printf("\n");
    else
	printf(" ");

    if (length < IDRP_HDR_SIZE)
	goto truncated;

    (void)bcopy(p, (char *)&pkt, IDRP_HDR_SIZE);
    PACK(pkt.ih.len, pkt.ih.lenh, pkt.ih.lenl);

    PACK(idrp_len, idrp_lenh, idrp_lenl);
    PACK4(idrp_seq, idrp_seq0, idrp_seq1, idrp_seq2, idrp_seq3);
    PACK4(idrp_ack, idrp_ack0, idrp_ack1, idrp_ack2, idrp_ack3);

    p += IDRP_HDR_SIZE;
    length -= IDRP_HDR_SIZE;

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

    if (idrp_nlpi != NLPI_IDRP) {
	printf("not an idrp packet ?");
	return;
    }
    if (idrp_len < IDRP_HDR_SIZE) {
	printf("idrp illegal length %d", idrp_len);
	return;
    }

    /* print BISPDU type */

    switch (idrp_type) {
      case IDRP_OPEN:
	printf("idrp Open BISPDU");
	break;
      case IDRP_UPDATE:
	printf("idrp Update BISPDU");
	break;
      case IDRP_ERROR:
	printf("idrp Error BISPDU");
        break;
      case IDRP_KEEPALIVE:
	printf("idrp Keepalive BISPDU");
        break;
      case IDRP_CEASE:
	printf("idrp Cease BISPDU");
        break;
      case IDRP_REFRESH:
	printf("idrp RIB Refresh BISPDU");
	break;
      default:
	printf("idrp unknown BISPDU %d", idrp_type);
	return;
    }

    if (qflag)
	return;

    printf("len %d seq %d ack %d credit <%d,%d>",
	   idrp_len, idrp_seq, idrp_ack, idrp_cdtoff, idrp_cdtavl);
    idrp_len -= IDRP_HDR_SIZE;

    if (((int)idrp_len > length) || (p + idrp_len > snapend))
	goto truncated;

    switch (idrp_type) {

      /* OPEN BISPDU */
      case IDRP_OPEN:
	bcopy(p, (caddr_t)&pkt.iun.io, sizeof(struct idrp_open));
	PACK(idrp_hold, idrp_holdh, idrp_holdl);
	PACK(idrp_maxsz, idrp_maxszh, idrp_maxszl);

	if (idrp_vers != IDRP_VERSION) {
	    printf(" bad version %d", idrp_vers);
	    return;
	}

	printf("\n hold=%ds", idrp_hold);

	if (idrp_maxsz < IDRP_MINMAX_SZ) {
	    printf(" illegal maximum PDU size %d", idrp_maxsz);
	    return;
	}
	p += 5;

	printf(" src RDI=");
	len = *p++;
	p = idrp_addr_print(p, len);

	cnt = *p++;
	while (--cnt >= 0) {
	    printf("\n Attribute Set=");
	    len = *p++;
	    while (--len >= 0)
		p = idrp_attrname_print(p, len > 1);
	}

	cnt = *p++;
	while (--cnt >= 0) {
	    printf("\n RDC=");
	    len = *p++;
	    p = idrp_addr_print(p, len);
	}
	return;

      /* UPDATE PDU */
      case IDRP_UPDATE:
	cnt = *p++;
	PACK(idrp_unrtcnt, cnt, *p++);
	if (cnt = idrp_unrtcnt) {
	    printf("\n (%d) withdrawn routes:", cnt);
	    while (--cnt >= 0) {
		printf("<%02x.%02x.%02x.%02x>", p[0], p[1], p[2], p[3]);
		p += 4;
		if (cnt > 1)
		    printf(", ");
	    }
	}

	cnt = *p++;
	PACK(idrp_tpalen, cnt, *p++);
	printf("\n Path Attributes (%d)", cnt = idrp_tpalen);
	while (--cnt >= 0) {
	    /* printf("\n  "); */
	    p = idrp_attribute_print(p);
	}

	while (p <= cp + idrp_len + IDRP_HDR_SIZE) {
	    printf("\n NLRI=");
	    p = idrp_NLRI_print(p);
	}
	return;

      /* ERROR PDU */
      case IDRP_ERROR:
	idrp_code = *p++;
	idrp_subcode = *p++;

	switch (idrp_code) {
	  case IDRP_ERR_OPEN:
	    printf("\n  OPEN_PDU_Error");
	    switch (idrp_subcode) {
	      case IDRP_ERO_VER:
		printf(" / Unsupported_Version_Number");
		return;
	      case IDRP_ERO_MAX:
		printf(" / Bad_Max_PDU_Size");
		return;
	      case IDRP_ERO_PEER:
		printf(" / Bad_Peer_RD");
                return;
              case IDRP_ERO_AUTHC:
		printf(" / Unsupported_Authentification_code");
                return;
              case IDRP_ERO_AUTHF:
		printf(" / Authentification_Failure");
                return;
              case IDRP_ERO_RIB:
		printf(" / Bad_RIB_AttsSet");
                return;
              case IDRP_ERO_RDC:
		printf(" / RDC_Mismatch");
		return;
	      default:
		printf(" / unknown subcode %d", idrp_subcode);
		return;
	    }
#ifdef lint
	    /*NOTREACHED*/
	    return;
#endif

	  case IDRP_ERR_UPDATE:
	    printf("\n UPDATE_PDU_Error");
	    switch (idrp_subcode) {
	      case IDRP_ERU_FORML:
		printf(" / Malformed_Attribute_List");
		return;
	      case IDRP_ERU_UWKA:
		printf(" / Unrecognized_Well-known_Attribute");
                return;
              case IDRP_ERU_MWKA:
                printf(" / Missing_Well-known_Attribute");
                return;
              case IDRP_ERU_ATTF:
                printf(" / Attribute_Flags_Error");
                return;
              case IDRP_ERU_ATTL:
                printf(" / Attribute_Length_Error");
                return;
              case IDRP_ERU_RDRL:
                printf(" / RD_Routeing_Loop");
                return;
              case IDRP_ERU_INHA:
                printf(" / Invalid_NEXT_HOP_Attribute");
                return;
              case IDRP_ERU_OPTA:
                printf(" / Optional_Attribute_Error");
                return;
              case IDRP_ERU_IREA:
                printf(" / Invalid_Reachability_Information");
                return;
              case IDRP_ERU_MRDC:
                printf(" / Misconfigured_RDCs");
                return;
              case IDRP_ERU_NLRI:
                printf(" / Malformed_NLRI");
                return;
	      default:
                printf(" / unknown subcode %d", idrp_subcode);
                return;
            }
#ifdef lint
	    /*NOTREACHED*/
            return;
#endif

          case IDRP_ERR_HOLD:
	    printf("\n Hold_Timer_Expired");
	    return;

	  case IDRP_ERR_FSM:
	    printf("\n FSM_Error / type=%d state=%d",
		   idrp_subcode >> 4, idrp_subcode & 0xf);
	    return;

	  case IDRP_ERR_RIB:
	    printf("\n RIB_REFRESH_PDU_Error");
	    switch (idrp_subcode) {
              case IDRP_ERRR_INV:
		printf(" / Invalid_OpCode");
		return;
	      case IDRP_ERRR_ATT:
		printf(" / Unsupported_RIB-Atts");
                return;
	      default:
                printf(" / unknown subcode %d", idrp_subcode);
                return;
	    }
#ifdef lint
            /*NOTREACHED*/
            return;
#endif

	  default:
	    printf("\n Unknown Error (code=%d / subcode=%d)",
		   idrp_code, idrp_subcode);
	    return;
	}
#ifdef lint
	/*NOTREACHED*/
	return;
#endif

      /* KEEPALIVE PDU */
      case IDRP_KEEPALIVE:
	return;

      /* CEASE PDU */
      case IDRP_CEASE:
	return;

      /* RIB REFRESH PDU */
      case IDRP_REFRESH:
	idrp_opcode = *p++;
	switch (idrp_opcode) {
	  case IDRP_RIB_REQ:
	    printf(" OpCode=RIB Refresh Request");
	    break;
	  case IDRP_RIB_START:
	    printf(" OpCode=RIB Refresh Start");
	    break;
	  case IDRP_RIB_END:
	    printf(" OpCode=RIB Refresh End");
	    break;
	  default:
	    printf(" Unknown OpCode %d", idrp_opcode);
	    return;
	}
	/* TO_DO ? */
	return;

      default:
	printf(" Unknown Type %d", idrp_type);
	return;
    }
#ifdef lint
    /*NOTREACHED*/
    return;
#endif

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