nexmon – Rev 1

Subversion Repositories:
Rev:
/*
 * Copyright (c) 1998-2011 The TCPDUMP project
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code
 * distributions retain the above copyright notice and this paragraph
 * in its entirety, and (2) distributions including binary code include
 * the above copyright notice and this paragraph in its entirety in
 * the documentation or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE.
 *
 * support for the The RPKI/Router Protocol as RFC6810
 *
 * Original code by Hannes Gredler (hannes@juniper.net)
 */

#define NETDISSECT_REWORKED
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <tcpdump-stdinc.h>

#include <string.h>

#include "interface.h"
#include "extract.h"
#include "addrtoname.h"

/*
 * RPKI/Router PDU header
 *
 * Here's what the PDU header looks like.
 * The length does include the version and length fields.
 */
typedef struct rpki_rtr_pdu_ {
    u_char version;             /* Version number */
    u_char pdu_type;            /* PDU type */
    union {
        u_char session_id[2];   /* Session id */
        u_char error_code[2];   /* Error code */
    } u;
    u_char length[4];
} rpki_rtr_pdu;
#define RPKI_RTR_PDU_OVERHEAD (offsetof(rpki_rtr_pdu, rpki_rtr_pdu_msg))

/*
 * IPv4 Prefix PDU.
 */
typedef struct rpki_rtr_pdu_ipv4_prefix_ {
    rpki_rtr_pdu pdu_header;
    u_char flags;
    u_char prefix_length;
    u_char max_length;
    u_char zero;
    u_char prefix[4];
    u_char as[4];
} rpki_rtr_pdu_ipv4_prefix;

/*
 * IPv6 Prefix PDU.
 */
typedef struct rpki_rtr_pdu_ipv6_prefix_ {
    rpki_rtr_pdu pdu_header;
    u_char flags;
    u_char prefix_length;
    u_char max_length;
    u_char zero;
    u_char prefix[16];
    u_char as[4];
} rpki_rtr_pdu_ipv6_prefix;

/*
 * Error report PDU.
 */
typedef struct rpki_rtr_pdu_error_report_ {
    rpki_rtr_pdu pdu_header;
    u_char encapsulated_pdu_length[4]; /* Encapsulated PDU length */
} rpki_rtr_pdu_error_report;

/*
 * PDU type codes
 */
#define RPKI_RTR_SERIAL_NOTIFY_PDU      0
#define RPKI_RTR_SERIAL_QUERY_PDU       1
#define RPKI_RTR_RESET_QUERY_PDU        2
#define RPKI_RTR_CACHE_RESPONSE_PDU     3
#define RPKI_RTR_IPV4_PREFIX_PDU        4
#define RPKI_RTR_IPV6_PREFIX_PDU        6
#define RPKI_RTR_END_OF_DATA_PDU        7
#define RPKI_RTR_CACHE_RESET_PDU        8
#define RPKI_RTR_ERROR_REPORT_PDU       10

static const struct tok rpki_rtr_pdu_values[] = {
    { RPKI_RTR_SERIAL_NOTIFY_PDU, "Serial Notify" },
    { RPKI_RTR_SERIAL_QUERY_PDU, "Serial Query" },
    { RPKI_RTR_RESET_QUERY_PDU, "Reset Query" },
    { RPKI_RTR_CACHE_RESPONSE_PDU, "Cache Response" },
    { RPKI_RTR_IPV4_PREFIX_PDU, "IPV4 Prefix" },
    { RPKI_RTR_IPV6_PREFIX_PDU, "IPV6 Prefix" },
    { RPKI_RTR_END_OF_DATA_PDU, "End of Data" },
    { RPKI_RTR_CACHE_RESET_PDU, "Cache Reset" },
    { RPKI_RTR_ERROR_REPORT_PDU, "Error Report" },
    { 0, NULL}
};

static const struct tok rpki_rtr_error_codes[] = {
    { 0, "Corrupt Data" },
    { 1, "Internal Error" },
    { 2, "No Data Available" },
    { 3, "Invalid Request" },
    { 4, "Unsupported Protocol Version" },
    { 5, "Unsupported PDU Type" },
    { 6, "Withdrawal of Unknown Record" },
    { 7, "Duplicate Announcement Received" },
    { 0, NULL}
};

/*
 * Build a indentation string for a given indentation level.
 * XXX this should be really in util.c
 */
static char *
indent_string (u_int indent)
{
    static char buf[20];
    u_int idx;

    idx = 0;
    buf[idx] = '\0';

    /*
     * Does the static buffer fit ?
     */
    if (sizeof(buf) < ((indent/8) + (indent %8) + 2)) {
        return buf;
    }

    /*
     * Heading newline.
     */
    buf[idx] = '\n';
    idx++;

    while (indent >= 8) {
        buf[idx] = '\t';
        idx++;
        indent -= 8;
    }

    while (indent > 0) {
        buf[idx] = ' ';
        idx++;
        indent--;
    }

    /*
     * Trailing zero.
     */
    buf[idx] = '\0';

    return buf;
}

/*
 * Print a single PDU.
 */
