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.3 2015/03/31 21:59:35 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 { 110 time_t t; 111 struct tm *tm; 112 char buf[BUFSIZE]; 113 114 if (cp + 4 != ep) 115 goto corrupt; 116 ND_TCHECK2(*cp, 4); 117 t = EXTRACT_32BITS(cp); 118 if (NULL == (tm = gmtime(&t))) 119 ND_PRINT((ndo, ": gmtime() error")); 120 else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)) 121 ND_PRINT((ndo, ": strftime() error")); 122 else 123 ND_PRINT((ndo, ": %s UTC", buf)); 124 return 0; 125 126 corrupt: 127 ND_PRINT((ndo, ": %s", cstr)); 128 ND_TCHECK2(*cp, ep - cp); 129 return 0; 130 trunc: 131 ND_PRINT((ndo, "%s", tstr)); 132 return -1; 133 } 134 135 static int 136 ahcp_seconds_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 137 { 138 if (cp + 4 != ep) 139 goto corrupt; 140 ND_TCHECK2(*cp, 4); 141 ND_PRINT((ndo, ": %us", EXTRACT_32BITS(cp))); 142 return 0; 143 144 corrupt: 145 ND_PRINT((ndo, ": %s", cstr)); 146 ND_TCHECK2(*cp, ep - cp); 147 return 0; 148 trunc: 149 ND_PRINT((ndo, "%s", tstr)); 150 return -1; 151 } 152 153 static int 154 ahcp_ipv6_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 155 { 156 const char *sep = ": "; 157 158 while (cp < ep) { 159 if (cp + 16 > ep) 160 goto corrupt; 161 ND_TCHECK2(*cp, 16); 162 #ifdef INET6 163 ND_PRINT((ndo, "%s%s", sep, ip6addr_string(ndo, cp))); 164 #else 165 ND_PRINT((ndo, "%s(compiled w/o IPv6)", sep)); 166 #endif /* INET6 */ 167 cp += 16; 168 sep = ", "; 169 } 170 return 0; 171 172 corrupt: 173 ND_PRINT((ndo, ": %s", cstr)); 174 ND_TCHECK2(*cp, ep - cp); 175 return 0; 176 trunc: 177 ND_PRINT((ndo, "%s", tstr)); 178 return -1; 179 } 180 181 static int 182 ahcp_ipv4_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 183 { 184 const char *sep = ": "; 185 186 while (cp < ep) { 187 if (cp + 4 > ep) 188 goto corrupt; 189 ND_TCHECK2(*cp, 4); 190 ND_PRINT((ndo, "%s%s", sep, ipaddr_string(ndo, cp))); 191 cp += 4; 192 sep = ", "; 193 } 194 return 0; 195 196 corrupt: 197 ND_PRINT((ndo, ": %s", cstr)); 198 ND_TCHECK2(*cp, ep - cp); 199 return 0; 200 trunc: 201 ND_PRINT((ndo, "%s", tstr)); 202 return -1; 203 } 204 205 static int 206 ahcp_ipv6_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 207 { 208 const char *sep = ": "; 209 210 while (cp < ep) { 211 if (cp + 17 > ep) 212 goto corrupt; 213 ND_TCHECK2(*cp, 17); 214 #ifdef INET6 215 ND_PRINT((ndo, "%s%s/%u", sep, ip6addr_string(ndo, cp), *(cp + 16))); 216 #else 217 ND_PRINT((ndo, "%s(compiled w/o IPv6)/%u", sep, *(cp + 16))); 218 #endif /* INET6 */ 219 cp += 17; 220 sep = ", "; 221 } 222 return 0; 223 224 corrupt: 225 ND_PRINT((ndo, ": %s", cstr)); 226 ND_TCHECK2(*cp, ep - cp); 227 return 0; 228 trunc: 229 ND_PRINT((ndo, "%s", tstr)); 230 return -1; 231 } 232 233 static int 234 ahcp_ipv4_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 235 { 236 const char *sep = ": "; 237 238 while (cp < ep) { 239 if (cp + 5 > ep) 240 goto corrupt; 241 ND_TCHECK2(*cp, 5); 242 ND_PRINT((ndo, "%s%s/%u", sep, ipaddr_string(ndo, cp), *(cp + 4))); 243 cp += 5; 244 sep = ", "; 245 } 246 return 0; 247 248 corrupt: 249 ND_PRINT((ndo, ": %s", cstr)); 250 ND_TCHECK2(*cp, ep - cp); 251 return 0; 252 trunc: 253 ND_PRINT((ndo, "%s", tstr)); 254 return -1; 255 } 256 257 /* Data decoders signal truncated data with -1. */ 258 static int 259 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = { 260 /* [AHCP1_OPT_PAD] = */ NULL, 261 /* [AHCP1_OPT_MANDATORY] = */ NULL, 262 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print, 263 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print, 264 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 265 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 266 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print, 267 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL, 268 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 269 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 270 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print, 271 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print, 272 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print, 273 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print, 274 }; 275 276 static void 277 ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 278 { 279 uint8_t option_no, option_len; 280 281 while (cp < ep) { 282 /* Option no */ 283 ND_TCHECK2(*cp, 1); 284 option_no = *cp; 285 cp += 1; 286 ND_PRINT((ndo, "\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no))); 287 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY) 288 continue; 289 /* Length */ 290 if (cp + 1 > ep) 291 goto corrupt; 292 ND_TCHECK2(*cp, 1); 293 option_len = *cp; 294 cp += 1; 295 if (cp + option_len > ep) 296 goto corrupt; 297 /* Value */ 298 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) { 299 if (data_decoders[option_no](ndo, cp, cp + option_len) < 0) 300 break; /* truncated and already marked up */ 301 } else { 302 ND_PRINT((ndo, " (Length %u)", option_len)); 303 ND_TCHECK2(*cp, option_len); 304 } 305 cp += option_len; 306 } 307 return; 308 309 corrupt: 310 ND_PRINT((ndo, " %s", cstr)); 311 ND_TCHECK2(*cp, ep - cp); 312 return; 313 trunc: 314 ND_PRINT((ndo, "%s", tstr)); 315 } 316 317 static void 318 ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) 319 { 320 uint8_t type, mbz; 321 uint16_t body_len; 322 323 if (cp + AHCP1_BODY_MIN_LEN > ep) 324 goto corrupt; 325 /* Type */ 326 ND_TCHECK2(*cp, 1); 327 type = *cp; 328 cp += 1; 329 /* MBZ */ 330 ND_TCHECK2(*cp, 1); 331 mbz = *cp; 332 cp += 1; 333 /* Length */ 334 ND_TCHECK2(*cp, 2); 335 body_len = EXTRACT_16BITS(cp); 336 cp += 2; 337 338 if (ndo->ndo_vflag) { 339 ND_PRINT((ndo, "\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type))); 340 if (mbz != 0) 341 ND_PRINT((ndo, ", MBZ %u", mbz)); 342 ND_PRINT((ndo, ", Length %u", body_len)); 343 } 344 if (cp + body_len > ep) 345 goto corrupt; 346 347 /* Options */ 348 if (ndo->ndo_vflag >= 2) 349 ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */ 350 else 351 ND_TCHECK2(*cp, body_len); 352 return; 353 354 corrupt: 355 ND_PRINT((ndo, " %s", cstr)); 356 ND_TCHECK2(*cp, ep - cp); 357 return; 358 trunc: 359 ND_PRINT((ndo, "%s", tstr)); 360 } 361 362 void 363 ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len) 364 { 365 const u_char *ep = cp + len; 366 uint8_t version; 367 368 ND_PRINT((ndo, "AHCP")); 369 if (len < 2) 370 goto corrupt; 371 /* Magic */ 372 ND_TCHECK2(*cp, 1); 373 if (*cp != AHCP_MAGIC_NUMBER) 374 goto corrupt; 375 cp += 1; 376 /* Version */ 377 ND_TCHECK2(*cp, 1); 378 version = *cp; 379 cp += 1; 380 switch (version) { 381 case AHCP_VERSION_1: { 382 ND_PRINT((ndo, " Version 1")); 383 if (len < AHCP1_HEADER_FIX_LEN) 384 goto corrupt; 385 if (!ndo->ndo_vflag) { 386 ND_TCHECK2(*cp, AHCP1_HEADER_FIX_LEN - 2); 387 cp += AHCP1_HEADER_FIX_LEN - 2; 388 } else { 389 /* Hopcount */ 390 ND_TCHECK2(*cp, 1); 391 ND_PRINT((ndo, "\n\tHopcount %u", *cp)); 392 cp += 1; 393 /* Original Hopcount */ 394 ND_TCHECK2(*cp, 1); 395 ND_PRINT((ndo, ", Original Hopcount %u", *cp)); 396 cp += 1; 397 /* Nonce */ 398 ND_TCHECK2(*cp, 4); 399 ND_PRINT((ndo, ", Nonce 0x%08x", EXTRACT_32BITS(cp))); 400 cp += 4; 401 /* Source Id */ 402 ND_TCHECK2(*cp, 8); 403 ND_PRINT((ndo, ", Source Id %s", linkaddr_string(ndo, cp, 0, 8))); 404 cp += 8; 405 /* Destination Id */ 406 ND_TCHECK2(*cp, 8); 407 ND_PRINT((ndo, ", Destination Id %s", linkaddr_string(ndo, cp, 0, 8))); 408 cp += 8; 409 } 410 /* Body */ 411 ahcp1_body_print(ndo, cp, ep); 412 break; 413 } 414 default: 415 ND_PRINT((ndo, " Version %u (unknown)", version)); 416 break; 417 } 418 return; 419 420 corrupt: 421 ND_PRINT((ndo, " %s", cstr)); 422 ND_TCHECK2(*cp, ep - cp); 423 return; 424 trunc: 425 ND_PRINT((ndo, "%s", tstr)); 426 } 427