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