]> The Tcpdump Group git mirrors - tcpdump/blob - print-ip.c
IPv4/IPv6 demux: Rename a printer
[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 ND_TCHECK_1(cp + 2);
70 ptr = GET_U_1(cp + 2) - 1;
71 if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
72 ND_PRINT(" [bad ptr %u]", GET_U_1(cp + 2));
73
74 for (len = 3; len < length; len += 4) {
75 ND_TCHECK_4(cp + len);
76 ND_PRINT(" %s", GET_IPADDR_STRING(cp + len));
77 if (ptr > len)
78 ND_PRINT(",");
79 }
80 return (0);
81
82 trunc:
83 return (-1);
84 }
85
86 /*
87 * If source-routing is present and valid, return the final destination.
88 * Otherwise, return IP destination.
89 *
90 * This is used for UDP and TCP pseudo-header in the checksum
91 * calculation.
92 */
93 static uint32_t
94 ip_finddst(netdissect_options *ndo,
95 const struct ip *ip)
96 {
97 u_int length;
98 u_int len;
99 const u_char *cp;
100
101 cp = (const u_char *)(ip + 1);
102 length = IP_HL(ip) * 4;
103 if (length < sizeof(struct ip))
104 goto trunc;
105 length -= sizeof(struct ip);
106
107 for (; length != 0; cp += len, length -= len) {
108 int tt;
109
110 ND_TCHECK_1(cp);
111 tt = GET_U_1(cp);
112 if (tt == IPOPT_EOL)
113 break;
114 else if (tt == IPOPT_NOP)
115 len = 1;
116 else {
117 ND_TCHECK_1(cp + 1);
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 ND_TCHECK_1(cp + 3);
187 hoplen = ((GET_U_1(cp + 3) & 0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
188 if ((length - 4) & (hoplen-1))
189 ND_PRINT("[bad length %u]", length);
190 ND_TCHECK_1(cp + 2);
191 ptr = GET_U_1(cp + 2) - 1;
192 len = 0;
193 if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
194 ND_PRINT("[bad ptr %u]", GET_U_1(cp + 2));
195 ND_TCHECK_1(cp + 3);
196 switch (GET_U_1(cp + 3)&0xF) {
197 case IPOPT_TS_TSONLY:
198 ND_PRINT("TSONLY");
199 break;
200 case IPOPT_TS_TSANDADDR:
201 ND_PRINT("TS+ADDR");
202 break;
203 /*
204 * prespecified should really be 3, but some ones might send 2
205 * instead, and the IPOPT_TS_PRESPEC constant can apparently
206 * have both values, so we have to hard-code it here.
207 */
208
209 case 2:
210 ND_PRINT("PRESPEC2.0");
211 break;
212 case 3: /* IPOPT_TS_PRESPEC */
213 ND_PRINT("PRESPEC");
214 break;
215 default:
216 ND_PRINT("[bad ts type %u]", GET_U_1(cp + 3)&0xF);
217 goto done;
218 }
219
220 type = " ";
221 for (len = 4; len < length; len += hoplen) {
222 if (ptr == len)
223 type = " ^ ";
224 ND_TCHECK_LEN(cp + len, hoplen);
225 ND_PRINT("%s%u@%s", type, GET_BE_U_4(cp + len + hoplen - 4),
226 hoplen!=8 ? "" : GET_IPADDR_STRING(cp + len));
227 type = " ";
228 }
229
230 done:
231 ND_PRINT("%s", ptr == len ? " ^ " : "");
232
233 if (GET_U_1(cp + 3) >> 4)
234 ND_PRINT(" [%u hops not recorded]} ", GET_U_1(cp + 3)>>4);
235 else
236 ND_PRINT("}");
237 return (0);
238
239 trunc:
240 return (-1);
241 }
242
243 /*
244 * print IP options.
245 If truncated return -1, else 0.
246 */
247 static int
248 ip_optprint(netdissect_options *ndo,
249 const u_char *cp, u_int length)
250 {
251 u_int option_len;
252 const char *sep = "";
253
254 for (; length > 0; cp += option_len, length -= option_len) {
255 u_int option_code;
256
257 ND_PRINT("%s", sep);
258 sep = ",";
259
260 ND_TCHECK_1(cp);
261 option_code = GET_U_1(cp);
262
263 ND_PRINT("%s",
264 tok2str(ip_option_values,"unknown %u",option_code));
265
266 if (option_code == IPOPT_NOP ||
267 option_code == IPOPT_EOL)
268 option_len = 1;
269
270 else {
271 ND_TCHECK_1(cp + 1);
272 option_len = GET_U_1(cp + 1);
273 if (option_len < 2) {
274 ND_PRINT(" [bad length %u]", option_len);
275 return 0;
276 }
277 }
278
279 if (option_len > length) {
280 ND_PRINT(" [bad length %u]", option_len);
281 return 0;
282 }
283
284 ND_TCHECK_LEN(cp, option_len);
285
286 switch (option_code) {
287 case IPOPT_EOL:
288 return 0;
289
290 case IPOPT_TS:
291 if (ip_printts(ndo, cp, option_len) == -1)
292 goto trunc;
293 break;
294
295 case IPOPT_RR: /* fall through */
296 case IPOPT_SSRR:
297 case IPOPT_LSRR:
298 if (ip_printroute(ndo, cp, option_len) == -1)
299 goto trunc;
300 break;
301
302 case IPOPT_RA:
303 if (option_len < 4) {
304 ND_PRINT(" [bad length %u]", option_len);
305 break;
306 }
307 ND_TCHECK_1(cp + 3);
308 if (GET_BE_U_2(cp + 2) != 0)
309 ND_PRINT(" value %u", GET_BE_U_2(cp + 2));
310 break;
311
312 case IPOPT_NOP: /* nothing to print - fall through */
313 case IPOPT_SECURITY:
314 default:
315 break;
316 }
317 }
318 return 0;
319
320 trunc:
321 return -1;
322 }
323
324 #define IP_RES 0x8000
325
326 static const struct tok ip_frag_values[] = {
327 { IP_MF, "+" },
328 { IP_DF, "DF" },
329 { IP_RES, "rsvd" }, /* The RFC3514 evil ;-) bit */
330 { 0, NULL }
331 };
332
333
334 /*
335 * print an IP datagram.
336 */
337 void
338 ip_print(netdissect_options *ndo,
339 const u_char *bp,
340 u_int length)
341 {
342 const struct ip *ip;
343 u_int off;
344 u_int hlen;
345 u_int len;
346 struct cksum_vec vec[1];
347 uint8_t ip_tos, ip_ttl, ip_proto;
348 uint16_t sum, ip_sum;
349 const char *p_name;
350 int truncated = 0;
351
352 ndo->ndo_protocol = "ip";
353 ip = (const struct ip *)bp;
354 if (IP_V(ip) != 4) { /* print version and fail if != 4 */
355 if (IP_V(ip) == 6)
356 ND_PRINT("IP6, wrong link-layer encapsulation");
357 else
358 ND_PRINT("IP%u", IP_V(ip));
359 nd_print_invalid(ndo);
360 return;
361 }
362 if (!ndo->ndo_eflag)
363 ND_PRINT("IP ");
364
365 ND_TCHECK_SIZE(ip);
366 if (length < sizeof (struct ip)) {
367 ND_PRINT("truncated-ip %u", length);
368 return;
369 }
370 hlen = IP_HL(ip) * 4;
371 if (hlen < sizeof (struct ip)) {
372 ND_PRINT("bad-hlen %u", hlen);
373 return;
374 }
375
376 len = GET_BE_U_2(ip->ip_len);
377 if (length < len)
378 ND_PRINT("truncated-ip - %u bytes missing! ",
379 len - length);
380 if (len < hlen) {
381 #ifdef GUESS_TSO
382 if (len) {
383 ND_PRINT("bad-len %u", len);
384 return;
385 }
386 else {
387 /* we guess that it is a TSO send */
388 len = length;
389 }
390 #else
391 ND_PRINT("bad-len %u", len);
392 return;
393 #endif /* GUESS_TSO */
394 }
395
396 /*
397 * Cut off the snapshot length to the end of the IP payload.
398 */
399 nd_push_snapend(ndo, bp + len);
400
401 len -= hlen;
402
403 off = GET_BE_U_2(ip->ip_off);
404
405 ip_proto = GET_U_1(ip->ip_p);
406
407 if (ndo->ndo_vflag) {
408 ip_tos = GET_U_1(ip->ip_tos);
409 ND_PRINT("(tos 0x%x", ip_tos);
410 /* ECN bits */
411 switch (ip_tos & 0x03) {
412
413 case 0:
414 break;
415
416 case 1:
417 ND_PRINT(",ECT(1)");
418 break;
419
420 case 2:
421 ND_PRINT(",ECT(0)");
422 break;
423
424 case 3:
425 ND_PRINT(",CE");
426 break;
427 }
428
429 ip_ttl = GET_U_1(ip->ip_ttl);
430 if (ip_ttl >= 1)
431 ND_PRINT(", ttl %u", ip_ttl);
432
433 /*
434 * for the firewall guys, print id, offset.
435 * On all but the last stick a "+" in the flags portion.
436 * For unfragmented datagrams, note the don't fragment flag.
437 */
438 ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)",
439 GET_BE_U_2(ip->ip_id),
440 (off & IP_OFFMASK) * 8,
441 bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)),
442 tok2str(ipproto_values, "unknown", ip_proto),
443 ip_proto);
444
445 ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len));
446
447 if ((hlen - sizeof(struct ip)) > 0) {
448 ND_PRINT(", options (");
449 if (ip_optprint(ndo, (const u_char *)(ip + 1),
450 hlen - sizeof(struct ip)) == -1) {
451 ND_PRINT(" [truncated-option]");
452 truncated = 1;
453 }
454 ND_PRINT(")");
455 }
456
457 if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) {
458 vec[0].ptr = (const uint8_t *)(const void *)ip;
459 vec[0].len = hlen;
460 sum = in_cksum(vec, 1);
461 if (sum != 0) {
462 ip_sum = GET_BE_U_2(ip->ip_sum);
463 ND_PRINT(", bad cksum %x (->%x)!", ip_sum,
464 in_cksum_shouldbe(ip_sum, sum));
465 }
466 }
467
468 ND_PRINT(")\n ");
469 if (truncated) {
470 ND_PRINT("%s > %s: ",
471 GET_IPADDR_STRING(ip->ip_src),
472 GET_IPADDR_STRING(ip->ip_dst));
473 nd_print_trunc(ndo);
474 nd_pop_packet_info(ndo);
475 return;
476 }
477 }
478
479 /*
480 * If this is fragment zero, hand it to the next higher
481 * level protocol. Let them know whether there are more
482 * fragments.
483 */
484 if ((off & IP_OFFMASK) == 0) {
485 uint8_t nh = GET_U_1(ip->ip_p);
486
487 if (nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
488 nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) {
489 ND_PRINT("%s > %s: ",
490 GET_IPADDR_STRING(ip->ip_src),
491 GET_IPADDR_STRING(ip->ip_dst));
492 }
493 ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4,
494 off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp);
495 } else {
496 /*
497 * Ultra quiet now means that all this stuff should be
498 * suppressed.
499 */
500 if (ndo->ndo_qflag > 1) {
501 nd_pop_packet_info(ndo);
502 return;
503 }
504
505 /*
506 * This isn't the first frag, so we're missing the
507 * next level protocol header. print the ip addr
508 * and the protocol.
509 */
510 ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src),
511 GET_IPADDR_STRING(ip->ip_dst));
512 if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL)
513 ND_PRINT(" %s", p_name);
514 else
515 ND_PRINT(" ip-proto-%u", ip_proto);
516 }
517 nd_pop_packet_info(ndo);
518 return;
519
520 trunc:
521 nd_print_trunc(ndo);
522 return;
523 }
524
525 void
526 ipN_print(netdissect_options *ndo, const u_char *bp, u_int length)
527 {
528 ndo->ndo_protocol = "ipn";
529 if (length < 1) {
530 ND_PRINT("truncated-ip %u", length);
531 return;
532 }
533
534 ND_TCHECK_1(bp);
535 switch (GET_U_1(bp) & 0xF0) {
536 case 0x40:
537 ip_print(ndo, bp, length);
538 break;
539 case 0x60:
540 ip6_print(ndo, bp, length);
541 break;
542 default:
543 ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4);
544 break;
545 }
546 return;
547
548 trunc:
549 nd_print_trunc(ndo);
550 return;
551 }