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