/* Copyright (c) 2017, Sabrina Dubroca * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * 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. */ /* \summary: MACsec printer */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "netdissect.h" #include "addrtoname.h" #include "ethertype.h" #include "extract.h" #define MACSEC_DEFAULT_ICV_LEN 16 /* Header format (SecTAG), following an Ethernet header * IEEE 802.1AE-2006 9.3 * * +---------------------------------+----------------+----------------+ * | (MACsec ethertype) | TCI_AN | SL | * +---------------------------------+----------------+----------------+ * | Packet Number | * +-------------------------------------------------------------------+ * | Secure Channel Identifier | * | (optional) | * +-------------------------------------------------------------------+ * * MACsec ethertype = 0x88e5 * TCI: Tag Control Information, set of flags * AN: association number, 2 bits * SL (short length): 6-bit length of the protected payload, if < 48 * Packet Number: 32-bits packet identifier * Secure Channel Identifier: 64-bit unique identifier, usually * composed of a MAC address + 16-bit port number */ struct macsec_sectag { nd_uint8_t tci_an; nd_uint8_t short_length; nd_uint32_t packet_number; nd_uint8_t secure_channel_id[8]; /* optional */ }; /* IEEE 802.1AE-2006 9.5 */ #define MACSEC_TCI_VERSION 0x80 #define MACSEC_TCI_ES 0x40 /* end station */ #define MACSEC_TCI_SC 0x20 /* SCI present */ #define MACSEC_TCI_SCB 0x10 /* epon */ #define MACSEC_TCI_E 0x08 /* encryption */ #define MACSEC_TCI_C 0x04 /* changed text */ #define MACSEC_AN_MASK 0x03 /* association number */ #define MACSEC_TCI_FLAGS (MACSEC_TCI_ES | MACSEC_TCI_SC | MACSEC_TCI_SCB | MACSEC_TCI_E | MACSEC_TCI_C) #define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C) #define MACSEC_SL_MASK 0x3F /* short length */ #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 static const struct tok macsec_flag_values[] = { { MACSEC_TCI_E, "E" }, { MACSEC_TCI_C, "C" }, { MACSEC_TCI_ES, "S" }, { MACSEC_TCI_SCB, "B" }, { MACSEC_TCI_SC, "I" }, { 0, NULL } }; /* 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) { 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 sectag_len; save_protocol = ndo->ndo_protocol; ndo->ndo_protocol = "MACsec"; /* 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 (length < MACSEC_SECTAG_LEN_NOSCI) { nd_print_trunc(ndo); ndo->ndo_protocol = save_protocol; return hdrlen + caplen; } 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 (length < MACSEC_SECTAG_LEN_SCI) { nd_print_trunc(ndo); ndo->ndo_protocol = save_protocol; return hdrlen + caplen; } } else sectag_len = MACSEC_SECTAG_LEN_NOSCI; 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; } if (ndo->ndo_eflag) { 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 ((GET_U_1(sectag->short_length) & MACSEC_SL_MASK) != 0) ND_PRINT(", sl %u", GET_U_1(sectag->short_length) & MACSEC_SL_MASK); 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(", "); } /* 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)) { /* * The payload is encrypted. Tell our * caller it can't be dissected. */ ndo->ndo_protocol = save_protocol; return 0; } else { /* * 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) *lengthp -= MACSEC_DEFAULT_ICV_LEN; if (*caplenp >= MACSEC_DEFAULT_ICV_LEN) *caplenp -= MACSEC_DEFAULT_ICV_LEN; ndo->ndo_protocol = save_protocol; return -1; } }