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