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