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 #include <config.h> 33 34 #include "netdissect-stdinc.h" 35 36 #define ND_LONGJMP_FROM_TCHECK 37 #include "netdissect.h" 38 #include "extract.h" 39 #include "addrtoname.h" 40 41 42 #define AHCP_MAGIC_NUMBER 43 43 #define AHCP_VERSION_1 1 44 #define AHCP1_HEADER_FIX_LEN 24 45 #define AHCP1_BODY_MIN_LEN 4 46 47 #define AHCP1_MSG_DISCOVER 0 48 #define AHCP1_MSG_OFFER 1 49 #define AHCP1_MSG_REQUEST 2 50 #define AHCP1_MSG_ACK 3 51 #define AHCP1_MSG_NACK 4 52 #define AHCP1_MSG_RELEASE 5 53 54 static const struct tok ahcp1_msg_str[] = { 55 { AHCP1_MSG_DISCOVER, "Discover" }, 56 { AHCP1_MSG_OFFER, "Offer" }, 57 { AHCP1_MSG_REQUEST, "Request" }, 58 { AHCP1_MSG_ACK, "Ack" }, 59 { AHCP1_MSG_NACK, "Nack" }, 60 { AHCP1_MSG_RELEASE, "Release" }, 61 { 0, NULL } 62 }; 63 64 #define AHCP1_OPT_PAD 0 65 #define AHCP1_OPT_MANDATORY 1 66 #define AHCP1_OPT_ORIGIN_TIME 2 67 #define AHCP1_OPT_EXPIRES 3 68 #define AHCP1_OPT_MY_IPV6_ADDRESS 4 69 #define AHCP1_OPT_MY_IPV4_ADDRESS 5 70 #define AHCP1_OPT_IPV6_PREFIX 6 71 #define AHCP1_OPT_IPV4_PREFIX 7 72 #define AHCP1_OPT_IPV6_ADDRESS 8 73 #define AHCP1_OPT_IPV4_ADDRESS 9 74 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10 75 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11 76 #define AHCP1_OPT_NAME_SERVER 12 77 #define AHCP1_OPT_NTP_SERVER 13 78 #define AHCP1_OPT_MAX 13 79 80 static const struct tok ahcp1_opt_str[] = { 81 { AHCP1_OPT_PAD, "Pad" }, 82 { AHCP1_OPT_MANDATORY, "Mandatory" }, 83 { AHCP1_OPT_ORIGIN_TIME, "Origin Time" }, 84 { AHCP1_OPT_EXPIRES, "Expires" }, 85 { AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" }, 86 { AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" }, 87 { AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" }, 88 { AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" }, 89 { AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" }, 90 { AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" }, 91 { AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" }, 92 { AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" }, 93 { AHCP1_OPT_NAME_SERVER, "Name Server" }, 94 { AHCP1_OPT_NTP_SERVER, "NTP Server" }, 95 { 0, NULL } 96 }; 97 98 static void 99 ahcp_time_print(netdissect_options *ndo, 100 const u_char *cp, uint8_t len) 101 { 102 time_t t; 103 char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss UTC")]; 104 105 if (len != 4) 106 goto invalid; 107 t = GET_BE_U_4(cp); 108 ND_PRINT(": %s", 109 nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", 110 gmtime(&t))); 111 return; 112 113 invalid: 114 nd_print_invalid(ndo); 115 ND_TCHECK_LEN(cp, len); 116 } 117 118 static void 119 ahcp_seconds_print(netdissect_options *ndo, 120 const u_char *cp, uint8_t len) 121 { 122 if (len != 4) 123 goto invalid; 124 ND_PRINT(": %us", GET_BE_U_4(cp)); 125 return; 126 127 invalid: 128 nd_print_invalid(ndo); 129 ND_TCHECK_LEN(cp, len); 130 } 131 132 static void 133 ahcp_ipv6_addresses_print(netdissect_options *ndo, 134 const u_char *cp, uint8_t len) 135 { 136 const char *sep = ": "; 137 138 while (len) { 139 if (len < 16) 140 goto invalid; 141 ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp)); 142 cp += 16; 143 len -= 16; 144 sep = ", "; 145 } 146 return; 147 148 invalid: 149 nd_print_invalid(ndo); 150 ND_TCHECK_LEN(cp, len); 151 } 152 153 static void 154 ahcp_ipv4_addresses_print(netdissect_options *ndo, 155 const u_char *cp, uint8_t len) 156 { 157 const char *sep = ": "; 158 159 while (len) { 160 if (len < 4) 161 goto invalid; 162 ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp)); 163 cp += 4; 164 len -= 4; 165 sep = ", "; 166 } 167 return; 168 169 invalid: 170 nd_print_invalid(ndo); 171 ND_TCHECK_LEN(cp, len); 172 } 173 174 static void 175 ahcp_ipv6_prefixes_print(netdissect_options *ndo, 176 const u_char *cp, uint8_t len) 177 { 178 const char *sep = ": "; 179 180 while (len) { 181 if (len < 17) 182 goto invalid; 183 ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16)); 184 cp += 17; 185 len -= 17; 186 sep = ", "; 187 } 188 return; 189 190 invalid: 191 nd_print_invalid(ndo); 192 ND_TCHECK_LEN(cp, len); 193 } 194 195 static void 196 ahcp_ipv4_prefixes_print(netdissect_options *ndo, 197 const u_char *cp, uint8_t len) 198 { 199 const char *sep = ": "; 200 201 while (len) { 202 if (len < 5) 203 goto invalid; 204 ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4)); 205 cp += 5; 206 len -= 5; 207 sep = ", "; 208 } 209 return; 210 211 invalid: 212 nd_print_invalid(ndo); 213 ND_TCHECK_LEN(cp, len); 214 } 215 216 static void 217 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = { 218 /* [AHCP1_OPT_PAD] = */ NULL, 219 /* [AHCP1_OPT_MANDATORY] = */ NULL, 220 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print, 221 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print, 222 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 223 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 224 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print, 225 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL, 226 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, 227 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, 228 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print, 229 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print, 230 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print, 231 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print, 232 }; 233 234 static void 235 ahcp1_options_print(netdissect_options *ndo, 236 const u_char *cp, uint16_t len) 237 { 238 while (len) { 239 uint8_t option_no, option_len; 240 241 /* Option no */ 242 option_no = GET_U_1(cp); 243 cp += 1; 244 len -= 1; 245 ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)); 246 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY) 247 continue; 248 /* Length */ 249 if (!len) 250 goto invalid; 251 option_len = GET_U_1(cp); 252 cp += 1; 253 len -= 1; 254 if (option_len > len) 255 goto invalid; 256 /* Value */ 257 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) { 258 data_decoders[option_no](ndo, cp, option_len); 259 } else { 260 ND_PRINT(" (Length %u)", option_len); 261 ND_TCHECK_LEN(cp, option_len); 262 } 263 cp += option_len; 264 len -= option_len; 265 } 266 return; 267 268 invalid: 269 nd_print_invalid(ndo); 270 ND_TCHECK_LEN(cp, len); 271 } 272 273 static void 274 ahcp1_body_print(netdissect_options *ndo, 275 const u_char *cp, u_int len) 276 { 277 uint8_t type, mbz; 278 uint16_t body_len; 279 280 if (len < AHCP1_BODY_MIN_LEN) 281 goto invalid; 282 /* Type */ 283 type = GET_U_1(cp); 284 cp += 1; 285 len -= 1; 286 /* MBZ */ 287 mbz = GET_U_1(cp); 288 cp += 1; 289 len -= 1; 290 /* Length */ 291 body_len = GET_BE_U_2(cp); 292 cp += 2; 293 len -= 2; 294 295 if (ndo->ndo_vflag) { 296 ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)); 297 if (mbz != 0) 298 ND_PRINT(", MBZ %u", mbz); 299 ND_PRINT(", Length %u", body_len); 300 } 301 if (body_len > len) 302 goto invalid; 303 304 /* Options */ 305 /* Here use "body_len", not "len" (ignore any extra data). */ 306 if (ndo->ndo_vflag >= 2) 307 ahcp1_options_print(ndo, cp, body_len); 308 else 309 ND_TCHECK_LEN(cp, body_len); 310 return; 311 312 invalid: 313 nd_print_invalid(ndo); 314 ND_TCHECK_LEN(cp, len); 315 316 } 317 318 void 319 ahcp_print(netdissect_options *ndo, 320 const u_char *cp, u_int len) 321 { 322 uint8_t version; 323 324 ndo->ndo_protocol = "ahcp"; 325 nd_print_protocol_caps(ndo); 326 if (len < 2) 327 goto invalid; 328 /* Magic */ 329 if (GET_U_1(cp) != AHCP_MAGIC_NUMBER) 330 goto invalid; 331 cp += 1; 332 len -= 1; 333 /* Version */ 334 version = GET_U_1(cp); 335 cp += 1; 336 len -= 1; 337 switch (version) { 338 case AHCP_VERSION_1: { 339 ND_PRINT(" Version 1"); 340 if (len < AHCP1_HEADER_FIX_LEN - 2) 341 goto invalid; 342 if (!ndo->ndo_vflag) { 343 ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2); 344 cp += AHCP1_HEADER_FIX_LEN - 2; 345 len -= AHCP1_HEADER_FIX_LEN - 2; 346 } else { 347 /* Hopcount */ 348 ND_PRINT("\n\tHopcount %u", GET_U_1(cp)); 349 cp += 1; 350 len -= 1; 351 /* Original Hopcount */ 352 ND_PRINT(", Original Hopcount %u", GET_U_1(cp)); 353 cp += 1; 354 len -= 1; 355 /* Nonce */ 356 ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp)); 357 cp += 4; 358 len -= 4; 359 /* Source Id */ 360 ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 361 cp += 8; 362 len -= 8; 363 /* Destination Id */ 364 ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8)); 365 cp += 8; 366 len -= 8; 367 } 368 /* Body */ 369 ahcp1_body_print(ndo, cp, len); 370 break; 371 } 372 default: 373 ND_PRINT(" Version %u (unknown)", version); 374 ND_TCHECK_LEN(cp, len); 375 break; 376 } 377 return; 378 379 invalid: 380 nd_print_invalid(ndo); 381 ND_TCHECK_LEN(cp, len); 382 } 383