X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/63c261606326ad3ca01f57e006312b41e9857d7c..HEAD:/print-macsec.c diff --git a/print-macsec.c b/print-macsec.c index 1dc5d051..5fca3922 100644 --- a/print-macsec.c +++ b/print-macsec.c @@ -21,22 +21,14 @@ /* \summary: MACsec printer */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include -#include - -#include +#include "netdissect-stdinc.h" #include "netdissect.h" #include "addrtoname.h" -#include "ether.h" -#include "ethertype.h" #include "extract.h" -static const char tstr[] = "[|MACsec]"; - #define MACSEC_DEFAULT_ICV_LEN 16 /* Header format (SecTAG), following an Ethernet header @@ -78,40 +70,8 @@ struct macsec_sectag { #define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C) #define MACSEC_SL_MASK 0x3F /* short length */ -#define MACSEC_SECTAG_LEN_NOSCI 6 -#define MACSEC_SECTAG_LEN_SCI 14 -static int -ieee8021ae_sectag_len(netdissect_options *ndo, const struct macsec_sectag *sectag) -{ - return (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) ? - MACSEC_SECTAG_LEN_SCI : - MACSEC_SECTAG_LEN_NOSCI; -} - -static int macsec_check_length(netdissect_options *ndo, cconst struct macsec_sectag *sectag, u_int length, u_int caplen) -{ - u_int len; - - /* we need the full MACsec header in the capture */ - if (caplen < (MACSEC_SECTAG_LEN_NOSCI + 2)) - return 0; - - len = ieee8021ae_sectag_len(ndo, sectag); - if (caplen < (len + 2)) - return 0; - - if ((GET_U_1(sectag->short_length) & MACSEC_SL_MASK) != 0) { - /* original packet must have exact length */ - u_int exact = ETHER_HDRLEN + len + 2 + (GET_U_1(sectag->short_length) & MACSEC_SL_MASK); - return exact == length; - } else { - /* original packet must not be short */ - u_int minlen = ETHER_HDRLEN + len + 2 + 48; - return length >= minlen; - } - - return 1; -} +#define MACSEC_SECTAG_LEN_NOSCI 6 /* length of MACsec header without SCI */ +#define MACSEC_SECTAG_LEN_SCI 14 /* length of MACsec header with SCI */ #define SCI_FMT "%016" PRIx64 @@ -124,80 +84,172 @@ static const struct tok macsec_flag_values[] = { { 0, NULL } }; +static void +macsec_print_header(netdissect_options *ndo, + const struct macsec_sectag *sectag, + u_int short_length) +{ + ND_PRINT("an %u, pn %u, flags %s", + GET_U_1(sectag->tci_an) & MACSEC_AN_MASK, + GET_BE_U_4(sectag->packet_number), + bittok2str_nosep(macsec_flag_values, "none", + GET_U_1(sectag->tci_an) & MACSEC_TCI_FLAGS)); + + if (short_length != 0) + ND_PRINT(", sl %u", short_length); + + if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) + ND_PRINT(", sci " SCI_FMT, GET_BE_U_8(sectag->secure_channel_id)); + + ND_PRINT(", "); +} + /* returns < 0 iff the packet can be decoded completely */ -int macsec_print(netdissect_options *ndo, const u_char **bp, - u_int *lengthp, u_int *caplenp, u_int *hdrlenp, - u_short *length_type) +int +macsec_print(netdissect_options *ndo, const u_char **bp, + u_int *lengthp, u_int *caplenp, u_int *hdrlenp, + const struct lladdr_info *src, const struct lladdr_info *dst) { + const char *save_protocol; const u_char *p = *bp; u_int length = *lengthp; u_int caplen = *caplenp; u_int hdrlen = *hdrlenp; const struct macsec_sectag *sectag = (const struct macsec_sectag *)p; - u_int len; + u_int sectag_len; + u_int short_length; + + save_protocol = ndo->ndo_protocol; + ndo->ndo_protocol = "macsec"; - if (!macsec_check_length(sectag, length, caplen)) { - ND_PRINT((ndo, tstr)); + /* we need the full MACsec header in the capture */ + if (caplen < MACSEC_SECTAG_LEN_NOSCI) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; return hdrlen + caplen; } - - if (sectag->unused || sectag->tci_an & MACSEC_TCI_VERSION) { - ND_PRINT((ndo, "%s", istr)); + if (length < MACSEC_SECTAG_LEN_NOSCI) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; return hdrlen + caplen; } - if (ndo->ndo_eflag) { - char buf[128]; - int n = snprintf(buf, sizeof(buf), "an %u, pn %u, flags %s", - GET_U_1(sectag->tci_an) & MACSEC_AN_MASK, - GET_BE_U_4(sectag->packet_number), - bittok2str_nosep(macsec_flag_values, "none", - GET_U_1(sectag->tci_an) & MACSEC_TCI_FLAGS)); - if (n < 0) + if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) { + sectag_len = MACSEC_SECTAG_LEN_SCI; + if (caplen < MACSEC_SECTAG_LEN_SCI) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; return hdrlen + caplen; - - - if (sectag->short_length) { - int r = snprintf(buf + n, sizeof(buf) - n, ", sl %u", - GET_U_1(sectag->short_length) & MACSEC_SL_MASK); - if (r < 0) - return hdrlen + caplen; - n += r; } - - if (sectag->tci_an & MACSEC_TCI_SC) { - uint64_t sci; - int r; - sci = GET_BE_U_8(sectag->secure_channel_id); - r = snprintf(buf + n, sizeof(buf) - n, ", sci " SCI_FMT, sci); - if (r < 0) - return hdrlen + caplen; - n += r; + if (length < MACSEC_SECTAG_LEN_SCI) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; } + } else + sectag_len = MACSEC_SECTAG_LEN_NOSCI; - ND_PRINT((ndo, "%s, ", buf)); + if ((GET_U_1(sectag->short_length) & ~MACSEC_SL_MASK) != 0 || + GET_U_1(sectag->tci_an) & MACSEC_TCI_VERSION) { + nd_print_invalid(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; } - len = ieee8021ae_sectag_len(ndo, sectag); - *length_type = GET_BE_U_2(*bp + len); - if (ndo->ndo_eflag && *length_type > ETHERMTU && !(sectag->tci_an & MACSEC_TCI_E)) - ND_PRINT((ndo, "ethertype %s, ", tok2str(ethertype_values,"0x%04x", *length_type))); + short_length = GET_U_1(sectag->short_length) & MACSEC_SL_MASK; + if (ndo->ndo_eflag) + macsec_print_header(ndo, sectag, short_length); + + /* Skip the MACsec header. */ + *bp += sectag_len; + *hdrlenp += sectag_len; + + /* Remove it from the lengths, as it's been processed. */ + *lengthp -= sectag_len; + *caplenp -= sectag_len; if ((GET_U_1(sectag->tci_an) & MACSEC_TCI_CONFID)) { - *bp += len; - *hdrlenp += len; + /* + * The payload is encrypted. Print link-layer + * information, if it hasn't already been printed. + */ + if (!ndo->ndo_eflag) { + /* + * Nobody printed the link-layer addresses, + * so print them, if we have any. + */ + if (src != NULL && dst != NULL) { + ND_PRINT("%s > %s ", + (src->addr_string)(ndo, src->addr), + (dst->addr_string)(ndo, dst->addr)); + } + + ND_PRINT("802.1AE MACsec, "); + + /* + * Print the MACsec header. + */ + macsec_print_header(ndo, sectag, short_length); + } - *lengthp -= len; - *caplenp -= len; + /* + * Tell our caller it can't be dissected. + */ + ndo->ndo_protocol = save_protocol; return 0; - } else { - len += 2; - *bp += len; - *hdrlenp += len; - - len += MACSEC_DEFAULT_ICV_LEN; - *lengthp -= len; - *caplenp -= len; - return -1; } + + /* + * The payload isn't encrypted; remove the + * ICV length from the lengths, so our caller + * doesn't treat it as payload. + */ + if (*lengthp < MACSEC_DEFAULT_ICV_LEN) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; + } + if (*caplenp < MACSEC_DEFAULT_ICV_LEN) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; + } + *lengthp -= MACSEC_DEFAULT_ICV_LEN; + *caplenp -= MACSEC_DEFAULT_ICV_LEN; + /* + * Update the snapend thus the ICV field is not in the payload for + * the caller. + * The ICV (Integrity Check Value) is at the end of the frame, after + * the secure data. + */ + ndo->ndo_snapend -= MACSEC_DEFAULT_ICV_LEN; + + /* + * If the SL field is non-zero, then it's the length of the + * Secure Data; otherwise, the Secure Data is what's left + * ver after the MACsec header and ICV are removed. + */ + if (short_length != 0) { + /* + * If the short length is more than we *have*, + * that's an error. + */ + if (short_length > *lengthp) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; + } + if (short_length > *caplenp) { + nd_print_trunc(ndo); + ndo->ndo_protocol = save_protocol; + return hdrlen + caplen; + } + if (*lengthp > short_length) + *lengthp = short_length; + if (*caplenp > short_length) + *caplenp = short_length; + } + + ndo->ndo_protocol = save_protocol; + return -1; }