| /* |
| * This module implements decoding of AHCP (Ad Hoc Configuration Protocol) based |
| * on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53. |
| * |
| * |
| * Copyright (c) 2013 The TCPDUMP project |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #define NETDISSECT_REWORKED |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <tcpdump-stdinc.h> |
| |
| #include "interface.h" |
| #include "extract.h" |
| #include "addrtoname.h" |
| |
| static const char tstr[] = " [|ahcp]"; |
| static const char cstr[] = "(corrupt)"; |
| |
| #define AHCP_MAGIC_NUMBER 43 |
| #define AHCP_VERSION_1 1 |
| #define AHCP1_HEADER_FIX_LEN 24 |
| #define AHCP1_BODY_MIN_LEN 4 |
| |
| #define AHCP1_MSG_DISCOVER 0 |
| #define AHCP1_MSG_OFFER 1 |
| #define AHCP1_MSG_REQUEST 2 |
| #define AHCP1_MSG_ACK 3 |
| #define AHCP1_MSG_NACK 4 |
| #define AHCP1_MSG_RELEASE 5 |
| |
| static const struct tok ahcp1_msg_str[] = { |
| { AHCP1_MSG_DISCOVER, "Discover" }, |
| { AHCP1_MSG_OFFER, "Offer" }, |
| { AHCP1_MSG_REQUEST, "Request" }, |
| { AHCP1_MSG_ACK, "Ack" }, |
| { AHCP1_MSG_NACK, "Nack" }, |
| { AHCP1_MSG_RELEASE, "Release" }, |
| { 0, NULL } |
| }; |
| |
| #define AHCP1_OPT_PAD 0 |
| #define AHCP1_OPT_MANDATORY 1 |
| #define AHCP1_OPT_ORIGIN_TIME 2 |
| #define AHCP1_OPT_EXPIRES 3 |
| #define AHCP1_OPT_MY_IPV6_ADDRESS 4 |
| #define AHCP1_OPT_MY_IPV4_ADDRESS 5 |
| #define AHCP1_OPT_IPV6_PREFIX 6 |
| #define AHCP1_OPT_IPV4_PREFIX 7 |
| #define AHCP1_OPT_IPV6_ADDRESS 8 |
| #define AHCP1_OPT_IPV4_ADDRESS 9 |
| #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10 |
| #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11 |
| #define AHCP1_OPT_NAME_SERVER 12 |
| #define AHCP1_OPT_NTP_SERVER 13 |
| #define AHCP1_OPT_MAX 13 |
| |
| static const struct tok ahcp1_opt_str[] = { |
| { AHCP1_OPT_PAD, "Pad" }, |
| { AHCP1_OPT_MANDATORY, "Mandatory" }, |
| { AHCP1_OPT_ORIGIN_TIME, "Origin Time" }, |
| { AHCP1_OPT_EXPIRES, "Expires" }, |
| { AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" }, |
| { AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" }, |
| { AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" }, |
| { AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" }, |
| { AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" }, |
| { AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" }, |
| { AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" }, |
| { AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" }, |
| { AHCP1_OPT_NAME_SERVER, "Name Server" }, |
| { AHCP1_OPT_NTP_SERVER, "NTP Server" }, |
| { 0, NULL } |
| }; |
| |
| static int |
| ahcp_time_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| time_t t; |
| struct tm *tm; |
| char buf[BUFSIZE]; |
| |
| if (cp + 4 != ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 4); |
| t = EXTRACT_32BITS(cp); |
| if (NULL == (tm = gmtime(&t))) |
| ND_PRINT((ndo, ": gmtime() error")); |
| else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm)) |
| ND_PRINT((ndo, ": strftime() error")); |
| else |
| ND_PRINT((ndo, ": %s UTC", buf)); |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| static int |
| ahcp_seconds_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| if (cp + 4 != ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 4); |
| ND_PRINT((ndo, ": %us", EXTRACT_32BITS(cp))); |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| static int |
| ahcp_ipv6_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| const char *sep = ": "; |
| |
| while (cp < ep) { |
| if (cp + 16 > ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 16); |
| #ifdef INET6 |
| ND_PRINT((ndo, "%s%s", sep, ip6addr_string(ndo, cp))); |
| #else |
| ND_PRINT((ndo, "%s(compiled w/o IPv6)", sep)); |
| #endif /* INET6 */ |
| cp += 16; |
| sep = ", "; |
| } |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| static int |
| ahcp_ipv4_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| const char *sep = ": "; |
| |
| while (cp < ep) { |
| if (cp + 4 > ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 4); |
| ND_PRINT((ndo, "%s%s", sep, ipaddr_string(ndo, cp))); |
| cp += 4; |
| sep = ", "; |
| } |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| static int |
| ahcp_ipv6_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| const char *sep = ": "; |
| |
| while (cp < ep) { |
| if (cp + 17 > ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 17); |
| #ifdef INET6 |
| ND_PRINT((ndo, "%s%s/%u", sep, ip6addr_string(ndo, cp), *(cp + 16))); |
| #else |
| ND_PRINT((ndo, "%s(compiled w/o IPv6)/%u", sep, *(cp + 16))); |
| #endif /* INET6 */ |
| cp += 17; |
| sep = ", "; |
| } |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| static int |
| ahcp_ipv4_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| const char *sep = ": "; |
| |
| while (cp < ep) { |
| if (cp + 5 > ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 5); |
| ND_PRINT((ndo, "%s%s/%u", sep, ipaddr_string(ndo, cp), *(cp + 4))); |
| cp += 5; |
| sep = ", "; |
| } |
| return 0; |
| |
| corrupt: |
| ND_PRINT((ndo, ": %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return 0; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| return -1; |
| } |
| |
| /* Data decoders signal truncated data with -1. */ |
| static int |
| (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = { |
| /* [AHCP1_OPT_PAD] = */ NULL, |
| /* [AHCP1_OPT_MANDATORY] = */ NULL, |
| /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print, |
| /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print, |
| /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, |
| /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, |
| /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print, |
| /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL, |
| /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print, |
| /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print, |
| /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print, |
| /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print, |
| /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print, |
| /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print, |
| }; |
| |
| static void |
| ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| uint8_t option_no, option_len; |
| |
| while (cp < ep) { |
| /* Option no */ |
| ND_TCHECK2(*cp, 1); |
| option_no = *cp; |
| cp += 1; |
| ND_PRINT((ndo, "\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no))); |
| if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY) |
| continue; |
| /* Length */ |
| if (cp + 1 > ep) |
| goto corrupt; |
| ND_TCHECK2(*cp, 1); |
| option_len = *cp; |
| cp += 1; |
| if (cp + option_len > ep) |
| goto corrupt; |
| /* Value */ |
| if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) { |
| if (data_decoders[option_no](ndo, cp, cp + option_len) < 0) |
| break; /* truncated and already marked up */ |
| } else { |
| ND_PRINT((ndo, " (Length %u)", option_len)); |
| ND_TCHECK2(*cp, option_len); |
| } |
| cp += option_len; |
| } |
| return; |
| |
| corrupt: |
| ND_PRINT((ndo, " %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| } |
| |
| static void |
| ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) |
| { |
| uint8_t type, mbz; |
| uint16_t body_len; |
| |
| if (cp + AHCP1_BODY_MIN_LEN > ep) |
| goto corrupt; |
| /* Type */ |
| ND_TCHECK2(*cp, 1); |
| type = *cp; |
| cp += 1; |
| /* MBZ */ |
| ND_TCHECK2(*cp, 1); |
| mbz = *cp; |
| cp += 1; |
| /* Length */ |
| ND_TCHECK2(*cp, 2); |
| body_len = EXTRACT_16BITS(cp); |
| cp += 2; |
| |
| if (ndo->ndo_vflag) { |
| ND_PRINT((ndo, "\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type))); |
| if (mbz != 0) |
| ND_PRINT((ndo, ", MBZ %u", mbz)); |
| ND_PRINT((ndo, ", Length %u", body_len)); |
| } |
| if (cp + body_len > ep) |
| goto corrupt; |
| |
| /* Options */ |
| if (ndo->ndo_vflag >= 2) |
| ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */ |
| else |
| ND_TCHECK2(*cp, body_len); |
| return; |
| |
| corrupt: |
| ND_PRINT((ndo, " %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| } |
| |
| void |
| ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len) |
| { |
| const u_char *ep = cp + len; |
| uint8_t version; |
| |
| ND_PRINT((ndo, "AHCP")); |
| if (len < 2) |
| goto corrupt; |
| /* Magic */ |
| ND_TCHECK2(*cp, 1); |
| if (*cp != AHCP_MAGIC_NUMBER) |
| goto corrupt; |
| cp += 1; |
| /* Version */ |
| ND_TCHECK2(*cp, 1); |
| version = *cp; |
| cp += 1; |
| switch (version) { |
| case AHCP_VERSION_1: { |
| ND_PRINT((ndo, " Version 1")); |
| if (len < AHCP1_HEADER_FIX_LEN) |
| goto corrupt; |
| if (!ndo->ndo_vflag) { |
| ND_TCHECK2(*cp, AHCP1_HEADER_FIX_LEN - 2); |
| cp += AHCP1_HEADER_FIX_LEN - 2; |
| } else { |
| /* Hopcount */ |
| ND_TCHECK2(*cp, 1); |
| ND_PRINT((ndo, "\n\tHopcount %u", *cp)); |
| cp += 1; |
| /* Original Hopcount */ |
| ND_TCHECK2(*cp, 1); |
| ND_PRINT((ndo, ", Original Hopcount %u", *cp)); |
| cp += 1; |
| /* Nonce */ |
| ND_TCHECK2(*cp, 4); |
| ND_PRINT((ndo, ", Nonce 0x%08x", EXTRACT_32BITS(cp))); |
| cp += 4; |
| /* Source Id */ |
| ND_TCHECK2(*cp, 8); |
| ND_PRINT((ndo, ", Source Id %s", linkaddr_string(ndo, cp, 0, 8))); |
| cp += 8; |
| /* Destination Id */ |
| ND_TCHECK2(*cp, 8); |
| ND_PRINT((ndo, ", Destination Id %s", linkaddr_string(ndo, cp, 0, 8))); |
| cp += 8; |
| } |
| /* Body */ |
| ahcp1_body_print(ndo, cp, ep); |
| break; |
| } |
| default: |
| ND_PRINT((ndo, " Version %u (unknown)", version)); |
| break; |
| } |
| return; |
| |
| corrupt: |
| ND_PRINT((ndo, " %s", cstr)); |
| ND_TCHECK2(*cp, ep - cp); |
| return; |
| trunc: |
| ND_PRINT((ndo, "%s", tstr)); |
| } |