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