1 /* Copyright (c) 2017, Sabrina Dubroca <sd@queasysnail.net> 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in 11 * the documentation and/or other materials provided with the 12 * distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior 15 * written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20 */ 21 22 /* \summary: MACsec printer */ 23 24 #include <config.h> 25 26 #include "netdissect-stdinc.h" 27 28 #include "netdissect.h" 29 #include "addrtoname.h" 30 #include "extract.h" 31 32 #define MACSEC_DEFAULT_ICV_LEN 16 33 34 /* Header format (SecTAG), following an Ethernet header 35 * IEEE 802.1AE-2006 9.3 36 * 37 * +---------------------------------+----------------+----------------+ 38 * | (MACsec ethertype) | TCI_AN | SL | 39 * +---------------------------------+----------------+----------------+ 40 * | Packet Number | 41 * +-------------------------------------------------------------------+ 42 * | Secure Channel Identifier | 43 * | (optional) | 44 * +-------------------------------------------------------------------+ 45 * 46 * MACsec ethertype = 0x88e5 47 * TCI: Tag Control Information, set of flags 48 * AN: association number, 2 bits 49 * SL (short length): 6-bit length of the protected payload, if < 48 50 * Packet Number: 32-bits packet identifier 51 * Secure Channel Identifier: 64-bit unique identifier, usually 52 * composed of a MAC address + 16-bit port number 53 */ 54 struct macsec_sectag { 55 nd_uint8_t tci_an; 56 nd_uint8_t short_length; 57 nd_uint32_t packet_number; 58 nd_uint8_t secure_channel_id[8]; /* optional */ 59 }; 60 61 /* IEEE 802.1AE-2006 9.5 */ 62 #define MACSEC_TCI_VERSION 0x80 63 #define MACSEC_TCI_ES 0x40 /* end station */ 64 #define MACSEC_TCI_SC 0x20 /* SCI present */ 65 #define MACSEC_TCI_SCB 0x10 /* epon */ 66 #define MACSEC_TCI_E 0x08 /* encryption */ 67 #define MACSEC_TCI_C 0x04 /* changed text */ 68 #define MACSEC_AN_MASK 0x03 /* association number */ 69 #define MACSEC_TCI_FLAGS (MACSEC_TCI_ES | MACSEC_TCI_SC | MACSEC_TCI_SCB | MACSEC_TCI_E | MACSEC_TCI_C) 70 #define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C) 71 #define MACSEC_SL_MASK 0x3F /* short length */ 72 73 #define MACSEC_SECTAG_LEN_NOSCI 6 /* length of MACsec header without SCI */ 74 #define MACSEC_SECTAG_LEN_SCI 14 /* length of MACsec header with SCI */ 75 76 #define SCI_FMT "%016" PRIx64 77 78 static const struct tok macsec_flag_values[] = { 79 { MACSEC_TCI_E, "E" }, 80 { MACSEC_TCI_C, "C" }, 81 { MACSEC_TCI_ES, "S" }, 82 { MACSEC_TCI_SCB, "B" }, 83 { MACSEC_TCI_SC, "I" }, 84 { 0, NULL } 85 }; 86 87 static void macsec_print_header(netdissect_options *ndo, 88 const struct macsec_sectag *sectag, 89 u_int short_length) 90 { 91 ND_PRINT("an %u, pn %u, flags %s", 92 GET_U_1(sectag->tci_an) & MACSEC_AN_MASK, 93 GET_BE_U_4(sectag->packet_number), 94 bittok2str_nosep(macsec_flag_values, "none", 95 GET_U_1(sectag->tci_an) & MACSEC_TCI_FLAGS)); 96 97 if (short_length != 0) 98 ND_PRINT(", sl %u", short_length); 99 100 if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) 101 ND_PRINT(", sci " SCI_FMT, GET_BE_U_8(sectag->secure_channel_id)); 102 103 ND_PRINT(", "); 104 } 105 106 /* returns < 0 iff the packet can be decoded completely */ 107 int macsec_print(netdissect_options *ndo, const u_char **bp, 108 u_int *lengthp, u_int *caplenp, u_int *hdrlenp, 109 const struct lladdr_info *src, const struct lladdr_info *dst) 110 { 111 const char *save_protocol; 112 const u_char *p = *bp; 113 u_int length = *lengthp; 114 u_int caplen = *caplenp; 115 u_int hdrlen = *hdrlenp; 116 const struct macsec_sectag *sectag = (const struct macsec_sectag *)p; 117 u_int sectag_len; 118 u_int short_length; 119 120 save_protocol = ndo->ndo_protocol; 121 ndo->ndo_protocol = "macsec"; 122 123 /* we need the full MACsec header in the capture */ 124 if (caplen < MACSEC_SECTAG_LEN_NOSCI) { 125 nd_print_trunc(ndo); 126 ndo->ndo_protocol = save_protocol; 127 return hdrlen + caplen; 128 } 129 if (length < MACSEC_SECTAG_LEN_NOSCI) { 130 nd_print_trunc(ndo); 131 ndo->ndo_protocol = save_protocol; 132 return hdrlen + caplen; 133 } 134 135 if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) { 136 sectag_len = MACSEC_SECTAG_LEN_SCI; 137 if (caplen < MACSEC_SECTAG_LEN_SCI) { 138 nd_print_trunc(ndo); 139 ndo->ndo_protocol = save_protocol; 140 return hdrlen + caplen; 141 } 142 if (length < MACSEC_SECTAG_LEN_SCI) { 143 nd_print_trunc(ndo); 144 ndo->ndo_protocol = save_protocol; 145 return hdrlen + caplen; 146 } 147 } else 148 sectag_len = MACSEC_SECTAG_LEN_NOSCI; 149 150 if ((GET_U_1(sectag->short_length) & ~MACSEC_SL_MASK) != 0 || 151 GET_U_1(sectag->tci_an) & MACSEC_TCI_VERSION) { 152 nd_print_invalid(ndo); 153 ndo->ndo_protocol = save_protocol; 154 return hdrlen + caplen; 155 } 156 157 short_length = GET_U_1(sectag->short_length) & MACSEC_SL_MASK; 158 if (ndo->ndo_eflag) 159 macsec_print_header(ndo, sectag, short_length); 160 161 /* Skip the MACsec header. */ 162 *bp += sectag_len; 163 *hdrlenp += sectag_len; 164 165 /* Remove it from the lengths, as it's been processed. */ 166 *lengthp -= sectag_len; 167 *caplenp -= sectag_len; 168 169 if ((GET_U_1(sectag->tci_an) & MACSEC_TCI_CONFID)) { 170 /* 171 * The payload is encrypted. Print link-layer 172 * information, if it hasn't already been printed. 173 */ 174 if (!ndo->ndo_eflag) { 175 /* 176 * Nobody printed the link-layer addresses, 177 * so print them, if we have any. 178 */ 179 if (src != NULL && dst != NULL) { 180 ND_PRINT("%s > %s ", 181 (src->addr_string)(ndo, src->addr), 182 (dst->addr_string)(ndo, dst->addr)); 183 } 184 185 ND_PRINT("802.1AE MACsec, "); 186 187 /* 188 * Print the MACsec header. 189 */ 190 macsec_print_header(ndo, sectag, short_length); 191 } 192 193 /* 194 * Tell our caller it can't be dissected. 195 */ 196 ndo->ndo_protocol = save_protocol; 197 return 0; 198 } 199 200 /* 201 * The payload isn't encrypted; remove the 202 * ICV length from the lengths, so our caller 203 * doesn't treat it as payload. 204 */ 205 if (*lengthp < MACSEC_DEFAULT_ICV_LEN) { 206 nd_print_trunc(ndo); 207 ndo->ndo_protocol = save_protocol; 208 return hdrlen + caplen; 209 } 210 if (*caplenp < MACSEC_DEFAULT_ICV_LEN) { 211 nd_print_trunc(ndo); 212 ndo->ndo_protocol = save_protocol; 213 return hdrlen + caplen; 214 } 215 *lengthp -= MACSEC_DEFAULT_ICV_LEN; 216 *caplenp -= MACSEC_DEFAULT_ICV_LEN; 217 /* 218 * Update the snapend thus the ICV field is not in the payload for 219 * the caller. 220 * The ICV (Integrity Check Value) is at the end of the frame, after 221 * the secure data. 222 */ 223 ndo->ndo_snapend -= MACSEC_DEFAULT_ICV_LEN; 224 225 /* 226 * If the SL field is non-zero, then it's the length of the 227 * Secure Data; otherwise, the Secure Data is what's left 228 * ver after the MACsec header and ICV are removed. 229 */ 230 if (short_length != 0) { 231 /* 232 * If the short length is more than we *have*, 233 * that's an error. 234 */ 235 if (short_length > *lengthp) { 236 nd_print_trunc(ndo); 237 ndo->ndo_protocol = save_protocol; 238 return hdrlen + caplen; 239 } 240 if (short_length > *caplenp) { 241 nd_print_trunc(ndo); 242 ndo->ndo_protocol = save_protocol; 243 return hdrlen + caplen; 244 } 245 if (*lengthp > short_length) 246 *lengthp = short_length; 247 if (*caplenp > short_length) 248 *caplenp = short_length; 249 } 250 251 ndo->ndo_protocol = save_protocol; 252 return -1; 253 } 254