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