Home | History | Annotate | Download | only in tcpdump
      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 #define NETDISSECT_REWORKED
     33 #ifdef HAVE_CONFIG_H
     34 #include "config.h"
     35 #endif
     36 
     37 #include <tcpdump-stdinc.h>
     38 
     39 #include "interface.h"
     40 #include "extract.h"
     41 #include "addrtoname.h"
     42 
     43 static const char tstr[] = " [|ahcp]";
     44 static const char cstr[] = "(corrupt)";
     45 
     46 #define AHCP_MAGIC_NUMBER 43
     47 #define AHCP_VERSION_1 1
     48 #define AHCP1_HEADER_FIX_LEN 24
     49 #define AHCP1_BODY_MIN_LEN 4
     50 
     51 #define AHCP1_MSG_DISCOVER 0
     52 #define AHCP1_MSG_OFFER    1
     53 #define AHCP1_MSG_REQUEST  2
     54 #define AHCP1_MSG_ACK      3
     55 #define AHCP1_MSG_NACK     4
     56 #define AHCP1_MSG_RELEASE  5
     57 
     58 static const struct tok ahcp1_msg_str[] = {
     59 	{ AHCP1_MSG_DISCOVER, "Discover" },
     60 	{ AHCP1_MSG_OFFER,    "Offer"    },
     61 	{ AHCP1_MSG_REQUEST,  "Request"  },
     62 	{ AHCP1_MSG_ACK,      "Ack"      },
     63 	{ AHCP1_MSG_NACK,     "Nack"     },
     64 	{ AHCP1_MSG_RELEASE,  "Release"  },
     65 	{ 0, NULL }
     66 };
     67 
     68 #define AHCP1_OPT_PAD                     0
     69 #define AHCP1_OPT_MANDATORY               1
     70 #define AHCP1_OPT_ORIGIN_TIME             2
     71 #define AHCP1_OPT_EXPIRES                 3
     72 #define AHCP1_OPT_MY_IPV6_ADDRESS         4
     73 #define AHCP1_OPT_MY_IPV4_ADDRESS         5
     74 #define AHCP1_OPT_IPV6_PREFIX             6
     75 #define AHCP1_OPT_IPV4_PREFIX             7
     76 #define AHCP1_OPT_IPV6_ADDRESS            8
     77 #define AHCP1_OPT_IPV4_ADDRESS            9
     78 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10
     79 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11
     80 #define AHCP1_OPT_NAME_SERVER            12
     81 #define AHCP1_OPT_NTP_SERVER             13
     82 #define AHCP1_OPT_MAX                    13
     83 
     84 static const struct tok ahcp1_opt_str[] = {
     85 	{ AHCP1_OPT_PAD,                    "Pad"                    },
     86 	{ AHCP1_OPT_MANDATORY,              "Mandatory"              },
     87 	{ AHCP1_OPT_ORIGIN_TIME,            "Origin Time"            },
     88 	{ AHCP1_OPT_EXPIRES,                "Expires"                },
     89 	{ AHCP1_OPT_MY_IPV6_ADDRESS,        "My-IPv6-Address"        },
     90 	{ AHCP1_OPT_MY_IPV4_ADDRESS,        "My-IPv4-Address"        },
     91 	{ AHCP1_OPT_IPV6_PREFIX,            "IPv6 Prefix"            },
     92 	{ AHCP1_OPT_IPV4_PREFIX,            "IPv4 Prefix"            },
     93 	{ AHCP1_OPT_IPV6_ADDRESS,           "IPv6 Address"           },
     94 	{ AHCP1_OPT_IPV4_ADDRESS,           "IPv4 Address"           },
     95 	{ AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" },
     96 	{ AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" },
     97 	{ AHCP1_OPT_NAME_SERVER,            "Name Server"            },
     98 	{ AHCP1_OPT_NTP_SERVER,             "NTP Server"             },
     99 	{ 0, NULL }
    100 };
    101 
    102 static int
    103 ahcp_time_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    104 {
    105 	time_t t;
    106 	struct tm *tm;
    107 	char buf[BUFSIZE];
    108 
    109 	if (cp + 4 != ep)
    110 		goto corrupt;
    111 	ND_TCHECK2(*cp, 4);
    112 	t = EXTRACT_32BITS(cp);
    113 	if (NULL == (tm = gmtime(&t)))
    114 		ND_PRINT((ndo, ": gmtime() error"));
    115 	else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
    116 		ND_PRINT((ndo, ": strftime() error"));
    117 	else
    118 		ND_PRINT((ndo, ": %s UTC", buf));
    119 	return 0;
    120 
    121 corrupt:
    122 	ND_PRINT((ndo, ": %s", cstr));
    123 	ND_TCHECK2(*cp, ep - cp);
    124 	return 0;
    125 trunc:
    126 	ND_PRINT((ndo, "%s", tstr));
    127 	return -1;
    128 }
    129 
    130 static int
    131 ahcp_seconds_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    132 {
    133 	if (cp + 4 != ep)
    134 		goto corrupt;
    135 	ND_TCHECK2(*cp, 4);
    136 	ND_PRINT((ndo, ": %us", EXTRACT_32BITS(cp)));
    137 	return 0;
    138 
    139 corrupt:
    140 	ND_PRINT((ndo, ": %s", cstr));
    141 	ND_TCHECK2(*cp, ep - cp);
    142 	return 0;
    143 trunc:
    144 	ND_PRINT((ndo, "%s", tstr));
    145 	return -1;
    146 }
    147 
    148 static int
    149 ahcp_ipv6_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    150 {
    151 	const char *sep = ": ";
    152 
    153 	while (cp < ep) {
    154 		if (cp + 16 > ep)
    155 			goto corrupt;
    156 		ND_TCHECK2(*cp, 16);
    157 #ifdef INET6
    158 		ND_PRINT((ndo, "%s%s", sep, ip6addr_string(ndo, cp)));
    159 #else
    160 		ND_PRINT((ndo, "%s(compiled w/o IPv6)", sep));
    161 #endif /* INET6 */
    162 		cp += 16;
    163 		sep = ", ";
    164 	}
    165 	return 0;
    166 
    167 corrupt:
    168 	ND_PRINT((ndo, ": %s", cstr));
    169 	ND_TCHECK2(*cp, ep - cp);
    170 	return 0;
    171 trunc:
    172 	ND_PRINT((ndo, "%s", tstr));
    173 	return -1;
    174 }
    175 
    176 static int
    177 ahcp_ipv4_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    178 {
    179 	const char *sep = ": ";
    180 
    181 	while (cp < ep) {
    182 		if (cp + 4 > ep)
    183 			goto corrupt;
    184 		ND_TCHECK2(*cp, 4);
    185 		ND_PRINT((ndo, "%s%s", sep, ipaddr_string(ndo, cp)));
    186 		cp += 4;
    187 		sep = ", ";
    188 	}
    189 	return 0;
    190 
    191 corrupt:
    192 	ND_PRINT((ndo, ": %s", cstr));
    193 	ND_TCHECK2(*cp, ep - cp);
    194 	return 0;
    195 trunc:
    196 	ND_PRINT((ndo, "%s", tstr));
    197 	return -1;
    198 }
    199 
    200 static int
    201 ahcp_ipv6_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    202 {
    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 {
    231 	const char *sep = ": ";
    232 
    233 	while (cp < ep) {
    234 		if (cp + 5 > ep)
    235 			goto corrupt;
    236 		ND_TCHECK2(*cp, 5);
    237 		ND_PRINT((ndo, "%s%s/%u", sep, ipaddr_string(ndo, cp), *(cp + 4)));
    238 		cp += 5;
    239 		sep = ", ";
    240 	}
    241 	return 0;
    242 
    243 corrupt:
    244 	ND_PRINT((ndo, ": %s", cstr));
    245 	ND_TCHECK2(*cp, ep - cp);
    246 	return 0;
    247 trunc:
    248 	ND_PRINT((ndo, "%s", tstr));
    249 	return -1;
    250 }
    251 
    252 /* Data decoders signal truncated data with -1. */
    253 static int
    254 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = {
    255 	/* [AHCP1_OPT_PAD]                    = */  NULL,
    256 	/* [AHCP1_OPT_MANDATORY]              = */  NULL,
    257 	/* [AHCP1_OPT_ORIGIN_TIME]            = */  ahcp_time_print,
    258 	/* [AHCP1_OPT_EXPIRES]                = */  ahcp_seconds_print,
    259 	/* [AHCP1_OPT_MY_IPV6_ADDRESS]        = */  ahcp_ipv6_addresses_print,
    260 	/* [AHCP1_OPT_MY_IPV4_ADDRESS]        = */  ahcp_ipv4_addresses_print,
    261 	/* [AHCP1_OPT_IPV6_PREFIX]            = */  ahcp_ipv6_prefixes_print,
    262 	/* [AHCP1_OPT_IPV4_PREFIX]            = */  NULL,
    263 	/* [AHCP1_OPT_IPV6_ADDRESS]           = */  ahcp_ipv6_addresses_print,
    264 	/* [AHCP1_OPT_IPV4_ADDRESS]           = */  ahcp_ipv4_addresses_print,
    265 	/* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */  ahcp_ipv6_prefixes_print,
    266 	/* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */  ahcp_ipv4_prefixes_print,
    267 	/* [AHCP1_OPT_NAME_SERVER]            = */  ahcp_ipv6_addresses_print,
    268 	/* [AHCP1_OPT_NTP_SERVER]             = */  ahcp_ipv6_addresses_print,
    269 };
    270 
    271 static void
    272 ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    273 {
    274 	uint8_t option_no, option_len;
    275 
    276 	while (cp < ep) {
    277 		/* Option no */
    278 		ND_TCHECK2(*cp, 1);
    279 		option_no = *cp;
    280 		cp += 1;
    281 		ND_PRINT((ndo, "\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)));
    282 		if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
    283 			continue;
    284 		/* Length */
    285 		if (cp + 1 > ep)
    286 			goto corrupt;
    287 		ND_TCHECK2(*cp, 1);
    288 		option_len = *cp;
    289 		cp += 1;
    290 		if (cp + option_len > ep)
    291 			goto corrupt;
    292 		/* Value */
    293 		if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
    294 			if (data_decoders[option_no](ndo, cp, cp + option_len) < 0)
    295 				break; /* truncated and already marked up */
    296 		} else {
    297 			ND_PRINT((ndo, " (Length %u)", option_len));
    298 			ND_TCHECK2(*cp, option_len);
    299 		}
    300 		cp += option_len;
    301 	}
    302 	return;
    303 
    304 corrupt:
    305 	ND_PRINT((ndo, " %s", cstr));
    306 	ND_TCHECK2(*cp, ep - cp);
    307 	return;
    308 trunc:
    309 	ND_PRINT((ndo, "%s", tstr));
    310 }
    311 
    312 static void
    313 ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
    314 {
    315 	uint8_t type, mbz;
    316 	uint16_t body_len;
    317 
    318 	if (cp + AHCP1_BODY_MIN_LEN > ep)
    319 		goto corrupt;
    320 	/* Type */
    321 	ND_TCHECK2(*cp, 1);
    322 	type = *cp;
    323 	cp += 1;
    324 	/* MBZ */
    325 	ND_TCHECK2(*cp, 1);
    326 	mbz = *cp;
    327 	cp += 1;
    328 	/* Length */
    329 	ND_TCHECK2(*cp, 2);
    330 	body_len = EXTRACT_16BITS(cp);
    331 	cp += 2;
    332 
    333 	if (ndo->ndo_vflag) {
    334 		ND_PRINT((ndo, "\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)));
    335 		if (mbz != 0)
    336 			ND_PRINT((ndo, ", MBZ %u", mbz));
    337 		ND_PRINT((ndo, ", Length %u", body_len));
    338 	}
    339 	if (cp + body_len > ep)
    340 		goto corrupt;
    341 
    342 	/* Options */
    343 	if (ndo->ndo_vflag >= 2)
    344 		ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */
    345 	else
    346 		ND_TCHECK2(*cp, body_len);
    347 	return;
    348 
    349 corrupt:
    350 	ND_PRINT((ndo, " %s", cstr));
    351 	ND_TCHECK2(*cp, ep - cp);
    352 	return;
    353 trunc:
    354 	ND_PRINT((ndo, "%s", tstr));
    355 }
    356 
    357 void
    358 ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len)
    359 {
    360 	const u_char *ep = cp + len;
    361 	uint8_t version;
    362 
    363 	ND_PRINT((ndo, "AHCP"));
    364 	if (len < 2)
    365 		goto corrupt;
    366 	/* Magic */
    367 	ND_TCHECK2(*cp, 1);
    368 	if (*cp != AHCP_MAGIC_NUMBER)
    369 		goto corrupt;
    370 	cp += 1;
    371 	/* Version */
    372 	ND_TCHECK2(*cp, 1);
    373 	version = *cp;
    374 	cp += 1;
    375 	switch (version) {
    376 		case AHCP_VERSION_1: {
    377 			ND_PRINT((ndo, " Version 1"));
    378 			if (len < AHCP1_HEADER_FIX_LEN)
    379 				goto corrupt;
    380 			if (!ndo->ndo_vflag) {
    381 				ND_TCHECK2(*cp, AHCP1_HEADER_FIX_LEN - 2);
    382 				cp += AHCP1_HEADER_FIX_LEN - 2;
    383 			} else {
    384 				/* Hopcount */
    385 				ND_TCHECK2(*cp, 1);
    386 				ND_PRINT((ndo, "\n\tHopcount %u", *cp));
    387 				cp += 1;
    388 				/* Original Hopcount */
    389 				ND_TCHECK2(*cp, 1);
    390 				ND_PRINT((ndo, ", Original Hopcount %u", *cp));
    391 				cp += 1;
    392 				/* Nonce */
    393 				ND_TCHECK2(*cp, 4);
    394 				ND_PRINT((ndo, ", Nonce 0x%08x", EXTRACT_32BITS(cp)));
    395 				cp += 4;
    396 				/* Source Id */
    397 				ND_TCHECK2(*cp, 8);
    398 				ND_PRINT((ndo, ", Source Id %s", linkaddr_string(ndo, cp, 0, 8)));
    399 				cp += 8;
    400 				/* Destination Id */
    401 				ND_TCHECK2(*cp, 8);
    402 				ND_PRINT((ndo, ", Destination Id %s", linkaddr_string(ndo, cp, 0, 8)));
    403 				cp += 8;
    404 			}
    405 			/* Body */
    406 			ahcp1_body_print(ndo, cp, ep);
    407 			break;
    408 		}
    409 		default:
    410 			ND_PRINT((ndo, " Version %u (unknown)", version));
    411 			break;
    412 	}
    413 	return;
    414 
    415 corrupt:
    416 	ND_PRINT((ndo, " %s", cstr));
    417 	ND_TCHECK2(*cp, ep - cp);
    418 	return;
    419 trunc:
    420 	ND_PRINT((ndo, "%s", tstr));
    421 }
    422