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