| /* |
| * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that: (1) source code distributions |
| * retain the above copyright notice and this paragraph in its entirety, (2) |
| * distributions including binary code include the above copyright notice and |
| * this paragraph in its entirety in the documentation or other materials |
| * provided with the distribution, and (3) all advertising materials mentioning |
| * features or use of this software display the following acknowledgement: |
| * ``This product includes software developed by the University of California, |
| * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of |
| * the University nor the names of its contributors may be used to endorse |
| * or promote products derived from this software without specific prior |
| * written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| /* \summary: IP printer */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include "netdissect-stdinc.h" |
| |
| #include "netdissect.h" |
| #include "addrtoname.h" |
| #include "extract.h" |
| |
| #include "ip.h" |
| #include "ipproto.h" |
| |
| |
| static const struct tok ip_option_values[] = { |
| { IPOPT_EOL, "EOL" }, |
| { IPOPT_NOP, "NOP" }, |
| { IPOPT_TS, "timestamp" }, |
| { IPOPT_SECURITY, "security" }, |
| { IPOPT_RR, "RR" }, |
| { IPOPT_SSRR, "SSRR" }, |
| { IPOPT_LSRR, "LSRR" }, |
| { IPOPT_RA, "RA" }, |
| { IPOPT_RFC1393, "traceroute" }, |
| { 0, NULL } |
| }; |
| |
| /* |
| * print the recorded route in an IP RR, LSRR or SSRR option. |
| */ |
| static int |
| ip_printroute(netdissect_options *ndo, |
| const u_char *cp, u_int length) |
| { |
| u_int ptr; |
| u_int len; |
| |
| if (length < 3) { |
| ND_PRINT(" [bad length %u]", length); |
| return (0); |
| } |
| if ((length + 1) & 3) |
| ND_PRINT(" [bad length %u]", length); |
| ptr = GET_U_1(cp + 2) - 1; |
| if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1) |
| ND_PRINT(" [bad ptr %u]", GET_U_1(cp + 2)); |
| |
| for (len = 3; len < length; len += 4) { |
| ND_TCHECK_4(cp + len); /* Needed to print the IP addresses */ |
| ND_PRINT(" %s", GET_IPADDR_STRING(cp + len)); |
| if (ptr > len) |
| ND_PRINT(","); |
| } |
| return (0); |
| |
| trunc: |
| return (-1); |
| } |
| |
| /* |
| * If source-routing is present and valid, return the final destination. |
| * Otherwise, return IP destination. |
| * |
| * This is used for UDP and TCP pseudo-header in the checksum |
| * calculation. |
| */ |
| static uint32_t |
| ip_finddst(netdissect_options *ndo, |
| const struct ip *ip) |
| { |
| u_int length; |
| u_int len; |
| const u_char *cp; |
| |
| cp = (const u_char *)(ip + 1); |
| length = IP_HL(ip) * 4; |
| if (length < sizeof(struct ip)) |
| goto trunc; |
| length -= sizeof(struct ip); |
| |
| for (; length != 0; cp += len, length -= len) { |
| int tt; |
| |
| tt = GET_U_1(cp); |
| if (tt == IPOPT_EOL) |
| break; |
| else if (tt == IPOPT_NOP) |
| len = 1; |
| else { |
| len = GET_U_1(cp + 1); |
| if (len < 2) |
| break; |
| } |
| if (length < len) |
| goto trunc; |
| ND_TCHECK_LEN(cp, len); |
| switch (tt) { |
| |
| case IPOPT_SSRR: |
| case IPOPT_LSRR: |
| if (len < 7) |
| break; |
| return (GET_IPV4_TO_NETWORK_ORDER(cp + len - 4)); |
| } |
| } |
| trunc: |
| return (GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst)); |
| } |
| |
| /* |
| * Compute a V4-style checksum by building a pseudoheader. |
| */ |
| uint16_t |
| nextproto4_cksum(netdissect_options *ndo, |
| const struct ip *ip, const uint8_t *data, |
| u_int len, u_int covlen, uint8_t next_proto) |
| { |
| struct phdr { |
| uint32_t src; |
| uint32_t dst; |
| uint8_t mbz; |
| uint8_t proto; |
| uint16_t len; |
| } ph; |
| struct cksum_vec vec[2]; |
| |
| /* pseudo-header.. */ |
| ph.len = htons((uint16_t)len); |
| ph.mbz = 0; |
| ph.proto = next_proto; |
| ph.src = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src); |
| if (IP_HL(ip) == 5) |
| ph.dst = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst); |
| else |
| ph.dst = ip_finddst(ndo, ip); |
| |
| vec[0].ptr = (const uint8_t *)(void *)&ph; |
| vec[0].len = sizeof(ph); |
| vec[1].ptr = data; |
| vec[1].len = covlen; |
| return (in_cksum(vec, 2)); |
| } |
| |
| static int |
| ip_printts(netdissect_options *ndo, |
| const u_char *cp, u_int length) |
| { |
| u_int ptr; |
| u_int len; |
| u_int hoplen; |
| const char *type; |
| |
| if (length < 4) { |
| ND_PRINT("[bad length %u]", length); |
| return (0); |
| } |
| ND_PRINT(" TS{"); |
| hoplen = ((GET_U_1(cp + 3) & 0xF) != IPOPT_TS_TSONLY) ? 8 : 4; |
| if ((length - 4) & (hoplen-1)) |
| ND_PRINT("[bad length %u]", length); |
| ptr = GET_U_1(cp + 2) - 1; |
| len = 0; |
| if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1) |
| ND_PRINT("[bad ptr %u]", GET_U_1(cp + 2)); |
| switch (GET_U_1(cp + 3)&0xF) { |
| case IPOPT_TS_TSONLY: |
| ND_PRINT("TSONLY"); |
| break; |
| case IPOPT_TS_TSANDADDR: |
| ND_PRINT("TS+ADDR"); |
| break; |
| case IPOPT_TS_PRESPEC: |
| ND_PRINT("PRESPEC"); |
| break; |
| default: |
| ND_PRINT("[bad ts type %u]", GET_U_1(cp + 3)&0xF); |
| goto done; |
| } |
| |
| type = " "; |
| for (len = 4; len < length; len += hoplen) { |
| if (ptr == len) |
| type = " ^ "; |
| ND_TCHECK_LEN(cp + len, hoplen); |
| ND_PRINT("%s%u@%s", type, GET_BE_U_4(cp + len + hoplen - 4), |
| hoplen!=8 ? "" : GET_IPADDR_STRING(cp + len)); |
| type = " "; |
| } |
| |
| done: |
| ND_PRINT("%s", ptr == len ? " ^ " : ""); |
| |
| if (GET_U_1(cp + 3) >> 4) |
| ND_PRINT(" [%u hops not recorded]} ", GET_U_1(cp + 3)>>4); |
| else |
| ND_PRINT("}"); |
| return (0); |
| |
| trunc: |
| return (-1); |
| } |
| |
| /* |
| * print IP options. |
| If truncated return -1, else 0. |
| */ |
| static int |
| ip_optprint(netdissect_options *ndo, |
| const u_char *cp, u_int length) |
| { |
| u_int option_len; |
| const char *sep = ""; |
| |
| for (; length > 0; cp += option_len, length -= option_len) { |
| u_int option_code; |
| |
| ND_PRINT("%s", sep); |
| sep = ","; |
| |
| option_code = GET_U_1(cp); |
| |
| ND_PRINT("%s", |
| tok2str(ip_option_values,"unknown %u",option_code)); |
| |
| if (option_code == IPOPT_NOP || |
| option_code == IPOPT_EOL) |
| option_len = 1; |
| |
| else { |
| option_len = GET_U_1(cp + 1); |
| if (option_len < 2) { |
| ND_PRINT(" [bad length %u]", option_len); |
| return 0; |
| } |
| } |
| |
| if (option_len > length) { |
| ND_PRINT(" [bad length %u]", option_len); |
| return 0; |
| } |
| |
| ND_TCHECK_LEN(cp, option_len); |
| |
| switch (option_code) { |
| case IPOPT_EOL: |
| return 0; |
| |
| case IPOPT_TS: |
| if (ip_printts(ndo, cp, option_len) == -1) |
| goto trunc; |
| break; |
| |
| case IPOPT_RR: /* fall through */ |
| case IPOPT_SSRR: |
| case IPOPT_LSRR: |
| if (ip_printroute(ndo, cp, option_len) == -1) |
| goto trunc; |
| break; |
| |
| case IPOPT_RA: |
| if (option_len < 4) { |
| ND_PRINT(" [bad length %u]", option_len); |
| break; |
| } |
| ND_TCHECK_1(cp + 3); |
| if (GET_BE_U_2(cp + 2) != 0) |
| ND_PRINT(" value %u", GET_BE_U_2(cp + 2)); |
| break; |
| |
| case IPOPT_NOP: /* nothing to print - fall through */ |
| case IPOPT_SECURITY: |
| default: |
| break; |
| } |
| } |
| return 0; |
| |
| trunc: |
| return -1; |
| } |
| |
| #define IP_RES 0x8000 |
| |
| static const struct tok ip_frag_values[] = { |
| { IP_MF, "+" }, |
| { IP_DF, "DF" }, |
| { IP_RES, "rsvd" }, /* The RFC3514 evil ;-) bit */ |
| { 0, NULL } |
| }; |
| |
| |
| /* |
| * print an IP datagram. |
| */ |
| void |
| ip_print(netdissect_options *ndo, |
| const u_char *bp, |
| u_int length) |
| { |
| const struct ip *ip; |
| u_int off; |
| u_int hlen; |
| u_int len; |
| struct cksum_vec vec[1]; |
| uint8_t ip_tos, ip_ttl, ip_proto; |
| uint16_t sum, ip_sum; |
| const char *p_name; |
| int truncated = 0; |
| |
| ndo->ndo_protocol = "ip"; |
| ip = (const struct ip *)bp; |
| if (IP_V(ip) != 4) { /* print version and fail if != 4 */ |
| if (IP_V(ip) == 6) |
| ND_PRINT("IP6, wrong link-layer encapsulation"); |
| else |
| ND_PRINT("IP%u", IP_V(ip)); |
| nd_print_invalid(ndo); |
| return; |
| } |
| if (!ndo->ndo_eflag) |
| ND_PRINT("IP "); |
| |
| ND_TCHECK_SIZE(ip); |
| if (length < sizeof (struct ip)) { |
| ND_PRINT("truncated-ip %u", length); |
| return; |
| } |
| hlen = IP_HL(ip) * 4; |
| if (hlen < sizeof (struct ip)) { |
| ND_PRINT("bad-hlen %u", hlen); |
| return; |
| } |
| |
| len = GET_BE_U_2(ip->ip_len); |
| if (length < len) |
| ND_PRINT("truncated-ip - %u bytes missing! ", |
| len - length); |
| if (len < hlen) { |
| #ifdef GUESS_TSO |
| if (len) { |
| ND_PRINT("bad-len %u", len); |
| return; |
| } |
| else { |
| /* we guess that it is a TSO send */ |
| len = length; |
| } |
| #else |
| ND_PRINT("bad-len %u", len); |
| return; |
| #endif /* GUESS_TSO */ |
| } |
| |
| /* |
| * Cut off the snapshot length to the end of the IP payload. |
| */ |
| if (!nd_push_snaplen(ndo, bp, len)) { |
| (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, |
| "%s: can't push snaplen on buffer stack", __func__); |
| } |
| |
| len -= hlen; |
| |
| off = GET_BE_U_2(ip->ip_off); |
| |
| ip_proto = GET_U_1(ip->ip_p); |
| |
| if (ndo->ndo_vflag) { |
| ip_tos = GET_U_1(ip->ip_tos); |
| ND_PRINT("(tos 0x%x", ip_tos); |
| /* ECN bits */ |
| switch (ip_tos & 0x03) { |
| |
| case 0: |
| break; |
| |
| case 1: |
| ND_PRINT(",ECT(1)"); |
| break; |
| |
| case 2: |
| ND_PRINT(",ECT(0)"); |
| break; |
| |
| case 3: |
| ND_PRINT(",CE"); |
| break; |
| } |
| |
| ip_ttl = GET_U_1(ip->ip_ttl); |
| if (ip_ttl >= 1) |
| ND_PRINT(", ttl %u", ip_ttl); |
| |
| /* |
| * for the firewall guys, print id, offset. |
| * On all but the last stick a "+" in the flags portion. |
| * For unfragmented datagrams, note the don't fragment flag. |
| */ |
| ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)", |
| GET_BE_U_2(ip->ip_id), |
| (off & IP_OFFMASK) * 8, |
| bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)), |
| tok2str(ipproto_values, "unknown", ip_proto), |
| ip_proto); |
| |
| ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len)); |
| |
| if ((hlen - sizeof(struct ip)) > 0) { |
| ND_PRINT(", options ("); |
| if (ip_optprint(ndo, (const u_char *)(ip + 1), |
| hlen - sizeof(struct ip)) == -1) { |
| ND_PRINT(" [truncated-option]"); |
| truncated = 1; |
| } |
| ND_PRINT(")"); |
| } |
| |
| if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) { |
| vec[0].ptr = (const uint8_t *)(const void *)ip; |
| vec[0].len = hlen; |
| sum = in_cksum(vec, 1); |
| if (sum != 0) { |
| ip_sum = GET_BE_U_2(ip->ip_sum); |
| ND_PRINT(", bad cksum %x (->%x)!", ip_sum, |
| in_cksum_shouldbe(ip_sum, sum)); |
| } |
| } |
| |
| ND_PRINT(")\n "); |
| if (truncated) { |
| ND_PRINT("%s > %s: ", |
| GET_IPADDR_STRING(ip->ip_src), |
| GET_IPADDR_STRING(ip->ip_dst)); |
| nd_print_trunc(ndo); |
| nd_pop_packet_info(ndo); |
| return; |
| } |
| } |
| |
| /* |
| * If this is fragment zero, hand it to the next higher |
| * level protocol. Let them know whether there are more |
| * fragments. |
| */ |
| if ((off & IP_OFFMASK) == 0) { |
| uint8_t nh = GET_U_1(ip->ip_p); |
| |
| if (nh != IPPROTO_TCP && nh != IPPROTO_UDP && |
| nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) { |
| ND_PRINT("%s > %s: ", |
| GET_IPADDR_STRING(ip->ip_src), |
| GET_IPADDR_STRING(ip->ip_dst)); |
| } |
| /* |
| * Do a bounds check before calling ip_demux_print(). |
| * At least the header data is required. |
| */ |
| if (!ND_TTEST_LEN((const u_char *)ip, hlen)) { |
| ND_PRINT(" [remaining caplen(%u) < header length(%u)]", |
| ND_BYTES_AVAILABLE_AFTER((const u_char *)ip), |
| hlen); |
| nd_trunc_longjmp(ndo); |
| } |
| ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4, |
| off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp); |
| } else { |
| /* |
| * Ultra quiet now means that all this stuff should be |
| * suppressed. |
| */ |
| if (ndo->ndo_qflag > 1) { |
| nd_pop_packet_info(ndo); |
| return; |
| } |
| |
| /* |
| * This isn't the first frag, so we're missing the |
| * next level protocol header. print the ip addr |
| * and the protocol. |
| */ |
| ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src), |
| GET_IPADDR_STRING(ip->ip_dst)); |
| if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL) |
| ND_PRINT(" %s", p_name); |
| else |
| ND_PRINT(" ip-proto-%u", ip_proto); |
| } |
| nd_pop_packet_info(ndo); |
| return; |
| |
| trunc: |
| nd_print_trunc(ndo); |
| } |
| |
| void |
| ipN_print(netdissect_options *ndo, const u_char *bp, u_int length) |
| { |
| ndo->ndo_protocol = "ipn"; |
| if (length < 1) { |
| ND_PRINT("truncated-ip %u", length); |
| return; |
| } |
| |
| switch (GET_U_1(bp) & 0xF0) { |
| case 0x40: |
| ip_print(ndo, bp, length); |
| break; |
| case 0x60: |
| ip6_print(ndo, bp, length); |
| break; |
| default: |
| ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4); |
| break; |
| } |
| } |