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 48static const char tstr[] = " [|ahcp]"; 49static 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 63static 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 89static 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 107static int 108ahcp_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 125corrupt: 126 ND_PRINT((ndo, ": %s", cstr)); 127 ND_TCHECK2(*cp, ep - cp); 128 return 0; 129trunc: 130 ND_PRINT((ndo, "%s", tstr)); 131 return -1; 132} 133 134static int 135ahcp_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 142corrupt: 143 ND_PRINT((ndo, ": %s", cstr)); 144 ND_TCHECK2(*cp, ep - cp); 145 return 0; 146trunc: 147 ND_PRINT((ndo, "%s", tstr)); 148 return -1; 149} 150 151static int 152ahcp_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 169corrupt: 170 ND_PRINT((ndo, ": %s", cstr)); 171 ND_TCHECK2(*cp, ep - cp); 172 return 0; 173trunc: 174 ND_PRINT((ndo, "%s", tstr)); 175 return -1; 176} 177 178static int 179ahcp_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 192corrupt: 193 ND_PRINT((ndo, ": %s", cstr)); 194 ND_TCHECK2(*cp, ep - cp); 195 return 0; 196trunc: 197 ND_PRINT((ndo, "%s", tstr)); 198 return -1; 199} 200 201static int 202ahcp_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 219corrupt: 220 ND_PRINT((ndo, ": %s", cstr)); 221 ND_TCHECK2(*cp, ep - cp); 222 return 0; 223trunc: 224 ND_PRINT((ndo, "%s", tstr)); 225 return -1; 226} 227 228static int 229ahcp_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 242corrupt: 243 ND_PRINT((ndo, ": %s", cstr)); 244 ND_TCHECK2(*cp, ep - cp); 245 return 0; 246trunc: 247 ND_PRINT((ndo, "%s", tstr)); 248 return -1; 249} 250 251/* Data decoders signal truncated data with -1. */ 252static 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 270static void 271ahcp1_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 302corrupt: 303 ND_PRINT((ndo, " %s", cstr)); 304 ND_TCHECK2(*cp, ep - cp); 305 return; 306trunc: 307 ND_PRINT((ndo, "%s", tstr)); 308} 309 310static void 311ahcp1_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 346corrupt: 347 ND_PRINT((ndo, " %s", cstr)); 348 ND_TCHECK2(*cp, ep - cp); 349 return; 350trunc: 351 ND_PRINT((ndo, "%s", tstr)); 352} 353 354void 355ahcp_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 411corrupt: 412 ND_PRINT((ndo, " %s", cstr)); 413 ND_TCHECK2(*cp, ep - cp); 414 return; 415trunc: 416 ND_PRINT((ndo, "%s", tstr)); 417} 418 419