Home | History | Annotate | Line # | Download | only in dist
print-ip.c revision 1.13
      1 /*
      2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that: (1) source code distributions
      7  * retain the above copyright notice and this paragraph in its entirety, (2)
      8  * distributions including binary code include the above copyright notice and
      9  * this paragraph in its entirety in the documentation or other materials
     10  * provided with the distribution, and (3) all advertising materials mentioning
     11  * features or use of this software display the following acknowledgement:
     12  * ``This product includes software developed by the University of California,
     13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
     14  * the University nor the names of its contributors may be used to endorse
     15  * or promote products derived from this software without specific prior
     16  * written permission.
     17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
     18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
     19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     20  */
     21 
     22 #include <sys/cdefs.h>
     23 #ifndef lint
     24 __RCSID("$NetBSD: print-ip.c,v 1.13 2023/08/17 20:19:40 christos Exp $");
     25 #endif
     26 
     27 /* \summary: IP printer */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 #include <config.h>
     31 #endif
     32 
     33 #include "netdissect-stdinc.h"
     34 
     35 #include "netdissect.h"
     36 #include "addrtoname.h"
     37 #include "extract.h"
     38 
     39 #include "ip.h"
     40 #include "ipproto.h"
     41 
     42 
     43 static const struct tok ip_option_values[] = {
     44     { IPOPT_EOL, "EOL" },
     45     { IPOPT_NOP, "NOP" },
     46     { IPOPT_TS, "timestamp" },
     47     { IPOPT_SECURITY, "security" },
     48     { IPOPT_RR, "RR" },
     49     { IPOPT_SSRR, "SSRR" },
     50     { IPOPT_LSRR, "LSRR" },
     51     { IPOPT_RA, "RA" },
     52     { IPOPT_RFC1393, "traceroute" },
     53     { 0, NULL }
     54 };
     55 
     56 /*
     57  * print the recorded route in an IP RR, LSRR or SSRR option.
     58  */
     59 static int
     60 ip_printroute(netdissect_options *ndo,
     61               const u_char *cp, u_int length)
     62 {
     63 	u_int ptr;
     64 	u_int len;
     65 
     66 	if (length < 3) {
     67 		ND_PRINT(" [bad length %u]", length);
     68 		return (0);
     69 	}
     70 	if ((length + 1) & 3)
     71 		ND_PRINT(" [bad length %u]", length);
     72 	ptr = GET_U_1(cp + 2) - 1;
     73 	if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
     74 		ND_PRINT(" [bad ptr %u]", GET_U_1(cp + 2));
     75 
     76 	for (len = 3; len < length; len += 4) {
     77 		ND_TCHECK_4(cp + len);	/* Needed to print the IP addresses */
     78 		ND_PRINT(" %s", GET_IPADDR_STRING(cp + len));
     79 		if (ptr > len)
     80 			ND_PRINT(",");
     81 	}
     82 	return (0);
     83 
     84 trunc:
     85 	return (-1);
     86 }
     87 
     88 /*
     89  * If source-routing is present and valid, return the final destination.
     90  * Otherwise, return IP destination.
     91  *
     92  * This is used for UDP and TCP pseudo-header in the checksum
     93  * calculation.
     94  */
     95 static uint32_t
     96 ip_finddst(netdissect_options *ndo,
     97            const struct ip *ip)
     98 {
     99 	u_int length;
    100 	u_int len;
    101 	const u_char *cp;
    102 
    103 	cp = (const u_char *)(ip + 1);
    104 	length = IP_HL(ip) * 4;
    105 	if (length < sizeof(struct ip))
    106 		goto trunc;
    107 	length -= sizeof(struct ip);
    108 
    109 	for (; length != 0; cp += len, length -= len) {
    110 		int tt;
    111 
    112 		tt = GET_U_1(cp);
    113 		if (tt == IPOPT_EOL)
    114 			break;
    115 		else if (tt == IPOPT_NOP)
    116 			len = 1;
    117 		else {
    118 			len = GET_U_1(cp + 1);
    119 			if (len < 2)
    120 				break;
    121 		}
    122 		if (length < len)
    123 			goto trunc;
    124 		ND_TCHECK_LEN(cp, len);
    125 		switch (tt) {
    126 
    127 		case IPOPT_SSRR:
    128 		case IPOPT_LSRR:
    129 			if (len < 7)
    130 				break;
    131 			return (GET_IPV4_TO_NETWORK_ORDER(cp + len - 4));
    132 		}
    133 	}
    134 trunc:
    135 	return (GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst));
    136 }
    137 
    138 /*
    139  * Compute a V4-style checksum by building a pseudoheader.
    140  */
    141 uint16_t
    142 nextproto4_cksum(netdissect_options *ndo,
    143                  const struct ip *ip, const uint8_t *data,
    144                  u_int len, u_int covlen, uint8_t next_proto)
    145 {
    146 	struct phdr {
    147 		uint32_t src;
    148 		uint32_t dst;
    149 		uint8_t mbz;
    150 		uint8_t proto;
    151 		uint16_t len;
    152 	} ph;
    153 	struct cksum_vec vec[2];
    154 
    155 	/* pseudo-header.. */
    156 	ph.len = htons((uint16_t)len);
    157 	ph.mbz = 0;
    158 	ph.proto = next_proto;
    159 	ph.src = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
    160 	if (IP_HL(ip) == 5)
    161 		ph.dst = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
    162 	else
    163 		ph.dst = ip_finddst(ndo, ip);
    164 
    165 	vec[0].ptr = (const uint8_t *)(void *)&ph;
    166 	vec[0].len = sizeof(ph);
    167 	vec[1].ptr = data;
    168 	vec[1].len = covlen;
    169 	return (in_cksum(vec, 2));
    170 }
    171 
    172 static int
    173 ip_printts(netdissect_options *ndo,
    174            const u_char *cp, u_int length)
    175 {
    176 	u_int ptr;
    177 	u_int len;
    178 	u_int hoplen;
    179 	const char *type;
    180 
    181 	if (length < 4) {
    182 		ND_PRINT("[bad length %u]", length);
    183 		return (0);
    184 	}
    185 	ND_PRINT(" TS{");
    186 	hoplen = ((GET_U_1(cp + 3) & 0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
    187 	if ((length - 4) & (hoplen-1))
    188 		ND_PRINT("[bad length %u]", length);
    189 	ptr = GET_U_1(cp + 2) - 1;
    190 	len = 0;
    191 	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
    192 		ND_PRINT("[bad ptr %u]", GET_U_1(cp + 2));
    193 	switch (GET_U_1(cp + 3)&0xF) {
    194 	case IPOPT_TS_TSONLY:
    195 		ND_PRINT("TSONLY");
    196 		break;
    197 	case IPOPT_TS_TSANDADDR:
    198 		ND_PRINT("TS+ADDR");
    199 		break;
    200 	case IPOPT_TS_PRESPEC:
    201 		ND_PRINT("PRESPEC");
    202 		break;
    203 	default:
    204 		ND_PRINT("[bad ts type %u]", GET_U_1(cp + 3)&0xF);
    205 		goto done;
    206 	}
    207 
    208 	type = " ";
    209 	for (len = 4; len < length; len += hoplen) {
    210 		if (ptr == len)
    211 			type = " ^ ";
    212 		ND_TCHECK_LEN(cp + len, hoplen);
    213 		ND_PRINT("%s%u@%s", type, GET_BE_U_4(cp + len + hoplen - 4),
    214 			  hoplen!=8 ? "" : GET_IPADDR_STRING(cp + len));
    215 		type = " ";
    216 	}
    217 
    218 done:
    219 	ND_PRINT("%s", ptr == len ? " ^ " : "");
    220 
    221 	if (GET_U_1(cp + 3) >> 4)
    222 		ND_PRINT(" [%u hops not recorded]} ", GET_U_1(cp + 3)>>4);
    223 	else
    224 		ND_PRINT("}");
    225 	return (0);
    226 
    227 trunc:
    228 	return (-1);
    229 }
    230 
    231 /*
    232  * print IP options.
    233    If truncated return -1, else 0.
    234  */
    235 static int
    236 ip_optprint(netdissect_options *ndo,
    237             const u_char *cp, u_int length)
    238 {
    239 	u_int option_len;
    240 	const char *sep = "";
    241 
    242 	for (; length > 0; cp += option_len, length -= option_len) {
    243 		u_int option_code;
    244 
    245 		ND_PRINT("%s", sep);
    246 		sep = ",";
    247 
    248 		option_code = GET_U_1(cp);
    249 
    250 		ND_PRINT("%s",
    251 		          tok2str(ip_option_values,"unknown %u",option_code));
    252 
    253 		if (option_code == IPOPT_NOP ||
    254                     option_code == IPOPT_EOL)
    255 			option_len = 1;
    256 
    257 		else {
    258 			option_len = GET_U_1(cp + 1);
    259 			if (option_len < 2) {
    260 				ND_PRINT(" [bad length %u]", option_len);
    261 				return 0;
    262 			}
    263 		}
    264 
    265 		if (option_len > length) {
    266 			ND_PRINT(" [bad length %u]", option_len);
    267 			return 0;
    268 		}
    269 
    270 		ND_TCHECK_LEN(cp, option_len);
    271 
    272 		switch (option_code) {
    273 		case IPOPT_EOL:
    274 			return 0;
    275 
    276 		case IPOPT_TS:
    277 			if (ip_printts(ndo, cp, option_len) == -1)
    278 				goto trunc;
    279 			break;
    280 
    281 		case IPOPT_RR:       /* fall through */
    282 		case IPOPT_SSRR:
    283 		case IPOPT_LSRR:
    284 			if (ip_printroute(ndo, cp, option_len) == -1)
    285 				goto trunc;
    286 			break;
    287 
    288 		case IPOPT_RA:
    289 			if (option_len < 4) {
    290 				ND_PRINT(" [bad length %u]", option_len);
    291 				break;
    292 			}
    293 			ND_TCHECK_1(cp + 3);
    294 			if (GET_BE_U_2(cp + 2) != 0)
    295 				ND_PRINT(" value %u", GET_BE_U_2(cp + 2));
    296 			break;
    297 
    298 		case IPOPT_NOP:       /* nothing to print - fall through */
    299 		case IPOPT_SECURITY:
    300 		default:
    301 			break;
    302 		}
    303 	}
    304 	return 0;
    305 
    306 trunc:
    307 	return -1;
    308 }
    309 
    310 #define IP_RES 0x8000
    311 
    312 static const struct tok ip_frag_values[] = {
    313         { IP_MF,        "+" },
    314         { IP_DF,        "DF" },
    315 	{ IP_RES,       "rsvd" }, /* The RFC3514 evil ;-) bit */
    316         { 0,            NULL }
    317 };
    318 
    319 
    320 /*
    321  * print an IP datagram.
    322  */
    323 void
    324 ip_print(netdissect_options *ndo,
    325 	 const u_char *bp,
    326 	 u_int length)
    327 {
    328 	const struct ip *ip;
    329 	u_int off;
    330 	u_int hlen;
    331 	u_int len;
    332 	struct cksum_vec vec[1];
    333 	uint8_t ip_tos, ip_ttl, ip_proto;
    334 	uint16_t sum, ip_sum;
    335 	const char *p_name;
    336 	int truncated = 0;
    337 
    338 	ndo->ndo_protocol = "ip";
    339 	ip = (const struct ip *)bp;
    340 	if (IP_V(ip) != 4) { /* print version and fail if != 4 */
    341 	    if (IP_V(ip) == 6)
    342 	      ND_PRINT("IP6, wrong link-layer encapsulation");
    343 	    else
    344 	      ND_PRINT("IP%u", IP_V(ip));
    345 	    nd_print_invalid(ndo);
    346 	    return;
    347 	}
    348 	if (!ndo->ndo_eflag)
    349 		ND_PRINT("IP ");
    350 
    351 	ND_TCHECK_SIZE(ip);
    352 	if (length < sizeof (struct ip)) {
    353 		ND_PRINT("truncated-ip %u", length);
    354 		return;
    355 	}
    356 	hlen = IP_HL(ip) * 4;
    357 	if (hlen < sizeof (struct ip)) {
    358 		ND_PRINT("bad-hlen %u", hlen);
    359 		return;
    360 	}
    361 
    362 	len = GET_BE_U_2(ip->ip_len);
    363 	if (length < len)
    364 		ND_PRINT("truncated-ip - %u bytes missing! ",
    365 			len - length);
    366 	if (len < hlen) {
    367 #ifdef GUESS_TSO
    368             if (len) {
    369                 ND_PRINT("bad-len %u", len);
    370                 return;
    371             }
    372             else {
    373                 /* we guess that it is a TSO send */
    374                 len = length;
    375             }
    376 #else
    377             ND_PRINT("bad-len %u", len);
    378             return;
    379 #endif /* GUESS_TSO */
    380 	}
    381 
    382 	/*
    383 	 * Cut off the snapshot length to the end of the IP payload.
    384 	 */
    385 	if (!nd_push_snaplen(ndo, bp, len)) {
    386 		(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
    387 			"%s: can't push snaplen on buffer stack", __func__);
    388 	}
    389 
    390 	len -= hlen;
    391 
    392 	off = GET_BE_U_2(ip->ip_off);
    393 
    394         ip_proto = GET_U_1(ip->ip_p);
    395 
    396         if (ndo->ndo_vflag) {
    397             ip_tos = GET_U_1(ip->ip_tos);
    398             ND_PRINT("(tos 0x%x", ip_tos);
    399             /* ECN bits */
    400             switch (ip_tos & 0x03) {
    401 
    402             case 0:
    403                 break;
    404 
    405             case 1:
    406                 ND_PRINT(",ECT(1)");
    407                 break;
    408 
    409             case 2:
    410                 ND_PRINT(",ECT(0)");
    411                 break;
    412 
    413             case 3:
    414                 ND_PRINT(",CE");
    415                 break;
    416             }
    417 
    418             ip_ttl = GET_U_1(ip->ip_ttl);
    419             if (ip_ttl >= 1)
    420                 ND_PRINT(", ttl %u", ip_ttl);
    421 
    422 	    /*
    423 	     * for the firewall guys, print id, offset.
    424              * On all but the last stick a "+" in the flags portion.
    425 	     * For unfragmented datagrams, note the don't fragment flag.
    426 	     */
    427 	    ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)",
    428                          GET_BE_U_2(ip->ip_id),
    429                          (off & IP_OFFMASK) * 8,
    430                          bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)),
    431                          tok2str(ipproto_values, "unknown", ip_proto),
    432                          ip_proto);
    433 
    434             ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len));
    435 
    436             if ((hlen - sizeof(struct ip)) > 0) {
    437                 ND_PRINT(", options (");
    438                 if (ip_optprint(ndo, (const u_char *)(ip + 1),
    439                     hlen - sizeof(struct ip)) == -1) {
    440                         ND_PRINT(" [truncated-option]");
    441 			truncated = 1;
    442                 }
    443                 ND_PRINT(")");
    444             }
    445 
    446 	    if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) {
    447 	        vec[0].ptr = (const uint8_t *)(const void *)ip;
    448 	        vec[0].len = hlen;
    449 	        sum = in_cksum(vec, 1);
    450 		if (sum != 0) {
    451 		    ip_sum = GET_BE_U_2(ip->ip_sum);
    452 		    ND_PRINT(", bad cksum %x (->%x)!", ip_sum,
    453 			     in_cksum_shouldbe(ip_sum, sum));
    454 		}
    455 	    }
    456 
    457 	    ND_PRINT(")\n    ");
    458 	    if (truncated) {
    459 		ND_PRINT("%s > %s: ",
    460 			 GET_IPADDR_STRING(ip->ip_src),
    461 			 GET_IPADDR_STRING(ip->ip_dst));
    462 		nd_print_trunc(ndo);
    463 		nd_pop_packet_info(ndo);
    464 		return;
    465 	    }
    466 	}
    467 
    468 	/*
    469 	 * If this is fragment zero, hand it to the next higher
    470 	 * level protocol.  Let them know whether there are more
    471 	 * fragments.
    472 	 */
    473 	if ((off & IP_OFFMASK) == 0) {
    474 		uint8_t nh = GET_U_1(ip->ip_p);
    475 
    476 		if (nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
    477 		    nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) {
    478 			ND_PRINT("%s > %s: ",
    479 				     GET_IPADDR_STRING(ip->ip_src),
    480 				     GET_IPADDR_STRING(ip->ip_dst));
    481 		}
    482 		/*
    483 		 * Do a bounds check before calling ip_demux_print().
    484 		 * At least the header data is required.
    485 		 */
    486 		if (!ND_TTEST_LEN((const u_char *)ip, hlen)) {
    487 			ND_PRINT(" [remaining caplen(%u) < header length(%u)]",
    488 				 ND_BYTES_AVAILABLE_AFTER((const u_char *)ip),
    489 				 hlen);
    490 			nd_trunc_longjmp(ndo);
    491 		}
    492 		ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4,
    493 			       off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp);
    494 	} else {
    495 		/*
    496 		 * Ultra quiet now means that all this stuff should be
    497 		 * suppressed.
    498 		 */
    499 		if (ndo->ndo_qflag > 1) {
    500 			nd_pop_packet_info(ndo);
    501 			return;
    502 		}
    503 
    504 		/*
    505 		 * This isn't the first frag, so we're missing the
    506 		 * next level protocol header.  print the ip addr
    507 		 * and the protocol.
    508 		 */
    509 		ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src),
    510 		          GET_IPADDR_STRING(ip->ip_dst));
    511 		if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL)
    512 			ND_PRINT(" %s", p_name);
    513 		else
    514 			ND_PRINT(" ip-proto-%u", ip_proto);
    515 	}
    516 	nd_pop_packet_info(ndo);
    517 	return;
    518 
    519 trunc:
    520 	nd_print_trunc(ndo);
    521 }
    522 
    523 void
    524 ipN_print(netdissect_options *ndo, const u_char *bp, u_int length)
    525 {
    526 	ndo->ndo_protocol = "ipn";
    527 	if (length < 1) {
    528 		ND_PRINT("truncated-ip %u", length);
    529 		return;
    530 	}
    531 
    532 	switch (GET_U_1(bp) & 0xF0) {
    533 	case 0x40:
    534 		ip_print(ndo, bp, length);
    535 		break;
    536 	case 0x60:
    537 		ip6_print(ndo, bp, length);
    538 		break;
    539 	default:
    540 		ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4);
    541 		break;
    542 	}
    543 }
    544