You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
8.8 KiB
371 lines
8.8 KiB
/* |
|
* 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. |
|
* |
|
* Original code by Hannes Gredler (hannes@juniper.net) |
|
*/ |
|
|
|
/* \summary: Resource Public Key Infrastructure (RPKI) to Router Protocol printer */ |
|
|
|
/* specification: RFC 6810 */ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
|
#endif |
|
|
|
#include <netdissect-stdinc.h> |
|
|
|
#include <string.h> |
|
|
|
#include "netdissect.h" |
|
#include "extract.h" |
|
#include "addrtoname.h" |
|
|
|
static const char tstr[] = "[|RPKI-RTR]"; |
|
|
|
/* |
|
* 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 int |
|
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 = (const 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: |
|
{ |
|
const rpki_rtr_pdu_ipv4_prefix *pdu; |
|
|
|
pdu = (const 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; |
|
|
|
case RPKI_RTR_IPV6_PREFIX_PDU: |
|
{ |
|
const rpki_rtr_pdu_ipv6_prefix *pdu; |
|
|
|
pdu = (const 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; |
|
|
|
case RPKI_RTR_ERROR_REPORT_PDU: |
|
{ |
|
const rpki_rtr_pdu_error_report *pdu; |
|
u_int encapsulated_pdu_length, text_length, tlen, error_code; |
|
|
|
pdu = (const 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))); |
|
if (rpki_rtr_pdu_print(ndo, tptr, indent+2)) |
|
goto trunc; |
|
} |
|
|
|
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))); |
|
if (fn_printn(ndo, tptr, text_length, ndo->ndo_snapend)) |
|
goto trunc; |
|
} |
|
} |
|
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 0; |
|
|
|
trunc: |
|
return 1; |
|
} |
|
|
|
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 = (const 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. |
|
*/ |
|
if (rpki_rtr_pdu_print(ndo, tptr, 8)) |
|
goto trunc; |
|
|
|
tlen -= pdu_len; |
|
tptr += pdu_len; |
|
} |
|
return; |
|
trunc: |
|
ND_PRINT((ndo, "\n\t%s", tstr)); |
|
} |
|
|
|
/* |
|
* Local Variables: |
|
* c-style: whitesmith |
|
* c-basic-offset: 4 |
|
* End: |
|
*/
|
|
|