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