1 /* 2 * Copyright (c) 2013 The TCPDUMP project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 17 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #ifndef lint 30 __RCSID("$NetBSD: print-ahcp.c,v 1.6 2023/08/17 20:19:40 christos Exp $"); 31 #endif 32 33 /* \summary: Ad Hoc Configuration Protocol (AHCP) printer */ 34 35 /* Based on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53 */ 36 37 #ifdef HAVE_CONFIG_H 38 #include <config.h> 39 #endif 40 41 #include "netdissect-stdinc.h" 42 43 #define ND_LONGJMP_FROM_TCHECK 44 #include "netdissect.h" 45 #include "extract.h" 46 #include "addrtoname.h" 47 48 49 #define AHCP_MAGIC_NUMBER 43 50 #define AHCP_VERSION_1 1 51 #define AHCP1_HEADER_FIX_LEN 24 52 #define AHCP1_BODY_MIN_LEN 4 53 54 #define AHCP1_MSG_DISCOVER 0 55 #define AHCP1_MSG_OFFER 1 56 #define AHCP1_MSG_REQUEST 2 57 #define AHCP1_MSG_ACK 3 58 #define AHCP1_MSG_NACK 4 59 #define AHCP1_MSG_RELEASE 5 60 61 static const struct tok ahcp1_msg_str[] = { 62 { AHCP1_MSG_DISCOVER, "Discover" }, 63 { AHCP1_MSG_OFFER, "Offer" }, 64 { AHCP1_MSG_REQUEST, "Request" }, 65 { AHCP1_MSG_ACK, "Ack" }, 66 { AHCP1_MSG_NACK, "Nack" }, 67 { AHCP1_MSG_RELEASE, "Release" }, 68 { 0, NULL } 69 }; 70 71 #define AHCP1_OPT_PAD 0 72 #define AHCP1_OPT_MANDATORY 1 73 #define AHCP1_OPT_ORIGIN_TIME 2 74 #define AHCP1_OPT_EXPIRES 3 75 #define AHCP1_OPT_MY_IPV6_ADDRESS 4 76 #define AHCP1_OPT_MY_IPV4_ADDRESS 5 77 #define AHCP1_OPT_IPV6_PREFIX 6 78 #define AHCP1_OPT_IPV4_PREFIX 7 79 #define AHCP1_OPT_IPV6_ADDRESS 8 80 #define AHCP1_OPT_IPV4_ADDRESS 9 81 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10 82 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11 83 #define AHCP1_OPT_NAME_SERVER 12 84 #define AHCP1_OPT_NTP_SERVER 13 85 #define AHCP1_OPT_MAX 13 86 87 static const struct tok ahcp1_opt_str[] = { 88 { AHCP1_OPT_PAD, "Pad" }, 89 { AHCP1_OPT_MANDATORY, "Mandatory" }, 90 { AHCP1_OPT_ORIGIN_TIME, "Origin Time" }, 91 { AHCP1_OPT_EXPIRES, "Expires" }, 92 { AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" }, 93 { AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" }, 94 { AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" }, 95 { AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" }, 96 { AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" }, 97 { AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" }, 98 { AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" }, 99 { AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" }, 100 { AHCP1_OPT_NAME_SERVER, "Name Server" }, 101 { AHCP1_OPT_NTP_SERVER, "NTP Server" }, 102 { 0, NULL } 103 }; 104 105 static void 106 ahcp_time_print(netdissect_options *ndo, 107 const u_char *cp, uint8_t len) 108 { 109 time_t t; 110 char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss UTC")]; 111 112 if (len != 4) 113 goto invalid; 114 t = GET_BE_U_4(cp); 115 ND_PRINT(": %s", 116 nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", 117 gmtime(&t))); 118 return; 119 120 invalid: 121 nd_print_invalid(ndo); 122 ND_TCHECK_LEN(cp, len); 123 } 124 125 static void 126 ahcp_seconds_print(netdissect_options *ndo, 127 const u_char *cp, uint8_t len) 128 { 129 if (len != 4) 130 goto invalid; 131 ND_PRINT(": %us", GET_BE_U_4(cp)); 132 return; 133 134 invalid: 135 nd_print_invalid(ndo); 136 ND_TCHECK_LEN(cp, len); 137 } 138 139 static void 140 ahcp_ipv6_addresses_print(netdissect_options *ndo, 141 const u_char *cp, uint8_t len) 142 { 143 const char *sep = ": "; 144 145 while (len) { 146 if (len < 16) 147 goto invalid; 148 ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp)); 149 cp += 16; 150 len -= 16; 151 sep = ", "; 152 } 153 return; 154 155 invalid: 156 nd_print_invalid(ndo); 157 ND_TCHECK_LEN(cp, len); 158 } 159 160 static void 161 ahcp_ipv4_addresses_print(netdissect_options *ndo, 162 const u_char *cp, uint8_t len) 163 { 164 const char *sep = ": "; 165 166 while (len) { 167 if (len < 4) 168 goto invalid; 169 ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp)); 170 cp += 4; 171 len -= 4; 172 sep = ", "; 173 } 174 return; 175 176 invalid: 177 nd_print_invalid(ndo); 178 ND_TCHECK_LEN(cp, len); 179 } 180 181 static void 182 ahcp_ipv6_prefixes_print(netdissect_options *ndo, 183 const u_char *cp, uint8_t len) 184 { 185 const char *sep = ": "; 186 187 while (len) { 188 if (len < 17) 189 goto invalid; 190 ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16)); 191 cp += 17; 192 len -= 17; 193 sep = ", "; 194 } 195 return; 196 197 invalid: 198 nd_print_invalid(ndo); 199 ND_TCHECK_LEN(cp, len); 200 } 201 202 static void 203 ahcp_ipv4_prefixes_print(netdissect_options *ndo, 204 const u_char *cp, uint8_t len) 205 { 206 const char *sep = ": "; 207 208 while (len) { 209 if (len < 5) 210 goto invalid; 211 ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4)); 212 cp += 5; 213 len -= 5; 214 sep = ", "; 215 } 216 return; 217 218 invalid: 219 nd_print_invalid(ndo); 220 ND_TCHECK_LEN(cp, len); 221 } 222 223 static void 224 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = { 225 /* [AHCP1_OPT_PAD] = */ NULL, 226 /* [AHCP1_OPT_MANDATORY] = */ NULL, 227 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print, 228 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print, 229 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 230 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 231 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print, 232 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL, 233 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 234 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 235 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print, 236 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print, 237 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print, 238 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print, 239 }; 240 241 static void 242 ahcp1_options_print(netdissect_options *ndo, 243 const u_char *cp, uint16_t len) 244 { 245 while (len) { 246 uint8_t option_no, option_len; 247 248 /* Option no */ 249 option_no = GET_U_1(cp); 250 cp += 1; 251 len -= 1; 252 ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)); 253 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY) 254 continue; 255 /* Length */ 256 if (!len) 257 goto invalid; 258 option_len = GET_U_1(cp); 259 cp += 1; 260 len -= 1; 261 if (option_len > len) 262 goto invalid; 263 /* Value */ 264 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) { 265 data_decoders[option_no](ndo, cp, option_len); 266 } else { 267 ND_PRINT(" (Length %u)", option_len); 268 ND_TCHECK_LEN(cp, option_len); 269 } 270 cp += option_len; 271 len -= option_len; 272 } 273 return; 274 275 invalid: 276 nd_print_invalid(ndo); 277 ND_TCHECK_LEN(cp, len); 278 } 279 280 static void 281 ahcp1_body_print(netdissect_options *ndo, 282 const u_char *cp, u_int len) 283 { 284 uint8_t type, mbz; 285 uint16_t body_len; 286 287 if (len < AHCP1_BODY_MIN_LEN) 288 goto invalid; 289 /* Type */ 290 type = GET_U_1(cp); 291 cp += 1; 292 len -= 1; 293 /* MBZ */ 294 mbz = GET_U_1(cp); 295 cp += 1; 296 len -= 1; 297 /* Length */ 298 body_len = GET_BE_U_2(cp); 299 cp += 2; 300 len -= 2; 301 302 if (ndo->ndo_vflag) { 303 ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)); 304 if (mbz != 0) 305 ND_PRINT(", MBZ %u", mbz); 306 ND_PRINT(", Length %u", body_len); 307 } 308 if (body_len > len) 309 goto invalid; 310 311 /* Options */ 312 /* Here use "body_len", not "len" (ignore any extra data). */ 313 if (ndo->ndo_vflag >= 2) 314 ahcp1_options_print(ndo, cp, body_len); 315 else 316 ND_TCHECK_LEN(cp, body_len); 317 return; 318 319 invalid: 320 nd_print_invalid(ndo); 321 ND_TCHECK_LEN(cp, len); 322 323 } 324 325 void 326 ahcp_print(netdissect_options *ndo, 327 const u_char *cp, u_int len) 328 { 329 uint8_t version; 330 331 ndo->ndo_protocol = "ahcp"; 332 nd_print_protocol_caps(ndo); 333 if (len < 2) 334 goto invalid; 335 /* Magic */ 336 if (GET_U_1(cp) != AHCP_MAGIC_NUMBER) 337 goto invalid; 338 cp += 1; 339 len -= 1; 340 /* Version */ 341 version = GET_U_1(cp); 342 cp += 1; 343 len -= 1; 344 switch (version) { 345 case AHCP_VERSION_1: { 346 ND_PRINT(" Version 1"); 347 if (len < AHCP1_HEADER_FIX_LEN - 2) 348 goto invalid; 349 if (!ndo->ndo_vflag) { 350 ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2); 351 cp += AHCP1_HEADER_FIX_LEN - 2; 352 len -= AHCP1_HEADER_FIX_LEN - 2; 353 } else { 354 /* Hopcount */ 355 ND_PRINT("\n\tHopcount %u", GET_U_1(cp)); 356 cp += 1; 357 len -= 1; 358 /* Original Hopcount */ 359 ND_PRINT(", Original Hopcount %u", GET_U_1(cp)); 360 cp += 1; 361 len -= 1; 362 /* Nonce */ 363 ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp)); 364 cp += 4; 365 len -= 4; 366 /* Source Id */ 367 ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 368 cp += 8; 369 len -= 8; 370 /* Destination Id */ 371 ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 372 cp += 8; 373 len -= 8; 374 } 375 /* Body */ 376 ahcp1_body_print(ndo, cp, len); 377 break; 378 } 379 default: 380 ND_PRINT(" Version %u (unknown)", version); 381 ND_TCHECK_LEN(cp, len); 382 break; 383 } 384 return; 385 386 invalid: 387 nd_print_invalid(ndo); 388 ND_TCHECK_LEN(cp, len); 389 } 390