1 1.1 christos /* 2 1.1 christos * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997 3 1.1 christos * The Regents of the University of California. All rights reserved. 4 1.1 christos * 5 1.1 christos * Redistribution and use in source and binary forms, with or without 6 1.1 christos * modification, are permitted provided that: (1) source code distributions 7 1.1 christos * retain the above copyright notice and this paragraph in its entirety, (2) 8 1.1 christos * distributions including binary code include the above copyright notice and 9 1.1 christos * this paragraph in its entirety in the documentation or other materials 10 1.1 christos * provided with the distribution, and (3) all advertising materials mentioning 11 1.1 christos * features or use of this software display the following acknowledgement: 12 1.1 christos * ``This product includes software developed by the University of California, 13 1.1 christos * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 14 1.1 christos * the University nor the names of its contributors may be used to endorse 15 1.1 christos * or promote products derived from this software without specific prior 16 1.1 christos * written permission. 17 1.1 christos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 18 1.1 christos * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 19 1.1 christos * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20 1.1 christos * 21 1.1 christos * Code by Gert Doering, SpaceNet GmbH, gert (at) space.net 22 1.1 christos * 23 1.1 christos * Reference documentation: 24 1.9 christos * https://web.archive.org/web/20000914194913/http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.pdf 25 1.1 christos */ 26 1.1 christos 27 1.2 christos #include <sys/cdefs.h> 28 1.1 christos #ifndef lint 29 1.10 christos __RCSID("$NetBSD: print-cdp.c,v 1.10 2024/09/02 16:15:30 christos Exp $"); 30 1.1 christos #endif 31 1.1 christos 32 1.8 spz /* \summary: Cisco Discovery Protocol (CDP) printer */ 33 1.8 spz 34 1.9 christos #include <config.h> 35 1.1 christos 36 1.9 christos #include "netdissect-stdinc.h" 37 1.1 christos 38 1.1 christos #include <string.h> 39 1.1 christos 40 1.9 christos #define ND_LONGJMP_FROM_TCHECK 41 1.7 christos #include "netdissect.h" 42 1.1 christos #include "addrtoname.h" 43 1.7 christos #include "extract.h" 44 1.1 christos #include "nlpid.h" 45 1.1 christos 46 1.5 christos 47 1.6 christos #define CDP_HEADER_LEN 4 48 1.6 christos #define CDP_HEADER_VERSION_OFFSET 0 49 1.6 christos #define CDP_HEADER_TTL_OFFSET 1 50 1.6 christos #define CDP_HEADER_CHECKSUM_OFFSET 2 51 1.6 christos 52 1.6 christos #define CDP_TLV_HEADER_LEN 4 53 1.6 christos #define CDP_TLV_TYPE_OFFSET 0 54 1.6 christos #define CDP_TLV_LEN_OFFSET 2 55 1.1 christos 56 1.4 christos static const struct tok cdp_capability_values[] = { 57 1.1 christos { 0x01, "Router" }, 58 1.1 christos { 0x02, "Transparent Bridge" }, 59 1.1 christos { 0x04, "Source Route Bridge" }, 60 1.1 christos { 0x08, "L2 Switch" }, 61 1.1 christos { 0x10, "L3 capable" }, 62 1.1 christos { 0x20, "IGMP snooping" }, 63 1.1 christos { 0x40, "L1 capable" }, 64 1.1 christos { 0, NULL } 65 1.1 christos }; 66 1.1 christos 67 1.9 christos static void cdp_print_addr(netdissect_options *, const u_char *, u_int); 68 1.9 christos static void cdp_print_prefixes(netdissect_options *, const u_char *, u_int); 69 1.9 christos 70 1.9 christos static void 71 1.9 christos cdp_print_string(netdissect_options *ndo, 72 1.9 christos const u_char *cp, const u_int len) 73 1.9 christos { 74 1.9 christos ND_PRINT("'"); 75 1.9 christos (void)nd_printn(ndo, cp, len, NULL); 76 1.9 christos ND_PRINT("'"); 77 1.9 christos } 78 1.9 christos 79 1.9 christos static void 80 1.9 christos cdp_print_power(netdissect_options *ndo, 81 1.9 christos const u_char *cp, const u_int len) 82 1.9 christos { 83 1.9 christos u_int val = 0; 84 1.9 christos 85 1.9 christos switch (len) { 86 1.9 christos case 1: 87 1.9 christos val = GET_U_1(cp); 88 1.9 christos break; 89 1.9 christos case 2: 90 1.9 christos val = GET_BE_U_2(cp); 91 1.9 christos break; 92 1.9 christos case 3: 93 1.9 christos val = GET_BE_U_3(cp); 94 1.9 christos break; 95 1.9 christos } 96 1.9 christos ND_PRINT("%1.2fW", val / 1000.0); 97 1.9 christos } 98 1.9 christos 99 1.9 christos static void 100 1.9 christos cdp_print_capability(netdissect_options *ndo, 101 1.9 christos const u_char *cp, const u_int len _U_) 102 1.9 christos { 103 1.9 christos uint32_t val = GET_BE_U_4(cp); 104 1.9 christos 105 1.9 christos ND_PRINT("(0x%08x): %s", val, 106 1.9 christos bittok2str(cdp_capability_values, "none", val)); 107 1.9 christos } 108 1.9 christos 109 1.9 christos /* Rework the version string to get a nice indentation. */ 110 1.9 christos static void 111 1.9 christos cdp_print_version(netdissect_options *ndo, 112 1.9 christos const u_char *cp, const u_int len) 113 1.9 christos { 114 1.9 christos unsigned i; 115 1.9 christos 116 1.9 christos ND_PRINT("\n\t "); 117 1.9 christos for (i = 0; i < len; i++) { 118 1.9 christos u_char c = GET_U_1(cp + i); 119 1.9 christos 120 1.9 christos if (c == '\n') 121 1.9 christos ND_PRINT("\n\t "); 122 1.9 christos else 123 1.9 christos fn_print_char(ndo, c); 124 1.9 christos } 125 1.9 christos } 126 1.9 christos 127 1.9 christos static void 128 1.9 christos cdp_print_uint16(netdissect_options *ndo, 129 1.9 christos const u_char *cp, const u_int len _U_) 130 1.9 christos { 131 1.9 christos ND_PRINT("%u", GET_BE_U_2(cp)); 132 1.9 christos } 133 1.9 christos 134 1.9 christos static void 135 1.9 christos cdp_print_duplex(netdissect_options *ndo, 136 1.9 christos const u_char *cp, const u_int len _U_) 137 1.9 christos { 138 1.9 christos ND_PRINT("%s", GET_U_1(cp) ? "full": "half"); 139 1.9 christos } 140 1.9 christos 141 1.9 christos /* https://www.cisco.com/c/en/us/td/docs/voice_ip_comm/cata/186/2_12_m/english/release/notes/186rn21m.html 142 1.9 christos * plus more details from other sources 143 1.9 christos * 144 1.9 christos * There are apparently versions of the request with both 145 1.9 christos * 2 bytes and 3 bytes of value. The 3 bytes of value 146 1.9 christos * appear to be a 1-byte application type followed by a 147 1.9 christos * 2-byte VLAN ID; the 2 bytes of value are unknown 148 1.9 christos * (they're 0x20 0x00 in some captures I've seen; that 149 1.9 christos * is not a valid VLAN ID, as VLAN IDs are 12 bits). 150 1.9 christos * 151 1.9 christos * The replies all appear to be 3 bytes long. 152 1.9 christos */ 153 1.9 christos static void 154 1.9 christos cdp_print_ata186(netdissect_options *ndo, 155 1.9 christos const u_char *cp, const u_int len) 156 1.9 christos { 157 1.9 christos if (len == 2) 158 1.9 christos ND_PRINT("unknown 0x%04x", GET_BE_U_2(cp)); 159 1.9 christos else 160 1.9 christos ND_PRINT("app %u, vlan %u", GET_U_1(cp), GET_BE_U_2(cp + 1)); 161 1.9 christos } 162 1.9 christos 163 1.9 christos static void 164 1.9 christos cdp_print_mtu(netdissect_options *ndo, 165 1.9 christos const u_char *cp, const u_int len _U_) 166 1.9 christos { 167 1.9 christos ND_PRINT("%u bytes", GET_BE_U_4(cp)); 168 1.9 christos } 169 1.9 christos 170 1.9 christos static void 171 1.9 christos cdp_print_uint8x(netdissect_options *ndo, 172 1.9 christos const u_char *cp, const u_int len _U_) 173 1.9 christos { 174 1.9 christos ND_PRINT("0x%02x", GET_U_1(cp)); 175 1.9 christos } 176 1.9 christos 177 1.9 christos static void 178 1.9 christos cdp_print_phys_loc(netdissect_options *ndo, 179 1.9 christos const u_char *cp, const u_int len) 180 1.9 christos { 181 1.9 christos ND_PRINT("0x%02x", GET_U_1(cp)); 182 1.9 christos if (len > 1) { 183 1.9 christos ND_PRINT("/"); 184 1.9 christos (void)nd_printn(ndo, cp + 1, len - 1, NULL); 185 1.9 christos } 186 1.9 christos } 187 1.9 christos 188 1.9 christos struct cdp_tlvinfo { 189 1.9 christos const char *name; 190 1.9 christos void (*printer)(netdissect_options *ndo, const u_char *, u_int); 191 1.9 christos int min_len, max_len; 192 1.9 christos }; 193 1.9 christos 194 1.9 christos #define T_DEV_ID 0x01 195 1.9 christos #define T_MAX 0x17 196 1.9 christos static const struct cdp_tlvinfo cdptlvs[T_MAX + 1] = { 197 1.9 christos /* 0x00 */ 198 1.9 christos [ T_DEV_ID ] = { "Device-ID", cdp_print_string, -1, -1 }, 199 1.9 christos [ 0x02 ] = { "Address", cdp_print_addr, -1, -1 }, 200 1.9 christos [ 0x03 ] = { "Port-ID", cdp_print_string, -1, -1 }, 201 1.9 christos [ 0x04 ] = { "Capability", cdp_print_capability, 4, 4 }, 202 1.9 christos [ 0x05 ] = { "Version String", cdp_print_version, -1, -1 }, 203 1.9 christos [ 0x06 ] = { "Platform", cdp_print_string, -1, -1 }, 204 1.9 christos [ 0x07 ] = { "Prefixes", cdp_print_prefixes, -1, -1 }, 205 1.9 christos /* not documented */ 206 1.9 christos [ 0x08 ] = { "Protocol-Hello option", NULL, -1, -1 }, 207 1.9 christos /* CDPv2 */ 208 1.9 christos [ 0x09 ] = { "VTP Management Domain", cdp_print_string, -1, -1 }, 209 1.9 christos /* CDPv2 */ 210 1.9 christos [ 0x0a ] = { "Native VLAN ID", cdp_print_uint16, 2, 2 }, 211 1.9 christos /* CDPv2 */ 212 1.9 christos [ 0x0b ] = { "Duplex", cdp_print_duplex, 1, 1 }, 213 1.9 christos /* 0x0c */ 214 1.9 christos /* 0x0d */ 215 1.9 christos /* incomplete doc. */ 216 1.9 christos [ 0x0e ] = { "ATA-186 VoIP VLAN assignment", cdp_print_ata186, 3, 3 }, 217 1.9 christos /* incomplete doc. */ 218 1.9 christos [ 0x0f ] = { "ATA-186 VoIP VLAN request", cdp_print_ata186, 2, 3 }, 219 1.9 christos /* not documented */ 220 1.9 christos [ 0x10 ] = { "power consumption", cdp_print_power, 1, 3 }, 221 1.9 christos /* not documented */ 222 1.9 christos [ 0x11 ] = { "MTU", cdp_print_mtu, 4, 4 }, 223 1.9 christos /* not documented */ 224 1.9 christos [ 0x12 ] = { "AVVID trust bitmap", cdp_print_uint8x, 1, 1 }, 225 1.9 christos /* not documented */ 226 1.9 christos [ 0x13 ] = { "AVVID untrusted ports CoS", cdp_print_uint8x, 1, 1 }, 227 1.9 christos /* not documented */ 228 1.9 christos [ 0x14 ] = { "System Name", cdp_print_string, -1, -1 }, 229 1.9 christos /* not documented */ 230 1.9 christos [ 0x15 ] = { "System Object ID (not decoded)", NULL, -1, -1 }, 231 1.9 christos [ 0x16 ] = { "Management Addresses", cdp_print_addr, 4, -1 }, 232 1.9 christos /* not documented */ 233 1.9 christos [ 0x17 ] = { "Physical Location", cdp_print_phys_loc, 1, -1 }, 234 1.9 christos }; 235 1.1 christos 236 1.1 christos void 237 1.5 christos cdp_print(netdissect_options *ndo, 238 1.9 christos const u_char *tptr, u_int length) 239 1.1 christos { 240 1.9 christos u_int orig_length = length; 241 1.9 christos uint16_t checksum; 242 1.1 christos 243 1.9 christos ndo->ndo_protocol = "cdp"; 244 1.9 christos 245 1.9 christos if (length < CDP_HEADER_LEN) { 246 1.9 christos ND_PRINT(" (packet length %u < %u)", length, CDP_HEADER_LEN); 247 1.9 christos goto invalid; 248 1.1 christos } 249 1.9 christos ND_PRINT("CDPv%u, ttl: %us", 250 1.9 christos GET_U_1(tptr + CDP_HEADER_VERSION_OFFSET), 251 1.9 christos GET_U_1(tptr + CDP_HEADER_TTL_OFFSET)); 252 1.9 christos checksum = GET_BE_U_2(tptr + CDP_HEADER_CHECKSUM_OFFSET); 253 1.5 christos if (ndo->ndo_vflag) 254 1.9 christos ND_PRINT(", checksum: 0x%04x (unverified), length %u", 255 1.9 christos checksum, orig_length); 256 1.1 christos tptr += CDP_HEADER_LEN; 257 1.9 christos length -= CDP_HEADER_LEN; 258 1.1 christos 259 1.9 christos while (length) { 260 1.9 christos u_int type, len; 261 1.9 christos const struct cdp_tlvinfo *info; 262 1.9 christos const char *name; 263 1.9 christos u_char covered = 0; 264 1.9 christos 265 1.9 christos if (length < CDP_TLV_HEADER_LEN) { 266 1.9 christos ND_PRINT(" (remaining packet length %u < %u)", 267 1.9 christos length, CDP_TLV_HEADER_LEN); 268 1.9 christos goto invalid; 269 1.9 christos } 270 1.9 christos type = GET_BE_U_2(tptr + CDP_TLV_TYPE_OFFSET); 271 1.9 christos len = GET_BE_U_2(tptr + CDP_TLV_LEN_OFFSET); /* object length includes the 4 bytes header length */ 272 1.9 christos info = type <= T_MAX ? &cdptlvs[type] : NULL; 273 1.9 christos name = (info && info->name) ? info->name : "unknown field type"; 274 1.6 christos if (len < CDP_TLV_HEADER_LEN) { 275 1.9 christos if (ndo->ndo_vflag) 276 1.9 christos ND_PRINT("\n\t%s (0x%02x), TLV length: %u byte%s (too short)", 277 1.9 christos name, type, len, PLURAL_SUFFIX(len)); 278 1.9 christos else 279 1.9 christos ND_PRINT(", %s TLV length %u too short", 280 1.9 christos name, len); 281 1.9 christos goto invalid; 282 1.9 christos } 283 1.9 christos if (len > length) { 284 1.9 christos ND_PRINT(" (TLV length %u > %u)", len, length); 285 1.9 christos goto invalid; 286 1.6 christos } 287 1.6 christos tptr += CDP_TLV_HEADER_LEN; 288 1.9 christos length -= CDP_TLV_HEADER_LEN; 289 1.6 christos len -= CDP_TLV_HEADER_LEN; 290 1.1 christos 291 1.9 christos /* In non-verbose mode just print Device-ID. */ 292 1.9 christos if (!ndo->ndo_vflag && type == T_DEV_ID) 293 1.9 christos ND_PRINT(", Device-ID "); 294 1.9 christos else if (ndo->ndo_vflag) 295 1.9 christos ND_PRINT("\n\t%s (0x%02x), value length: %u byte%s: ", 296 1.9 christos name, type, len, PLURAL_SUFFIX(len)); 297 1.9 christos 298 1.9 christos if (info) { 299 1.9 christos if ((info->min_len > 0 && len < (unsigned)info->min_len) || 300 1.9 christos (info->max_len > 0 && len > (unsigned)info->max_len)) 301 1.9 christos ND_PRINT(" (malformed TLV)"); 302 1.9 christos else if (ndo->ndo_vflag || type == T_DEV_ID) { 303 1.9 christos if (info->printer) 304 1.9 christos info->printer(ndo, tptr, len); 305 1.9 christos else 306 1.9 christos ND_TCHECK_LEN(tptr, len); 307 1.9 christos /* 308 1.9 christos * When the type is defined without a printer, 309 1.9 christos * do not print the hex dump. 310 1.9 christos */ 311 1.9 christos covered = 1; 312 1.9 christos } 313 1.9 christos } 314 1.1 christos 315 1.10 christos if (ndo->ndo_vflag && !covered) { 316 1.9 christos ND_TCHECK_LEN(tptr, len); 317 1.6 christos print_unknown_data(ndo, tptr, "\n\t ", len); 318 1.6 christos } 319 1.9 christos tptr += len; 320 1.9 christos length -= len; 321 1.1 christos } 322 1.6 christos if (ndo->ndo_vflag < 1) 323 1.9 christos ND_PRINT(", length %u", orig_length); 324 1.1 christos 325 1.1 christos return; 326 1.9 christos invalid: 327 1.9 christos nd_print_invalid(ndo); 328 1.9 christos ND_TCHECK_LEN(tptr, length); 329 1.1 christos } 330 1.1 christos 331 1.1 christos /* 332 1.1 christos * Protocol type values. 333 1.1 christos * 334 1.1 christos * PT_NLPID means that the protocol type field contains an OSI NLPID. 335 1.1 christos * 336 1.1 christos * PT_IEEE_802_2 means that the protocol type field contains an IEEE 802.2 337 1.1 christos * LLC header that specifies that the payload is for that protocol. 338 1.1 christos */ 339 1.1 christos #define PT_NLPID 1 /* OSI NLPID */ 340 1.1 christos #define PT_IEEE_802_2 2 /* IEEE 802.2 LLC header */ 341 1.1 christos 342 1.9 christos static void 343 1.5 christos cdp_print_addr(netdissect_options *ndo, 344 1.9 christos const u_char * p, u_int l) 345 1.1 christos { 346 1.9 christos u_int num; 347 1.5 christos static const u_char prot_ipv6[] = { 348 1.1 christos 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x86, 0xdd 349 1.1 christos }; 350 1.1 christos 351 1.9 christos if (l < 4) { 352 1.9 christos ND_PRINT(" (not enough space for num)"); 353 1.9 christos goto invalid; 354 1.9 christos } 355 1.9 christos num = GET_BE_U_4(p); 356 1.1 christos p += 4; 357 1.9 christos l -= 4; 358 1.9 christos 359 1.9 christos while (num) { 360 1.9 christos u_int pt, pl, al; 361 1.1 christos 362 1.9 christos if (l < 2) { 363 1.9 christos ND_PRINT(" (not enough space for PT+PL)"); 364 1.9 christos goto invalid; 365 1.9 christos } 366 1.9 christos pt = GET_U_1(p); /* type of "protocol" field */ 367 1.9 christos pl = GET_U_1(p + 1); /* length of "protocol" field */ 368 1.1 christos p += 2; 369 1.9 christos l -= 2; 370 1.1 christos 371 1.9 christos if (l < pl + 2) { 372 1.9 christos ND_PRINT(" (not enough space for P+AL)"); 373 1.9 christos goto invalid; 374 1.9 christos } 375 1.9 christos /* Skip the protocol for now. */ 376 1.9 christos al = GET_BE_U_2(p + pl); /* address length */ 377 1.1 christos 378 1.9 christos if (pt == PT_NLPID && pl == 1 && GET_U_1(p) == NLPID_IP && 379 1.9 christos al == 4) { 380 1.1 christos /* 381 1.1 christos * IPv4: protocol type = NLPID, protocol length = 1 382 1.1 christos * (1-byte NLPID), protocol = 0xcc (NLPID for IPv4), 383 1.1 christos * address length = 4 384 1.1 christos */ 385 1.9 christos p += pl + 2; 386 1.9 christos l -= pl + 2; 387 1.9 christos /* p is just beyond al now. */ 388 1.9 christos if (l < al) { 389 1.9 christos ND_PRINT(" (not enough space for A)"); 390 1.9 christos goto invalid; 391 1.9 christos } 392 1.9 christos ND_PRINT("IPv4 (%u) %s", num, GET_IPADDR_STRING(p)); 393 1.9 christos p += al; 394 1.9 christos l -= al; 395 1.10 christos } else if (pt == PT_IEEE_802_2 && pl == 8 && 396 1.9 christos memcmp(p, prot_ipv6, 8) == 0 && al == 16) { 397 1.1 christos /* 398 1.1 christos * IPv6: protocol type = IEEE 802.2 header, 399 1.1 christos * protocol length = 8 (size of LLC+SNAP header), 400 1.1 christos * protocol = LLC+SNAP header with the IPv6 401 1.1 christos * Ethertype, address length = 16 402 1.1 christos */ 403 1.9 christos p += pl + 2; 404 1.9 christos l -= pl + 2; 405 1.9 christos /* p is just beyond al now. */ 406 1.9 christos if (l < al) { 407 1.9 christos ND_PRINT(" (not enough space for A)"); 408 1.9 christos goto invalid; 409 1.9 christos } 410 1.9 christos ND_PRINT("IPv6 (%u) %s", num, GET_IP6ADDR_STRING(p)); 411 1.1 christos p += al; 412 1.9 christos l -= al; 413 1.10 christos } else { 414 1.1 christos /* 415 1.1 christos * Generic case: just print raw data 416 1.1 christos */ 417 1.9 christos ND_PRINT("pt=0x%02x, pl=%u, pb=", pt, pl); 418 1.9 christos while (pl != 0) { 419 1.9 christos ND_PRINT(" %02x", GET_U_1(p)); 420 1.9 christos p++; 421 1.9 christos l--; 422 1.9 christos pl--; 423 1.9 christos } 424 1.9 christos ND_PRINT(", al=%u, a=", al); 425 1.1 christos p += 2; 426 1.9 christos l -= 2; 427 1.9 christos /* p is just beyond al now. */ 428 1.9 christos if (l < al) { 429 1.9 christos ND_PRINT(" (not enough space for A)"); 430 1.9 christos goto invalid; 431 1.9 christos } 432 1.9 christos while (al != 0) { 433 1.9 christos ND_PRINT(" %02x", GET_U_1(p)); 434 1.9 christos p++; 435 1.9 christos l--; 436 1.9 christos al--; 437 1.9 christos } 438 1.1 christos } 439 1.1 christos num--; 440 1.1 christos if (num) 441 1.9 christos ND_PRINT(" "); 442 1.1 christos } 443 1.9 christos if (l) 444 1.9 christos ND_PRINT(" (%u bytes of stray data)", l); 445 1.9 christos return; 446 1.1 christos 447 1.9 christos invalid: 448 1.9 christos ND_TCHECK_LEN(p, l); 449 1.1 christos } 450 1.1 christos 451 1.9 christos static void 452 1.5 christos cdp_print_prefixes(netdissect_options *ndo, 453 1.9 christos const u_char * p, u_int l) 454 1.1 christos { 455 1.9 christos if (l % 5) { 456 1.9 christos ND_PRINT(" [length %u is not a multiple of 5]", l); 457 1.9 christos goto invalid; 458 1.9 christos } 459 1.1 christos 460 1.9 christos ND_PRINT(" IPv4 Prefixes (%u):", l / 5); 461 1.1 christos 462 1.1 christos while (l > 0) { 463 1.9 christos ND_PRINT(" %u.%u.%u.%u/%u", 464 1.9 christos GET_U_1(p), GET_U_1(p + 1), GET_U_1(p + 2), 465 1.9 christos GET_U_1(p + 3), GET_U_1(p + 4)); 466 1.1 christos l -= 5; 467 1.1 christos p += 5; 468 1.1 christos } 469 1.9 christos return; 470 1.1 christos 471 1.9 christos invalid: 472 1.9 christos ND_TCHECK_LEN(p, l); 473 1.1 christos } 474