hncp_print_rec(netdissect_options *ndo,
               const u_char *cp, u_int length, int indent)
{
    const int in_line = is_in_line(ndo, indent);
    int first_one = 1;

    u_int i, t;

    uint32_t last_type_mask = 0xffffffffU;
    int last_type_count = -1;

    const u_char *tlv, *value;
    uint16_t type, bodylen;
    uint32_t type_mask;

    i = 0;
    while (i < length) {
        tlv = cp + i;

        if (!in_line) {
            ND_PRINT((ndo, "\n"));
            for (t = indent; t > 0; t--)
                ND_PRINT((ndo, "\t"));
        }

        ND_TCHECK2(*tlv, 4);
        if (i + 4 > length)
            goto invalid;

        type = EXTRACT_16BITS(tlv);
        bodylen = EXTRACT_16BITS(tlv + 2);
        value = tlv + 4;
        ND_TCHECK2(*value, bodylen);
        if (i + bodylen + 4 > length)
            goto invalid;

        type_mask =
            (type == 0)                   ? RANGE_DNCP_RESERVED:
            (44 <= type && type <= 511)   ? RANGE_HNCP_UNASSIGNED:
            (768 <= type && type <= 1023) ? RANGE_DNCP_PRIVATE_USE:
                                            RANGE_DNCP_FUTURE_USE;
        if (type == 6 || type == 7)
            type_mask = RANGE_DNCP_FUTURE_USE;

        /* defined types */
        {
            t = 0;
            while (1) {
                u_int key = type_values[t++].v;
                if (key > 0xffff)
                    break;
                if (key == type) {
                    type_mask = type;
                    break;
                }
            }
        }

        if (in_line) {
            if (last_type_mask == type_mask) {
                last_type_count++;
            } else {
                print_type_in_line(ndo, last_type_mask, last_type_count, indent, &first_one);
                last_type_mask = type_mask;
                last_type_count = 1;
            }

            goto skip_multiline;
        }

        ND_PRINT((ndo,"%s", tok2str(type_values, "Easter Egg (42)", type_mask) ));
        if (type_mask > 0xffff)
            ND_PRINT((ndo,": type=%u", type ));
        ND_PRINT((ndo," (%u)", bodylen + 4 ));

        switch (type_mask) {

        case DNCP_REQUEST_NETWORK_STATE: {
            if (bodylen != 0)
                ND_PRINT((ndo, " %s", istr));
        }
            break;

        case DNCP_REQUEST_NODE_STATE: {
            const char *node_identifier;
            if (bodylen != 4) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            node_identifier = format_nid(value);
            ND_PRINT((ndo, " NID: %s", node_identifier));
        }
            break;

        case DNCP_NODE_ENDPOINT: {
            const char *node_identifier;
            uint32_t endpoint_identifier;
            if (bodylen != 8) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            node_identifier = format_nid(value);
            endpoint_identifier = EXTRACT_32BITS(value + 4);
            ND_PRINT((ndo, " NID: %s EPID: %08x",
                node_identifier,
                endpoint_identifier
            ));
        }
            break;

        case DNCP_NETWORK_STATE: {
            uint64_t hash;
            if (bodylen != 8) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            hash = EXTRACT_64BITS(value);
            ND_PRINT((ndo, " hash: %016" PRIx64, hash));
        }
            break;

        case DNCP_NODE_STATE: {
            const char *node_identifier, *interval;
            uint32_t sequence_number;
            uint64_t hash;
            if (bodylen < 20) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            node_identifier = format_nid(value);
            sequence_number = EXTRACT_32BITS(value + 4);
            interval = format_interval(EXTRACT_32BITS(value + 8));
            hash = EXTRACT_64BITS(value + 12);
            ND_PRINT((ndo, " NID: %s seqno: %u %s hash: %016" PRIx64,
                node_identifier,
                sequence_number,
                interval,
                hash
            ));
            hncp_print_rec(ndo, value+20, bodylen-20, indent+1);
        }
            break;

        case DNCP_PEER: {
            const char *peer_node_identifier;
            uint32_t peer_endpoint_identifier, endpoint_identifier;
            if (bodylen != 12) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            peer_node_identifier = format_nid(value);
            peer_endpoint_identifier = EXTRACT_32BITS(value + 4);
            endpoint_identifier = EXTRACT_32BITS(value + 8);
            ND_PRINT((ndo, " Peer-NID: %s Peer-EPID: %08x Local-EPID: %08x",
                peer_node_identifier,
                peer_endpoint_identifier,
                endpoint_identifier
            ));
        }
            break;

        case DNCP_KEEP_ALIVE_INTERVAL: {
            uint32_t endpoint_identifier;
            const char *interval;
            if (bodylen < 8) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            endpoint_identifier = EXTRACT_32BITS(value);
            interval = format_interval(EXTRACT_32BITS(value + 4));
            ND_PRINT((ndo, " EPID: %08x Interval: %s",
                endpoint_identifier,
                interval
            ));
        }
            break;

        case DNCP_TRUST_VERDICT: {
            if (bodylen <= 36) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ND_PRINT((ndo, " Verdict: %u Fingerprint: %s Common Name: ",
                *value,
                format_256(value + 4)));
            safeputs(ndo, value + 36, bodylen - 36);
        }
            break;

        case HNCP_HNCP_VERSION: {
            uint16_t capabilities;
            uint8_t M, P, H, L;
            if (bodylen < 5) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            capabilities = EXTRACT_16BITS(value + 2);
            M = (uint8_t)((capabilities >> 12) & 0xf);
            P = (uint8_t)((capabilities >> 8) & 0xf);
            H = (uint8_t)((capabilities >> 4) & 0xf);
            L = (uint8_t)(capabilities & 0xf);
            ND_PRINT((ndo, " M: %u P: %u H: %u L: %u User-agent: ",
                M, P, H, L
            ));
            safeputs(ndo, value + 4, bodylen - 4);
        }
            break;

        case HNCP_EXTERNAL_CONNECTION: {
            /* Container TLV */
            hncp_print_rec(ndo, value, bodylen, indent+1);
        }
            break;

        case HNCP_DELEGATED_PREFIX: {
            int l;
            if (bodylen < 9 || bodylen < 9 + (value[8] + 7) / 8) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ND_PRINT((ndo, " VLSO: %s PLSO: %s Prefix: ",
                format_interval(EXTRACT_32BITS(value)),
                format_interval(EXTRACT_32BITS(value + 4))
            ));
            l = print_prefix(ndo, value + 8, bodylen - 8);
            if (l == -1) {
                ND_PRINT((ndo, "(length is invalid)"));
                break;
            }
            if (l < 0) {
                /*
                 * We've already checked that we've captured the
                 * entire TLV, based on its length, so this will
                 * either be -1, meaning "the prefix length is
                 * greater than the longest possible address of
                 * that type" (i.e., > 32 for IPv4 or > 128 for
                 * IPv6", or -3, meaning "the prefix runs past
                 * the end of the TLV".
                 */
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            l += 8 + (-l & 3);

            if (bodylen >= l)
                hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
        }
            break;

        case HNCP_PREFIX_POLICY: {
            uint8_t policy;
            int l;
            if (bodylen < 1) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            policy = value[0];
            ND_PRINT((ndo, " type: "));
            if (policy == 0) {
                if (bodylen != 1) {
                    ND_PRINT((ndo, " %s", istr));
                    break;
                }
                ND_PRINT((ndo, "Internet connectivity"));
            } else if (policy >= 1 && policy <= 128) {
                ND_PRINT((ndo, "Dest-Prefix: "));
                l = print_prefix(ndo, value, bodylen);
                if (l == -1) {
                    ND_PRINT((ndo, "(length is invalid)"));
                    break;
                }
                if (l < 0) {
                    /*
                     * We've already checked that we've captured the
                     * entire TLV, based on its length, so this will
                     * either be -1, meaning "the prefix length is
                     * greater than the longest possible address of
                     * that type" (i.e., > 32 for IPv4 or > 128 for
                     * IPv6", or -3, meaning "the prefix runs past
                     * the end of the TLV".
                     */
                    ND_PRINT((ndo, " %s", istr));
                    break;
                }
            } else if (policy == 129) {
                ND_PRINT((ndo, "DNS domain: "));
                print_dns_label(ndo, value+1, bodylen-1, 1);
            } else if (policy == 130) {
                ND_PRINT((ndo, "Opaque UTF-8: "));
                safeputs(ndo, value + 1, bodylen - 1);
            } else if (policy == 131) {
                if (bodylen != 1) {
                    ND_PRINT((ndo, " %s", istr));
                    break;
                }
                ND_PRINT((ndo, "Restrictive assignment"));
            } else if (policy >= 132) {
                ND_PRINT((ndo, "Unknown (%u)", policy)); /* Reserved for future additions */
            }
        }
            break;

        case HNCP_DHCPV4_DATA: {
            if (bodylen == 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            if (dhcpv4_print(ndo, value, bodylen, indent+1) != 0)
                goto invalid;
        }
            break;

        case HNCP_DHCPV6_DATA: {
            if (bodylen == 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            if (dhcpv6_print(ndo, value, bodylen, indent+1) != 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
        }
            break;

        case HNCP_ASSIGNED_PREFIX: {
            uint8_t prty;
            int l;
            if (bodylen < 6 || bodylen < 6 + (value[5] + 7) / 8) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            prty = (uint8_t)(value[4] & 0xf);
            ND_PRINT((ndo, " EPID: %08x Prty: %u",
                EXTRACT_32BITS(value),
                prty
            ));
            ND_PRINT((ndo, " Prefix: "));
            if ((l = print_prefix(ndo, value + 5, bodylen - 5)) < 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            l += 5;
            l += -l & 3;

            if (bodylen >= l)
                hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
        }
            break;

        case HNCP_NODE_ADDRESS: {
            uint32_t endpoint_identifier;
            const char *ip_address;
            if (bodylen < 20) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            endpoint_identifier = EXTRACT_32BITS(value);
            ip_address = format_ip6addr(ndo, value + 4);
            ND_PRINT((ndo, " EPID: %08x IP Address: %s",
                endpoint_identifier,
                ip_address
            ));

            hncp_print_rec(ndo, value + 20, bodylen - 20, indent+1);
        }
            break;

        case HNCP_DNS_DELEGATED_ZONE: {
            const char *ip_address;
            int len;
            if (bodylen < 17) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ip_address = format_ip6addr(ndo, value);
            ND_PRINT((ndo, " IP-Address: %s %c%c%c ",
                ip_address,
                (value[16] & 4) ? 'l' : '-',
                (value[16] & 2) ? 'b' : '-',
                (value[16] & 1) ? 's' : '-'
            ));
            len = print_dns_label(ndo, value+17, bodylen-17, 1);
            if (len < 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            len += 17;
            len += -len & 3;
            if (bodylen >= len)
                hncp_print_rec(ndo, value+len, bodylen-len, indent+1);
        }
            break;

        case HNCP_DOMAIN_NAME: {
            if (bodylen == 0) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ND_PRINT((ndo, " Domain: "));
            print_dns_label(ndo, value, bodylen, 1);
        }
            break;

        case HNCP_NODE_NAME: {
            u_int l;
            if (bodylen < 17) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            l = value[16];
            if (bodylen < 17 + l) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ND_PRINT((ndo, " IP-Address: %s Name: ",
                format_ip6addr(ndo, value)
            ));
            if (l < 64) {
                safeputchar(ndo, '"');
                safeputs(ndo, value + 17, l);
                safeputchar(ndo, '"');
            } else {
                ND_PRINT((ndo, "%s", istr));
            }
            l += 17;
            l += -l & 3;
            if (bodylen >= l)
                hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
        }
            break;

        case HNCP_MANAGED_PSK: {
            if (bodylen < 32) {
                ND_PRINT((ndo, " %s", istr));
                break;
            }
            ND_PRINT((ndo, " PSK: %s", format_256(value)));
            hncp_print_rec(ndo, value + 32, bodylen - 32, indent+1);
        }
            break;

        case RANGE_DNCP_RESERVED:
        case RANGE_HNCP_UNASSIGNED:
        case RANGE_DNCP_PRIVATE_USE:
        case RANGE_DNCP_FUTURE_USE:
            break;

        }
    skip_multiline:

        i += 4 + bodylen + (-bodylen & 3);
    }
    print_type_in_line(ndo, last_type_mask, last_type_count, indent, &first_one);

    return;

 trunc:
    ND_PRINT((ndo, "%s", "[|hncp]"));
    return;

 invalid:
    ND_PRINT((ndo, "%s", istr));
    return;
}