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