+ /* 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
+
+static const struct tok esis_pdu_values[] = {
+ { ESIS_PDU_REDIRECT, "redirect"},
+ { ESIS_PDU_ESH, "ESH"},
+ { ESIS_PDU_ISH, "ISH"},
+ { 0, NULL }
+};
+
+struct esis_header_t {
+ uint8_t nlpid;
+ uint8_t length_indicator;
+ uint8_t version;
+ uint8_t reserved;
+ uint8_t type;
+ uint8_t holdtime[2];
+ uint8_t cksum[2];
+};
+
+static void
+esis_print(netdissect_options *ndo,
+ const uint8_t *pptr, u_int length)
+{
+ const uint8_t *optr;
+ u_int li,esis_pdu_type,source_address_length, source_address_number;
+ const struct esis_header_t *esis_header;
+
+ if (!ndo->ndo_eflag)
+ ND_PRINT((ndo, "ES-IS"));
+
+ if (length <= 2) {
+ ND_PRINT((ndo, ndo->ndo_qflag ? "bad pkt!" : "no header at all!"));
+ return;
+ }
+
+ esis_header = (const struct esis_header_t *) pptr;
+ ND_TCHECK(*esis_header);
+ li = esis_header->length_indicator;
+ optr = pptr;
+
+ /*
+ * Sanity checking of the header.
+ */
+
+ if (esis_header->nlpid != NLPID_ESIS) {
+ ND_PRINT((ndo, " nlpid 0x%02x packet not supported", esis_header->nlpid));
+ return;
+ }
+
+ if (esis_header->version != ESIS_VERSION) {
+ ND_PRINT((ndo, " version %d packet not supported", esis_header->version));
+ return;
+ }
+
+ if (li > length) {
+ ND_PRINT((ndo, " length indicator(%d) > PDU size (%d)!", li, length));
+ return;
+ }
+
+ if (li < sizeof(struct esis_header_t) + 2) {
+ ND_PRINT((ndo, " length indicator < min PDU size %d:", li));
+ while (pptr < ndo->ndo_snapend)
+ ND_PRINT((ndo, "%02X", *pptr++));
+ return;
+ }
+
+ esis_pdu_type = esis_header->type & ESIS_PDU_TYPE_MASK;
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT((ndo, "%s%s, length %u",
+ ndo->ndo_eflag ? "" : ", ",
+ tok2str(esis_pdu_values,"unknown type (%u)",esis_pdu_type),
+ length));
+ return;
+ } else
+ ND_PRINT((ndo, "%slength %u\n\t%s (%u)",
+ ndo->ndo_eflag ? "" : ", ",
+ length,
+ tok2str(esis_pdu_values,"unknown type: %u", esis_pdu_type),
+ esis_pdu_type));
+
+ ND_PRINT((ndo, ", v: %u%s", esis_header->version, esis_header->version == ESIS_VERSION ? "" : "unsupported" ));
+ ND_PRINT((ndo, ", checksum: 0x%04x", EXTRACT_16BITS(esis_header->cksum)));
+
+ osi_print_cksum(ndo, pptr, EXTRACT_16BITS(esis_header->cksum), 7, li);
+
+ ND_PRINT((ndo, ", holding time: %us, length indicator: %u",
+ EXTRACT_16BITS(esis_header->holdtime), li));
+
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, optr, "\n\t", sizeof(struct esis_header_t));
+
+ pptr += sizeof(struct esis_header_t);
+ li -= sizeof(struct esis_header_t);
+
+ switch (esis_pdu_type) {
+ case ESIS_PDU_REDIRECT: {
+ const uint8_t *dst, *snpa, *neta;
+ u_int dstl, snpal, netal;
+
+ ND_TCHECK(*pptr);
+ if (li < 1) {
+ ND_PRINT((ndo, ", bad redirect/li"));
+ return;
+ }
+ dstl = *pptr;
+ pptr++;
+ li--;
+ ND_TCHECK2(*pptr, dstl);
+ if (li < dstl) {
+ ND_PRINT((ndo, ", bad redirect/li"));
+ return;
+ }
+ dst = pptr;
+ pptr += dstl;
+ li -= dstl;
+ ND_PRINT((ndo, "\n\t %s", isonsap_string(dst, dstl)));
+
+ ND_TCHECK(*pptr);
+ if (li < 1) {
+ ND_PRINT((ndo, ", bad redirect/li"));
+ return;
+ }
+ snpal = *pptr;
+ pptr++;
+ li--;
+ ND_TCHECK2(*pptr, snpal);
+ if (li < snpal) {
+ ND_PRINT((ndo, ", bad redirect/li"));
+ return;
+ }
+ snpa = pptr;
+ pptr += snpal;
+ li -= snpal;
+ ND_TCHECK(*pptr);
+ if (li < 1) {
+ ND_PRINT((ndo, ", bad redirect/li"));
+ return;