Home | History | Annotate | Line # | Download | only in dist
      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