print-ahcp.c revision 1.5
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 47static 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 61static 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 87static 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 105static int 106ahcp_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 124invalid: 125 ND_PRINT((ndo, "%s", istr)); 126 ND_TCHECK2(*cp, ep - cp); 127 return 0; 128trunc: 129 ND_PRINT((ndo, "%s", tstr)); 130 return -1; 131} 132 133static int 134ahcp_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 142invalid: 143 ND_PRINT((ndo, "%s", istr)); 144 ND_TCHECK2(*cp, ep - cp); 145 return 0; 146trunc: 147 ND_PRINT((ndo, "%s", tstr)); 148 return -1; 149} 150 151static int 152ahcp_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 166invalid: 167 ND_PRINT((ndo, "%s", istr)); 168 ND_TCHECK2(*cp, ep - cp); 169 return 0; 170trunc: 171 ND_PRINT((ndo, "%s", tstr)); 172 return -1; 173} 174 175static int 176ahcp_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 190invalid: 191 ND_PRINT((ndo, "%s", istr)); 192 ND_TCHECK2(*cp, ep - cp); 193 return 0; 194trunc: 195 ND_PRINT((ndo, "%s", tstr)); 196 return -1; 197} 198 199static int 200ahcp_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 214invalid: 215 ND_PRINT((ndo, "%s", istr)); 216 ND_TCHECK2(*cp, ep - cp); 217 return 0; 218trunc: 219 ND_PRINT((ndo, "%s", tstr)); 220 return -1; 221} 222 223static int 224ahcp_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 238invalid: 239 ND_PRINT((ndo, "%s", istr)); 240 ND_TCHECK2(*cp, ep - cp); 241 return 0; 242trunc: 243 ND_PRINT((ndo, "%s", tstr)); 244 return -1; 245} 246 247/* Data decoders signal truncated data with -1. */ 248static 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 266static void 267ahcp1_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 299invalid: 300 ND_PRINT((ndo, "%s", istr)); 301 ND_TCHECK2(*cp, ep - cp); 302 return; 303trunc: 304 ND_PRINT((ndo, "%s", tstr)); 305} 306 307static void 308ahcp1_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 344invalid: 345 ND_PRINT((ndo, "%s", istr)); 346 ND_TCHECK2(*cp, ep - cp); 347 return; 348trunc: 349 ND_PRINT((ndo, "%s", tstr)); 350} 351 352void 353ahcp_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 410invalid: 411 ND_PRINT((ndo, "%s", istr)); 412 ND_TCHECK2(*cp, ep - cp); 413 return; 414trunc: 415 ND_PRINT((ndo, "%s", tstr)); 416} 417