static void
rpki_rtr_pdu_print (netdissect_options *ndo, const u_char *tptr, u_int indent)
{
    const rpki_rtr_pdu *pdu_header;
    u_int pdu_type, pdu_len, hexdump;
    const u_char *msg;

    pdu_header = (rpki_rtr_pdu *)tptr;
    pdu_type = pdu_header->pdu_type;
    pdu_len = EXTRACT_32BITS(pdu_header->length);
    ND_TCHECK2(*tptr, pdu_len);
    hexdump = FALSE;

    ND_PRINT((ndo, "%sRPKI-RTRv%u, %s PDU (%u), length: %u",
           indent_string(8),
           pdu_header->version,
           tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type),
           pdu_type, pdu_len));

    switch (pdu_type) {

        /*
         * The following PDUs share the message format.
         */
    case RPKI_RTR_SERIAL_NOTIFY_PDU:
    case RPKI_RTR_SERIAL_QUERY_PDU:
    case RPKI_RTR_END_OF_DATA_PDU:
        msg = (const u_char *)(pdu_header + 1);
        ND_PRINT((ndo, "%sSession ID: 0x%04x, Serial: %u",
               indent_string(indent+2),
               EXTRACT_16BITS(pdu_header->u.session_id),
               EXTRACT_32BITS(msg)));
        break;

        /*
         * The following PDUs share the message format.
         */
    case RPKI_RTR_RESET_QUERY_PDU:
    case RPKI_RTR_CACHE_RESET_PDU:

        /*
         * Zero payload PDUs.
         */
        break;

    case RPKI_RTR_CACHE_RESPONSE_PDU:
        ND_PRINT((ndo, "%sSession ID: 0x%04x",
               indent_string(indent+2),
               EXTRACT_16BITS(pdu_header->u.session_id)));
        break;

    case RPKI_RTR_IPV4_PREFIX_PDU:
        {
            rpki_rtr_pdu_ipv4_prefix *pdu;

            pdu = (rpki_rtr_pdu_ipv4_prefix *)tptr;
            ND_PRINT((ndo, "%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
                   indent_string(indent+2),
                   ipaddr_string(ndo, pdu->prefix),
                   pdu->prefix_length, pdu->max_length,
                   EXTRACT_32BITS(pdu->as), pdu->flags));
        }
        break;

#ifdef INET6
    case RPKI_RTR_IPV6_PREFIX_PDU:
        {
            rpki_rtr_pdu_ipv6_prefix *pdu;

            pdu = (rpki_rtr_pdu_ipv6_prefix *)tptr;
            ND_PRINT((ndo, "%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
                   indent_string(indent+2),
                   ip6addr_string(ndo, pdu->prefix),
                   pdu->prefix_length, pdu->max_length,
                   EXTRACT_32BITS(pdu->as), pdu->flags));
        }
        break;
#endif

    case RPKI_RTR_ERROR_REPORT_PDU:
        {
            rpki_rtr_pdu_error_report *pdu;
            u_int encapsulated_pdu_length, text_length, tlen, error_code;

            pdu = (rpki_rtr_pdu_error_report *)tptr;
            encapsulated_pdu_length = EXTRACT_32BITS(pdu->encapsulated_pdu_length);
            ND_TCHECK2(*tptr, encapsulated_pdu_length);
            tlen = pdu_len;

            error_code = EXTRACT_16BITS(pdu->pdu_header.u.error_code);
            ND_PRINT((ndo, "%sError code: %s (%u), Encapsulated PDU length: %u",
                   indent_string(indent+2),
                   tok2str(rpki_rtr_error_codes, "Unknown", error_code),
                   error_code, encapsulated_pdu_length));

            tptr += sizeof(*pdu);
            tlen -= sizeof(*pdu);

            /*
             * Recurse if there is an encapsulated PDU.
             */
            if (encapsulated_pdu_length &&
                (encapsulated_pdu_length <= tlen)) {
                ND_PRINT((ndo, "%s-----encapsulated PDU-----", indent_string(indent+4)));
                rpki_rtr_pdu_print(ndo, tptr, indent+2);
            }

            tptr += encapsulated_pdu_length;
            tlen -= encapsulated_pdu_length;

            /*
             * Extract, trail-zero and print the Error message.
             */
            text_length = 0;
            if (tlen > 4) {
                text_length = EXTRACT_32BITS(tptr);
                tptr += 4;
                tlen -= 4;
            }
            ND_TCHECK2(*tptr, text_length);
            if (text_length && (text_length <= tlen )) {
                ND_PRINT((ndo, "%sError text: ", indent_string(indent+2)));
                fn_printn(ndo, tptr, text_length, ndo->ndo_snapend);
            }
        }
        break;

    default:

        /*
         * Unknown data, please hexdump.
         */
        hexdump = TRUE;
    }

    /* do we also want to see a hex dump ? */
    if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
        print_unknown_data(ndo,tptr,"\n\t  ", pdu_len);
    }
    return;

 trunc:
    ND_PRINT((ndo, "|trunc"));
    return;
}

void
rpki_rtr_print(netdissect_options *ndo, register const u_char *pptr, register u_int len)
{
    u_int tlen, pdu_type, pdu_len;
    const u_char *tptr;
    const rpki_rtr_pdu *pdu_header;

    tptr = pptr;
    tlen = len;

    if (!ndo->ndo_vflag) {
        ND_PRINT((ndo, ", RPKI-RTR"));
        return;
    }

    while (tlen >= sizeof(rpki_rtr_pdu)) {

        ND_TCHECK2(*tptr, sizeof(rpki_rtr_pdu));

        pdu_header = (rpki_rtr_pdu *)tptr;
        pdu_type = pdu_header->pdu_type;
        pdu_len = EXTRACT_32BITS(pdu_header->length);
        ND_TCHECK2(*tptr, pdu_len);

        /* infinite loop check */
        if (!pdu_type || !pdu_len) {
            break;
        }

        if (tlen < pdu_len) {
            goto trunc;
        }

        /*
         * Print the PDU.
         */
        rpki_rtr_pdu_print(ndo, tptr, 8);

        tlen -= pdu_len;
        tptr += pdu_len;
    }
    return;
 trunc:
    ND_PRINT((ndo, "\n\t[|RPKI-RTR]"));
}

/*
 * Local Variables:
 * c-style: whitesmith
 * c-basic-offset: 4
 * End:
 */