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