Home | History | Annotate | Line # | Download | only in dist
print-ahcp.c revision 1.6
      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