+#define CLNP_PDU_ER 1
+#define CLNP_PDU_DT 28
+#define CLNP_PDU_MD 29
+#define CLNP_PDU_ERQ 30
+#define CLNP_PDU_ERP 31
+
+static const struct tok clnp_pdu_values[] = {
+ { CLNP_PDU_ER, "Error Report"},
+ { CLNP_PDU_MD, "MD"},
+ { CLNP_PDU_DT, "Data"},
+ { CLNP_PDU_ERQ, "Echo Request"},
+ { CLNP_PDU_ERP, "Echo Response"},
+ { 0, NULL }
+};
+
+struct clnp_header_t {
+ uint8_t nlpid;
+ uint8_t length_indicator;
+ uint8_t version;
+ uint8_t lifetime; /* units of 500ms */
+ uint8_t type;
+ uint8_t segment_length[2];
+ uint8_t cksum[2];
+};
+
+struct clnp_segment_header_t {
+ uint8_t data_unit_id[2];
+ uint8_t segment_offset[2];
+ uint8_t total_length[2];
+};
+
+/*
+ * clnp_print
+ * Decode CLNP packets. Return 0 on error.
+ */
+
+static int
+clnp_print(netdissect_options *ndo,
+ const uint8_t *pptr, u_int length)
+{
+ const uint8_t *optr,*source_address,*dest_address;
+ u_int li,tlen,nsap_offset,source_address_length,dest_address_length, clnp_pdu_type, clnp_flags;
+ const struct clnp_header_t *clnp_header;
+ const struct clnp_segment_header_t *clnp_segment_header;
+ uint8_t rfd_error_major,rfd_error_minor;
+
+ clnp_header = (const struct clnp_header_t *) pptr;
+ ND_TCHECK(*clnp_header);
+
+ li = clnp_header->length_indicator;
+ optr = pptr;
+
+ if (!ndo->ndo_eflag)
+ ND_PRINT((ndo, "CLNP"));
+
+ /*
+ * Sanity checking of the header.
+ */
+
+ if (clnp_header->version != CLNP_VERSION) {
+ ND_PRINT((ndo, "version %d packet not supported", clnp_header->version));
+ return (0);
+ }
+
+ /* FIXME further header sanity checking */
+
+ clnp_pdu_type = clnp_header->type & CLNP_PDU_TYPE_MASK;
+ clnp_flags = clnp_header->type & CLNP_FLAG_MASK;
+
+ pptr += sizeof(struct clnp_header_t);
+ li -= sizeof(struct clnp_header_t);
+ dest_address_length = *pptr;
+ dest_address = pptr + 1;
+
+ pptr += (1 + dest_address_length);
+ li -= (1 + dest_address_length);
+ source_address_length = *pptr;
+ source_address = pptr +1;
+
+ pptr += (1 + source_address_length);
+ li -= (1 + source_address_length);
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT((ndo, "%s%s > %s, %s, length %u",
+ ndo->ndo_eflag ? "" : ", ",
+ isonsap_string(source_address, source_address_length),
+ isonsap_string(dest_address, dest_address_length),
+ tok2str(clnp_pdu_values,"unknown (%u)",clnp_pdu_type),
+ length));
+ return (1);
+ }
+ ND_PRINT((ndo, "%slength %u", ndo->ndo_eflag ? "" : ", ", length));
+
+ ND_PRINT((ndo, "\n\t%s PDU, hlen: %u, v: %u, lifetime: %u.%us, Segment PDU length: %u, checksum: 0x%04x",
+ tok2str(clnp_pdu_values, "unknown (%u)",clnp_pdu_type),
+ clnp_header->length_indicator,
+ clnp_header->version,
+ clnp_header->lifetime/2,
+ (clnp_header->lifetime%2)*5,
+ EXTRACT_16BITS(clnp_header->segment_length),
+ EXTRACT_16BITS(clnp_header->cksum)));
+
+ osi_print_cksum(ndo, optr, EXTRACT_16BITS(clnp_header->cksum), 7,
+ clnp_header->length_indicator);
+
+ ND_PRINT((ndo, "\n\tFlags [%s]",
+ bittok2str(clnp_flag_values, "none", clnp_flags)));
+
+ ND_PRINT((ndo, "\n\tsource address (length %u): %s\n\tdest address (length %u): %s",
+ source_address_length,
+ isonsap_string(source_address, source_address_length),
+ dest_address_length,
+ isonsap_string(dest_address, dest_address_length)));
+
+ if (clnp_flags & CLNP_SEGMENT_PART) {
+ clnp_segment_header = (const struct clnp_segment_header_t *) pptr;
+ ND_TCHECK(*clnp_segment_header);
+ ND_PRINT((ndo, "\n\tData Unit ID: 0x%04x, Segment Offset: %u, Total PDU Length: %u",
+ EXTRACT_16BITS(clnp_segment_header->data_unit_id),
+ EXTRACT_16BITS(clnp_segment_header->segment_offset),
+ EXTRACT_16BITS(clnp_segment_header->total_length)));
+ pptr+=sizeof(const struct clnp_segment_header_t);
+ li-=sizeof(const struct clnp_segment_header_t);
+ }
+
+ /* now walk the options */
+ while (li >= 2) {
+ u_int op, opli;
+ const uint8_t *tptr;
+
+ ND_TCHECK2(*pptr, 2);
+ if (li < 2) {
+ ND_PRINT((ndo, ", bad opts/li"));
+ return (0);
+ }
+ op = *pptr++;
+ opli = *pptr++;
+ li -= 2;
+ ND_TCHECK2(*pptr, opli);
+ if (opli > li) {
+ ND_PRINT((ndo, ", opt (%d) too long", op));
+ return (0);
+ }
+ li -= opli;
+ tptr = pptr;
+ tlen = opli;
+
+ ND_PRINT((ndo, "\n\t %s Option #%u, length %u, value: ",
+ tok2str(clnp_option_values,"Unknown",op),
+ op,
+ opli));
+
+ switch (op) {
+
+
+ case CLNP_OPTION_ROUTE_RECORDING: /* those two options share the format */
+ case CLNP_OPTION_SOURCE_ROUTING:
+ ND_PRINT((ndo, "%s %s",
+ tok2str(clnp_option_sr_rr_values,"Unknown",*tptr),
+ tok2str(clnp_option_sr_rr_string_values, "Unknown Option %u", op)));
+ nsap_offset=*(tptr+1);
+ if (nsap_offset == 0) {
+ ND_PRINT((ndo, " Bad NSAP offset (0)"));
+ break;
+ }
+ nsap_offset-=1; /* offset to nsap list */
+ if (nsap_offset > tlen) {
+ ND_PRINT((ndo, " Bad NSAP offset (past end of option)"));
+ break;
+ }
+ tptr+=nsap_offset;
+ tlen-=nsap_offset;
+ while (tlen > 0) {
+ source_address_length=*tptr;
+ if (tlen < source_address_length+1) {
+ ND_PRINT((ndo, "\n\t NSAP address goes past end of option"));
+ break;
+ }
+ if (source_address_length > 0) {
+ source_address=(tptr+1);
+ ND_TCHECK2(*source_address, source_address_length);
+ ND_PRINT((ndo, "\n\t NSAP address (length %u): %s",
+ source_address_length,
+ isonsap_string(source_address, source_address_length)));
+ }
+ tlen-=source_address_length+1;
+ }
+ break;
+
+ case CLNP_OPTION_PRIORITY:
+ ND_PRINT((ndo, "0x%1x", *tptr&0x0f));
+ break;
+
+ case CLNP_OPTION_QOS_MAINTENANCE:
+ ND_PRINT((ndo, "\n\t Format Code: %s",
+ tok2str(clnp_option_scope_values, "Reserved", *tptr&CLNP_OPTION_SCOPE_MASK)));
+
+ if ((*tptr&CLNP_OPTION_SCOPE_MASK) == CLNP_OPTION_SCOPE_GLOBAL)
+ ND_PRINT((ndo, "\n\t QoS Flags [%s]",
+ bittok2str(clnp_option_qos_global_values,
+ "none",
+ *tptr&CLNP_OPTION_OPTION_QOS_MASK)));
+ break;
+
+ case CLNP_OPTION_SECURITY:
+ ND_PRINT((ndo, "\n\t Format Code: %s, Security-Level %u",
+ tok2str(clnp_option_scope_values,"Reserved",*tptr&CLNP_OPTION_SCOPE_MASK),
+ *(tptr+1)));
+ break;
+
+ case CLNP_OPTION_DISCARD_REASON:
+ rfd_error_major = (*tptr&0xf0) >> 4;
+ rfd_error_minor = *tptr&0x0f;
+ ND_PRINT((ndo, "\n\t Class: %s Error (0x%01x), %s (0x%01x)",
+ tok2str(clnp_option_rfd_class_values,"Unknown",rfd_error_major),
+ rfd_error_major,
+ tok2str(clnp_option_rfd_error_class[rfd_error_major],"Unknown",rfd_error_minor),
+ rfd_error_minor));
+ break;
+
+ case CLNP_OPTION_PADDING:
+ ND_PRINT((ndo, "padding data"));
+ break;
+
+ /*
+ * FIXME those are the defined Options that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ default:
+ print_unknown_data(ndo, tptr, "\n\t ", opli);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, pptr, "\n\t ", opli);
+ pptr += opli;
+ }
+
+ switch (clnp_pdu_type) {
+
+ case CLNP_PDU_ER: /* fall through */
+ case CLNP_PDU_ERP:
+ ND_TCHECK(*pptr);
+ if (*(pptr) == NLPID_CLNP) {
+ ND_PRINT((ndo, "\n\t-----original packet-----\n\t"));
+ /* FIXME recursion protection */
+ clnp_print(ndo, pptr, length - clnp_header->length_indicator);
+ break;
+ }
+
+ case CLNP_PDU_DT:
+ case CLNP_PDU_MD:
+ case CLNP_PDU_ERQ:
+
+ default:
+ /* dump the PDU specific data */
+ if (length-(pptr-optr) > 0) {
+ ND_PRINT((ndo, "\n\t undecoded non-header data, length %u", length-clnp_header->length_indicator));
+ print_unknown_data(ndo, pptr, "\n\t ", length - (pptr - optr));
+ }
+ }
+
+ return (1);
+
+ trunc:
+ ND_PRINT((ndo, "[|clnp]"));
+ return (1);
+
+}
+
+
+#define ESIS_PDU_REDIRECT 6
+#define ESIS_PDU_ESH 2
+#define ESIS_PDU_ISH 4