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.7 2024/09/02 16:15:30 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 #include <config.h> 38 39 #include "netdissect-stdinc.h" 40 41 #define ND_LONGJMP_FROM_TCHECK 42 #include "netdissect.h" 43 #include "extract.h" 44 #include "addrtoname.h" 45 46 47 #define AHCP_MAGIC_NUMBER 43 48 #define AHCP_VERSION_1 1 49 #define AHCP1_HEADER_FIX_LEN 24 50 #define AHCP1_BODY_MIN_LEN 4 51 52 #define AHCP1_MSG_DISCOVER 0 53 #define AHCP1_MSG_OFFER 1 54 #define AHCP1_MSG_REQUEST 2 55 #define AHCP1_MSG_ACK 3 56 #define AHCP1_MSG_NACK 4 57 #define AHCP1_MSG_RELEASE 5 58 59 static const struct tok ahcp1_msg_str[] = { 60 { AHCP1_MSG_DISCOVER, "Discover" }, 61 { AHCP1_MSG_OFFER, "Offer" }, 62 { AHCP1_MSG_REQUEST, "Request" }, 63 { AHCP1_MSG_ACK, "Ack" }, 64 { AHCP1_MSG_NACK, "Nack" }, 65 { AHCP1_MSG_RELEASE, "Release" }, 66 { 0, NULL } 67 }; 68 69 #define AHCP1_OPT_PAD 0 70 #define AHCP1_OPT_MANDATORY 1 71 #define AHCP1_OPT_ORIGIN_TIME 2 72 #define AHCP1_OPT_EXPIRES 3 73 #define AHCP1_OPT_MY_IPV6_ADDRESS 4 74 #define AHCP1_OPT_MY_IPV4_ADDRESS 5 75 #define AHCP1_OPT_IPV6_PREFIX 6 76 #define AHCP1_OPT_IPV4_PREFIX 7 77 #define AHCP1_OPT_IPV6_ADDRESS 8 78 #define AHCP1_OPT_IPV4_ADDRESS 9 79 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10 80 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11 81 #define AHCP1_OPT_NAME_SERVER 12 82 #define AHCP1_OPT_NTP_SERVER 13 83 #define AHCP1_OPT_MAX 13 84 85 static const struct tok ahcp1_opt_str[] = { 86 { AHCP1_OPT_PAD, "Pad" }, 87 { AHCP1_OPT_MANDATORY, "Mandatory" }, 88 { AHCP1_OPT_ORIGIN_TIME, "Origin Time" }, 89 { AHCP1_OPT_EXPIRES, "Expires" }, 90 { AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" }, 91 { AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" }, 92 { AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" }, 93 { AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" }, 94 { AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" }, 95 { AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" }, 96 { AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" }, 97 { AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" }, 98 { AHCP1_OPT_NAME_SERVER, "Name Server" }, 99 { AHCP1_OPT_NTP_SERVER, "NTP Server" }, 100 { 0, NULL } 101 }; 102 103 static void 104 ahcp_time_print(netdissect_options *ndo, 105 const u_char *cp, uint8_t len) 106 { 107 time_t t; 108 char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss UTC")]; 109 110 if (len != 4) 111 goto invalid; 112 t = GET_BE_U_4(cp); 113 ND_PRINT(": %s", 114 nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", 115 gmtime(&t))); 116 return; 117 118 invalid: 119 nd_print_invalid(ndo); 120 ND_TCHECK_LEN(cp, len); 121 } 122 123 static void 124 ahcp_seconds_print(netdissect_options *ndo, 125 const u_char *cp, uint8_t len) 126 { 127 if (len != 4) 128 goto invalid; 129 ND_PRINT(": %us", GET_BE_U_4(cp)); 130 return; 131 132 invalid: 133 nd_print_invalid(ndo); 134 ND_TCHECK_LEN(cp, len); 135 } 136 137 static void 138 ahcp_ipv6_addresses_print(netdissect_options *ndo, 139 const u_char *cp, uint8_t len) 140 { 141 const char *sep = ": "; 142 143 while (len) { 144 if (len < 16) 145 goto invalid; 146 ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp)); 147 cp += 16; 148 len -= 16; 149 sep = ", "; 150 } 151 return; 152 153 invalid: 154 nd_print_invalid(ndo); 155 ND_TCHECK_LEN(cp, len); 156 } 157 158 static void 159 ahcp_ipv4_addresses_print(netdissect_options *ndo, 160 const u_char *cp, uint8_t len) 161 { 162 const char *sep = ": "; 163 164 while (len) { 165 if (len < 4) 166 goto invalid; 167 ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp)); 168 cp += 4; 169 len -= 4; 170 sep = ", "; 171 } 172 return; 173 174 invalid: 175 nd_print_invalid(ndo); 176 ND_TCHECK_LEN(cp, len); 177 } 178 179 static void 180 ahcp_ipv6_prefixes_print(netdissect_options *ndo, 181 const u_char *cp, uint8_t len) 182 { 183 const char *sep = ": "; 184 185 while (len) { 186 if (len < 17) 187 goto invalid; 188 ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16)); 189 cp += 17; 190 len -= 17; 191 sep = ", "; 192 } 193 return; 194 195 invalid: 196 nd_print_invalid(ndo); 197 ND_TCHECK_LEN(cp, len); 198 } 199 200 static void 201 ahcp_ipv4_prefixes_print(netdissect_options *ndo, 202 const u_char *cp, uint8_t len) 203 { 204 const char *sep = ": "; 205 206 while (len) { 207 if (len < 5) 208 goto invalid; 209 ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4)); 210 cp += 5; 211 len -= 5; 212 sep = ", "; 213 } 214 return; 215 216 invalid: 217 nd_print_invalid(ndo); 218 ND_TCHECK_LEN(cp, len); 219 } 220 221 static void 222 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = { 223 /* [AHCP1_OPT_PAD] = */ NULL, 224 /* [AHCP1_OPT_MANDATORY] = */ NULL, 225 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print, 226 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print, 227 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 228 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 229 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print, 230 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL, 231 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 232 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 233 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print, 234 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print, 235 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print, 236 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print, 237 }; 238 239 static void 240 ahcp1_options_print(netdissect_options *ndo, 241 const u_char *cp, uint16_t len) 242 { 243 while (len) { 244 uint8_t option_no, option_len; 245 246 /* Option no */ 247 option_no = GET_U_1(cp); 248 cp += 1; 249 len -= 1; 250 ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)); 251 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY) 252 continue; 253 /* Length */ 254 if (!len) 255 goto invalid; 256 option_len = GET_U_1(cp); 257 cp += 1; 258 len -= 1; 259 if (option_len > len) 260 goto invalid; 261 /* Value */ 262 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) { 263 data_decoders[option_no](ndo, cp, option_len); 264 } else { 265 ND_PRINT(" (Length %u)", option_len); 266 ND_TCHECK_LEN(cp, option_len); 267 } 268 cp += option_len; 269 len -= option_len; 270 } 271 return; 272 273 invalid: 274 nd_print_invalid(ndo); 275 ND_TCHECK_LEN(cp, len); 276 } 277 278 static void 279 ahcp1_body_print(netdissect_options *ndo, 280 const u_char *cp, u_int len) 281 { 282 uint8_t type, mbz; 283 uint16_t body_len; 284 285 if (len < AHCP1_BODY_MIN_LEN) 286 goto invalid; 287 /* Type */ 288 type = GET_U_1(cp); 289 cp += 1; 290 len -= 1; 291 /* MBZ */ 292 mbz = GET_U_1(cp); 293 cp += 1; 294 len -= 1; 295 /* Length */ 296 body_len = GET_BE_U_2(cp); 297 cp += 2; 298 len -= 2; 299 300 if (ndo->ndo_vflag) { 301 ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)); 302 if (mbz != 0) 303 ND_PRINT(", MBZ %u", mbz); 304 ND_PRINT(", Length %u", body_len); 305 } 306 if (body_len > len) 307 goto invalid; 308 309 /* Options */ 310 /* Here use "body_len", not "len" (ignore any extra data). */ 311 if (ndo->ndo_vflag >= 2) 312 ahcp1_options_print(ndo, cp, body_len); 313 else 314 ND_TCHECK_LEN(cp, body_len); 315 return; 316 317 invalid: 318 nd_print_invalid(ndo); 319 ND_TCHECK_LEN(cp, len); 320 321 } 322 323 void 324 ahcp_print(netdissect_options *ndo, 325 const u_char *cp, u_int len) 326 { 327 uint8_t version; 328 329 ndo->ndo_protocol = "ahcp"; 330 nd_print_protocol_caps(ndo); 331 if (len < 2) 332 goto invalid; 333 /* Magic */ 334 if (GET_U_1(cp) != AHCP_MAGIC_NUMBER) 335 goto invalid; 336 cp += 1; 337 len -= 1; 338 /* Version */ 339 version = GET_U_1(cp); 340 cp += 1; 341 len -= 1; 342 switch (version) { 343 case AHCP_VERSION_1: { 344 ND_PRINT(" Version 1"); 345 if (len < AHCP1_HEADER_FIX_LEN - 2) 346 goto invalid; 347 if (!ndo->ndo_vflag) { 348 ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2); 349 cp += AHCP1_HEADER_FIX_LEN - 2; 350 len -= AHCP1_HEADER_FIX_LEN - 2; 351 } else { 352 /* Hopcount */ 353 ND_PRINT("\n\tHopcount %u", GET_U_1(cp)); 354 cp += 1; 355 len -= 1; 356 /* Original Hopcount */ 357 ND_PRINT(", Original Hopcount %u", GET_U_1(cp)); 358 cp += 1; 359 len -= 1; 360 /* Nonce */ 361 ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp)); 362 cp += 4; 363 len -= 4; 364 /* Source Id */ 365 ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 366 cp += 8; 367 len -= 8; 368 /* Destination Id */ 369 ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 370 cp += 8; 371 len -= 8; 372 } 373 /* Body */ 374 ahcp1_body_print(ndo, cp, len); 375 break; 376 } 377 default: 378 ND_PRINT(" Version %u (unknown)", version); 379 ND_TCHECK_LEN(cp, len); 380 break; 381 } 382 return; 383 384 invalid: 385 nd_print_invalid(ndo); 386 ND_TCHECK_LEN(cp, len); 387 } 388