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