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 61static 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 87static 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 105static void 106ahcp_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 120invalid: 121 nd_print_invalid(ndo); 122 ND_TCHECK_LEN(cp, len); 123} 124 125static void 126ahcp_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 134invalid: 135 nd_print_invalid(ndo); 136 ND_TCHECK_LEN(cp, len); 137} 138 139static void 140ahcp_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 155invalid: 156 nd_print_invalid(ndo); 157 ND_TCHECK_LEN(cp, len); 158} 159 160static void 161ahcp_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 176invalid: 177 nd_print_invalid(ndo); 178 ND_TCHECK_LEN(cp, len); 179} 180 181static void 182ahcp_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 197invalid: 198 nd_print_invalid(ndo); 199 ND_TCHECK_LEN(cp, len); 200} 201 202static void 203ahcp_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 218invalid: 219 nd_print_invalid(ndo); 220 ND_TCHECK_LEN(cp, len); 221} 222 223static 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 241static void 242ahcp1_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 275invalid: 276 nd_print_invalid(ndo); 277 ND_TCHECK_LEN(cp, len); 278} 279 280static void 281ahcp1_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 319invalid: 320 nd_print_invalid(ndo); 321 ND_TCHECK_LEN(cp, len); 322 323} 324 325void 326ahcp_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 386invalid: 387 nd_print_invalid(ndo); 388 ND_TCHECK_LEN(cp, len); 389} 390