111b3aaa1Schristos /* NetBSD: print-tcp.c,v 1.9 2007/07/26 18:15:12 plunky Exp */ 20f74e101Schristos 30f74e101Schristos /* 40f74e101Schristos * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 50f74e101Schristos * The Regents of the University of California. All rights reserved. 60f74e101Schristos * 70f74e101Schristos * Copyright (c) 1999-2004 The tcpdump.org project 80f74e101Schristos * 90f74e101Schristos * Redistribution and use in source and binary forms, with or without 100f74e101Schristos * modification, are permitted provided that: (1) source code distributions 110f74e101Schristos * retain the above copyright notice and this paragraph in its entirety, (2) 120f74e101Schristos * distributions including binary code include the above copyright notice and 130f74e101Schristos * this paragraph in its entirety in the documentation or other materials 140f74e101Schristos * provided with the distribution, and (3) all advertising materials mentioning 150f74e101Schristos * features or use of this software display the following acknowledgement: 160f74e101Schristos * ``This product includes software developed by the University of California, 170f74e101Schristos * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 180f74e101Schristos * the University nor the names of its contributors may be used to endorse 190f74e101Schristos * or promote products derived from this software without specific prior 200f74e101Schristos * written permission. 210f74e101Schristos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 220f74e101Schristos * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 230f74e101Schristos * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 240f74e101Schristos */ 250f74e101Schristos 26dc860a36Sspz /* \summary: TCP printer */ 27dc860a36Sspz 2811b3aaa1Schristos #include <sys/cdefs.h> 290f74e101Schristos #ifndef lint 30*26ba0b50Schristos __RCSID("$NetBSD: print-tcp.c,v 1.12 2024/09/02 16:15:33 christos Exp $"); 310f74e101Schristos #endif 320f74e101Schristos 33c74ad251Schristos #include <config.h> 340f74e101Schristos 35c74ad251Schristos #include "netdissect-stdinc.h" 360f74e101Schristos 370f74e101Schristos #include <stdlib.h> 380f74e101Schristos #include <string.h> 390f74e101Schristos 40fdccd7e4Schristos #include "netdissect.h" 410f74e101Schristos #include "addrtoname.h" 420f74e101Schristos #include "extract.h" 430f74e101Schristos 44c74ad251Schristos #include "diag-control.h" 45c74ad251Schristos 460f74e101Schristos #include "tcp.h" 470f74e101Schristos 480f74e101Schristos #include "ip.h" 490f74e101Schristos #include "ip6.h" 500f74e101Schristos #include "ipproto.h" 510f74e101Schristos #include "rpc_auth.h" 520f74e101Schristos #include "rpc_msg.h" 530f74e101Schristos 540f74e101Schristos #ifdef HAVE_LIBCRYPTO 550f74e101Schristos #include <openssl/md5.h> 56fdccd7e4Schristos #include "signature.h" 570f74e101Schristos 58b3a00663Schristos static int tcp_verify_signature(netdissect_options *ndo, 59b3a00663Schristos const struct ip *ip, const struct tcphdr *tp, 60c74ad251Schristos const u_char *data, u_int length, const u_char *rcvsig); 610f74e101Schristos #endif 620f74e101Schristos 63c74ad251Schristos static void print_tcp_rst_data(netdissect_options *, const u_char *sp, u_int length); 64c74ad251Schristos static void print_tcp_fastopen_option(netdissect_options *ndo, const u_char *cp, 65fdccd7e4Schristos u_int datalen, int exp); 660f74e101Schristos 670f74e101Schristos #define MAX_RST_DATA_LEN 30 680f74e101Schristos 690f74e101Schristos 700f74e101Schristos struct tha { 71c74ad251Schristos nd_ipv4 src; 72c74ad251Schristos nd_ipv4 dst; 730f74e101Schristos u_int port; 740f74e101Schristos }; 750f74e101Schristos 760f74e101Schristos struct tcp_seq_hash { 770f74e101Schristos struct tcp_seq_hash *nxt; 780f74e101Schristos struct tha addr; 79c74ad251Schristos uint32_t seq; 80c74ad251Schristos uint32_t ack; 810f74e101Schristos }; 820f74e101Schristos 83870189d2Schristos struct tha6 { 84c74ad251Schristos nd_ipv6 src; 85c74ad251Schristos nd_ipv6 dst; 86870189d2Schristos u_int port; 87870189d2Schristos }; 88870189d2Schristos 89870189d2Schristos struct tcp_seq_hash6 { 90870189d2Schristos struct tcp_seq_hash6 *nxt; 91870189d2Schristos struct tha6 addr; 92c74ad251Schristos uint32_t seq; 93c74ad251Schristos uint32_t ack; 94870189d2Schristos }; 95870189d2Schristos 960f74e101Schristos #define TSEQ_HASHSIZE 919 970f74e101Schristos 98c74ad251Schristos /* These tcp options do not have the size octet */ 990f74e101Schristos #define ZEROLENOPT(o) ((o) == TCPOPT_EOL || (o) == TCPOPT_NOP) 1000f74e101Schristos 101870189d2Schristos static struct tcp_seq_hash tcp_seq_hash4[TSEQ_HASHSIZE]; 102870189d2Schristos static struct tcp_seq_hash6 tcp_seq_hash6[TSEQ_HASHSIZE]; 1030f74e101Schristos 104870189d2Schristos static const struct tok tcp_flag_values[] = { 1050f74e101Schristos { TH_FIN, "F" }, 1060f74e101Schristos { TH_SYN, "S" }, 1070f74e101Schristos { TH_RST, "R" }, 1080f74e101Schristos { TH_PUSH, "P" }, 1090f74e101Schristos { TH_ACK, "." }, 1100f74e101Schristos { TH_URG, "U" }, 1110f74e101Schristos { TH_ECNECHO, "E" }, 1120f74e101Schristos { TH_CWR, "W" }, 1130f74e101Schristos { 0, NULL } 1140f74e101Schristos }; 1150f74e101Schristos 116870189d2Schristos static const struct tok tcp_option_values[] = { 1170f74e101Schristos { TCPOPT_EOL, "eol" }, 1180f74e101Schristos { TCPOPT_NOP, "nop" }, 1190f74e101Schristos { TCPOPT_MAXSEG, "mss" }, 1200f74e101Schristos { TCPOPT_WSCALE, "wscale" }, 1210f74e101Schristos { TCPOPT_SACKOK, "sackOK" }, 1220f74e101Schristos { TCPOPT_SACK, "sack" }, 1230f74e101Schristos { TCPOPT_ECHO, "echo" }, 1240f74e101Schristos { TCPOPT_ECHOREPLY, "echoreply" }, 1250f74e101Schristos { TCPOPT_TIMESTAMP, "TS" }, 1260f74e101Schristos { TCPOPT_CC, "cc" }, 1270f74e101Schristos { TCPOPT_CCNEW, "ccnew" }, 128*26ba0b50Schristos { TCPOPT_CCECHO, "ccecho" }, 1290f74e101Schristos { TCPOPT_SIGNATURE, "md5" }, 130dc860a36Sspz { TCPOPT_SCPS, "scps" }, 1310f74e101Schristos { TCPOPT_UTO, "uto" }, 132dc860a36Sspz { TCPOPT_TCPAO, "tcp-ao" }, 133870189d2Schristos { TCPOPT_MPTCP, "mptcp" }, 134fdccd7e4Schristos { TCPOPT_FASTOPEN, "tfo" }, 135870189d2Schristos { TCPOPT_EXPERIMENT2, "exp" }, 1360f74e101Schristos { 0, NULL } 1370f74e101Schristos }; 1380f74e101Schristos 139c74ad251Schristos static uint16_t 140b3a00663Schristos tcp_cksum(netdissect_options *ndo, 141c74ad251Schristos const struct ip *ip, 142c74ad251Schristos const struct tcphdr *tp, 143c74ad251Schristos u_int len) 1440f74e101Schristos { 145b3a00663Schristos return nextproto4_cksum(ndo, ip, (const uint8_t *)tp, len, len, 146b3a00663Schristos IPPROTO_TCP); 1470f74e101Schristos } 1480f74e101Schristos 149c74ad251Schristos static uint16_t 150fdccd7e4Schristos tcp6_cksum(netdissect_options *ndo, 151c74ad251Schristos const struct ip6_hdr *ip6, 152c74ad251Schristos const struct tcphdr *tp, 153c74ad251Schristos u_int len) 154fdccd7e4Schristos { 155fdccd7e4Schristos return nextproto6_cksum(ndo, ip6, (const uint8_t *)tp, len, len, 156fdccd7e4Schristos IPPROTO_TCP); 157fdccd7e4Schristos } 158fdccd7e4Schristos 159a8e08e94Skamil UNALIGNED_OK 1600f74e101Schristos void 161b3a00663Schristos tcp_print(netdissect_options *ndo, 162c74ad251Schristos const u_char *bp, u_int length, 163c74ad251Schristos const u_char *bp2, int fragmented) 1640f74e101Schristos { 165c74ad251Schristos const struct tcphdr *tp; 166c74ad251Schristos const struct ip *ip; 167c74ad251Schristos u_char flags; 168c74ad251Schristos u_int hlen; 169c74ad251Schristos char ch; 170b3a00663Schristos uint16_t sport, dport, win, urp; 171b3a00663Schristos uint32_t seq, ack, thseq, thack; 1720f74e101Schristos u_int utoval; 173b3a00663Schristos uint16_t magic; 174c74ad251Schristos int rev; 175c74ad251Schristos const struct ip6_hdr *ip6; 176c74ad251Schristos u_int header_len; /* Header length in bytes */ 1770f74e101Schristos 178c74ad251Schristos ndo->ndo_protocol = "tcp"; 179fdccd7e4Schristos tp = (const struct tcphdr *)bp; 180fdccd7e4Schristos ip = (const struct ip *)bp2; 1810f74e101Schristos if (IP_V(ip) == 6) 182fdccd7e4Schristos ip6 = (const struct ip6_hdr *)bp2; 1830f74e101Schristos else 1840f74e101Schristos ip6 = NULL; 1850f74e101Schristos ch = '\0'; 186c74ad251Schristos if (!ND_TTEST_2(tp->th_dport)) { 187c74ad251Schristos if (ip6) { 188c74ad251Schristos ND_PRINT("%s > %s:", 189c74ad251Schristos GET_IP6ADDR_STRING(ip6->ip6_src), 190c74ad251Schristos GET_IP6ADDR_STRING(ip6->ip6_dst)); 191c74ad251Schristos } else { 192c74ad251Schristos ND_PRINT("%s > %s:", 193c74ad251Schristos GET_IPADDR_STRING(ip->ip_src), 194c74ad251Schristos GET_IPADDR_STRING(ip->ip_dst)); 195c74ad251Schristos } 196c74ad251Schristos nd_print_trunc(ndo); 1970f74e101Schristos return; 1980f74e101Schristos } 1990f74e101Schristos 200c74ad251Schristos sport = GET_BE_U_2(tp->th_sport); 201c74ad251Schristos dport = GET_BE_U_2(tp->th_dport); 2020f74e101Schristos 2030f74e101Schristos if (ip6) { 204c74ad251Schristos if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP) { 205c74ad251Schristos ND_PRINT("%s.%s > %s.%s: ", 206c74ad251Schristos GET_IP6ADDR_STRING(ip6->ip6_src), 207fdccd7e4Schristos tcpport_string(ndo, sport), 208c74ad251Schristos GET_IP6ADDR_STRING(ip6->ip6_dst), 209c74ad251Schristos tcpport_string(ndo, dport)); 2100f74e101Schristos } else { 211c74ad251Schristos ND_PRINT("%s > %s: ", 212c74ad251Schristos tcpport_string(ndo, sport), tcpport_string(ndo, dport)); 2130f74e101Schristos } 214fdccd7e4Schristos } else { 215c74ad251Schristos if (GET_U_1(ip->ip_p) == IPPROTO_TCP) { 216c74ad251Schristos ND_PRINT("%s.%s > %s.%s: ", 217c74ad251Schristos GET_IPADDR_STRING(ip->ip_src), 218fdccd7e4Schristos tcpport_string(ndo, sport), 219c74ad251Schristos GET_IPADDR_STRING(ip->ip_dst), 220c74ad251Schristos tcpport_string(ndo, dport)); 2210f74e101Schristos } else { 222c74ad251Schristos ND_PRINT("%s > %s: ", 223c74ad251Schristos tcpport_string(ndo, sport), tcpport_string(ndo, dport)); 2240f74e101Schristos } 2250f74e101Schristos } 2260f74e101Schristos 227c74ad251Schristos ND_TCHECK_SIZE(tp); 228dc860a36Sspz 229dc860a36Sspz hlen = TH_OFF(tp) * 4; 230dc860a36Sspz 2310f74e101Schristos if (hlen < sizeof(*tp)) { 232c74ad251Schristos ND_PRINT(" tcp %u [bad hdr length %u - too short, < %zu]", 233c74ad251Schristos length - hlen, hlen, sizeof(*tp)); 2340f74e101Schristos return; 2350f74e101Schristos } 2360f74e101Schristos 237c74ad251Schristos seq = GET_BE_U_4(tp->th_seq); 238c74ad251Schristos ack = GET_BE_U_4(tp->th_ack); 239c74ad251Schristos win = GET_BE_U_2(tp->th_win); 240c74ad251Schristos urp = GET_BE_U_2(tp->th_urp); 2410f74e101Schristos 242b3a00663Schristos if (ndo->ndo_qflag) { 243c74ad251Schristos ND_PRINT("tcp %u", length - hlen); 2440f74e101Schristos if (hlen > length) { 245c74ad251Schristos ND_PRINT(" [bad hdr length %u - too long, > %u]", 246c74ad251Schristos hlen, length); 2470f74e101Schristos } 2480f74e101Schristos return; 2490f74e101Schristos } 2500f74e101Schristos 251c74ad251Schristos flags = GET_U_1(tp->th_flags); 252c74ad251Schristos ND_PRINT("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags)); 2530f74e101Schristos 254b3a00663Schristos if (!ndo->ndo_Sflag && (flags & TH_ACK)) { 2550f74e101Schristos /* 2560f74e101Schristos * Find (or record) the initial sequence numbers for 2570f74e101Schristos * this conversation. (we pick an arbitrary 2580f74e101Schristos * collating order so there's only one entry for 2590f74e101Schristos * both directions). 2600f74e101Schristos */ 2610f74e101Schristos rev = 0; 2620f74e101Schristos if (ip6) { 263c74ad251Schristos struct tcp_seq_hash6 *th; 264870189d2Schristos struct tcp_seq_hash6 *tcp_seq_hash; 265c74ad251Schristos const void *src, *dst; 266870189d2Schristos struct tha6 tha; 267870189d2Schristos 268870189d2Schristos tcp_seq_hash = tcp_seq_hash6; 269c74ad251Schristos src = (const void *)ip6->ip6_src; 270c74ad251Schristos dst = (const void *)ip6->ip6_dst; 2710f74e101Schristos if (sport > dport) 2720f74e101Schristos rev = 1; 2730f74e101Schristos else if (sport == dport) { 274c74ad251Schristos if (UNALIGNED_MEMCMP(src, dst, sizeof(ip6->ip6_dst)) > 0) 2750f74e101Schristos rev = 1; 2760f74e101Schristos } 2770f74e101Schristos if (rev) { 278c74ad251Schristos UNALIGNED_MEMCPY(&tha.src, dst, sizeof(ip6->ip6_dst)); 279c74ad251Schristos UNALIGNED_MEMCPY(&tha.dst, src, sizeof(ip6->ip6_src)); 280817e9a7eSchristos tha.port = ((u_int)dport) << 16 | sport; 2810f74e101Schristos } else { 282c74ad251Schristos UNALIGNED_MEMCPY(&tha.dst, dst, sizeof(ip6->ip6_dst)); 283c74ad251Schristos UNALIGNED_MEMCPY(&tha.src, src, sizeof(ip6->ip6_src)); 284817e9a7eSchristos tha.port = ((u_int)sport) << 16 | dport; 2850f74e101Schristos } 2860f74e101Schristos 287870189d2Schristos for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; 288870189d2Schristos th->nxt; th = th->nxt) 289870189d2Schristos if (memcmp((char *)&tha, (char *)&th->addr, 290870189d2Schristos sizeof(th->addr)) == 0) 291870189d2Schristos break; 292870189d2Schristos 293870189d2Schristos if (!th->nxt || (flags & TH_SYN)) { 294870189d2Schristos /* didn't find it or new conversation */ 295c74ad251Schristos /* calloc() return used by the 'tcp_seq_hash6' 296c74ad251Schristos hash table: do not free() */ 297870189d2Schristos if (th->nxt == NULL) { 298870189d2Schristos th->nxt = (struct tcp_seq_hash6 *) 299870189d2Schristos calloc(1, sizeof(*th)); 300870189d2Schristos if (th->nxt == NULL) 301fdccd7e4Schristos (*ndo->ndo_error)(ndo, 302c74ad251Schristos S_ERR_ND_MEM_ALLOC, 303c74ad251Schristos "%s: calloc", __func__); 304870189d2Schristos } 305870189d2Schristos th->addr = tha; 306870189d2Schristos if (rev) 307870189d2Schristos th->ack = seq, th->seq = ack - 1; 308870189d2Schristos else 309870189d2Schristos th->seq = seq, th->ack = ack - 1; 310870189d2Schristos } else { 311870189d2Schristos if (rev) 312870189d2Schristos seq -= th->ack, ack -= th->seq; 313870189d2Schristos else 314870189d2Schristos seq -= th->seq, ack -= th->ack; 315870189d2Schristos } 316870189d2Schristos 317870189d2Schristos thseq = th->seq; 318870189d2Schristos thack = th->ack; 319870189d2Schristos } else { 320c74ad251Schristos struct tcp_seq_hash *th; 321870189d2Schristos struct tcp_seq_hash *tcp_seq_hash; 322870189d2Schristos struct tha tha; 323870189d2Schristos 324870189d2Schristos tcp_seq_hash = tcp_seq_hash4; 325870189d2Schristos if (sport > dport) 326870189d2Schristos rev = 1; 327870189d2Schristos else if (sport == dport) { 328c74ad251Schristos if (UNALIGNED_MEMCMP(ip->ip_src, ip->ip_dst, sizeof(ip->ip_dst)) > 0) 329870189d2Schristos rev = 1; 330870189d2Schristos } 331870189d2Schristos if (rev) { 332c74ad251Schristos UNALIGNED_MEMCPY(&tha.src, ip->ip_dst, 333c74ad251Schristos sizeof(ip->ip_dst)); 334c74ad251Schristos UNALIGNED_MEMCPY(&tha.dst, ip->ip_src, 335c74ad251Schristos sizeof(ip->ip_src)); 336817e9a7eSchristos tha.port = ((u_int)dport) << 16 | sport; 337870189d2Schristos } else { 338c74ad251Schristos UNALIGNED_MEMCPY(&tha.dst, ip->ip_dst, 339c74ad251Schristos sizeof(ip->ip_dst)); 340c74ad251Schristos UNALIGNED_MEMCPY(&tha.src, ip->ip_src, 341c74ad251Schristos sizeof(ip->ip_src)); 342817e9a7eSchristos tha.port = ((u_int)sport) << 16 | dport; 343870189d2Schristos } 344870189d2Schristos 3450f74e101Schristos for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE]; 3460f74e101Schristos th->nxt; th = th->nxt) 3470f74e101Schristos if (memcmp((char *)&tha, (char *)&th->addr, 3480f74e101Schristos sizeof(th->addr)) == 0) 3490f74e101Schristos break; 3500f74e101Schristos 3510f74e101Schristos if (!th->nxt || (flags & TH_SYN)) { 3520f74e101Schristos /* didn't find it or new conversation */ 353c74ad251Schristos /* calloc() return used by the 'tcp_seq_hash4' 354c74ad251Schristos hash table: do not free() */ 3550f74e101Schristos if (th->nxt == NULL) { 3560f74e101Schristos th->nxt = (struct tcp_seq_hash *) 3570f74e101Schristos calloc(1, sizeof(*th)); 3580f74e101Schristos if (th->nxt == NULL) 359fdccd7e4Schristos (*ndo->ndo_error)(ndo, 360c74ad251Schristos S_ERR_ND_MEM_ALLOC, 361c74ad251Schristos "%s: calloc", __func__); 3620f74e101Schristos } 3630f74e101Schristos th->addr = tha; 3640f74e101Schristos if (rev) 3650f74e101Schristos th->ack = seq, th->seq = ack - 1; 3660f74e101Schristos else 3670f74e101Schristos th->seq = seq, th->ack = ack - 1; 3680f74e101Schristos } else { 3690f74e101Schristos if (rev) 3700f74e101Schristos seq -= th->ack, ack -= th->seq; 3710f74e101Schristos else 3720f74e101Schristos seq -= th->seq, ack -= th->ack; 3730f74e101Schristos } 3740f74e101Schristos 3750f74e101Schristos thseq = th->seq; 3760f74e101Schristos thack = th->ack; 377870189d2Schristos } 3780f74e101Schristos } else { 3790f74e101Schristos /*fool gcc*/ 380870189d2Schristos thseq = thack = rev = 0; 3810f74e101Schristos } 3820f74e101Schristos if (hlen > length) { 383c74ad251Schristos ND_PRINT(" [bad hdr length %u - too long, > %u]", 384c74ad251Schristos hlen, length); 3850f74e101Schristos return; 3860f74e101Schristos } 3870f74e101Schristos 388b3a00663Schristos if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) { 3890e9868baSchristos /* Check the checksum, if possible. */ 390b3a00663Schristos uint16_t sum, tcp_sum; 3910e9868baSchristos 3920e9868baSchristos if (IP_V(ip) == 4) { 393c74ad251Schristos if (ND_TTEST_LEN(tp->th_sport, length)) { 394b3a00663Schristos sum = tcp_cksum(ndo, ip, tp, length); 395c74ad251Schristos tcp_sum = GET_BE_U_2(tp->th_sum); 3960e9868baSchristos 397c74ad251Schristos ND_PRINT(", cksum 0x%04x", tcp_sum); 3980e9868baSchristos if (sum != 0) 399c74ad251Schristos ND_PRINT(" (incorrect -> 0x%04x)", 400c74ad251Schristos in_cksum_shouldbe(tcp_sum, sum)); 4010e9868baSchristos else 402c74ad251Schristos ND_PRINT(" (correct)"); 4030f74e101Schristos } 404c74ad251Schristos } else if (IP_V(ip) == 6) { 405c74ad251Schristos if (ND_TTEST_LEN(tp->th_sport, length)) { 406fdccd7e4Schristos sum = tcp6_cksum(ndo, ip6, tp, length); 407c74ad251Schristos tcp_sum = GET_BE_U_2(tp->th_sum); 4080e9868baSchristos 409c74ad251Schristos ND_PRINT(", cksum 0x%04x", tcp_sum); 4100e9868baSchristos if (sum != 0) 411c74ad251Schristos ND_PRINT(" (incorrect -> 0x%04x)", 412c74ad251Schristos in_cksum_shouldbe(tcp_sum, sum)); 4130e9868baSchristos else 414c74ad251Schristos ND_PRINT(" (correct)"); 4150f74e101Schristos 4160f74e101Schristos } 4170f74e101Schristos } 4180e9868baSchristos } 4190f74e101Schristos 4200f74e101Schristos length -= hlen; 421b3a00663Schristos if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) { 422c74ad251Schristos ND_PRINT(", seq %u", seq); 4230f74e101Schristos 4240f74e101Schristos if (length > 0) { 425c74ad251Schristos ND_PRINT(":%u", seq + length); 4260f74e101Schristos } 4270f74e101Schristos } 4280f74e101Schristos 4290f74e101Schristos if (flags & TH_ACK) { 430c74ad251Schristos ND_PRINT(", ack %u", ack); 4310f74e101Schristos } 4320f74e101Schristos 433c74ad251Schristos ND_PRINT(", win %u", win); 4340f74e101Schristos 4350f74e101Schristos if (flags & TH_URG) 436c74ad251Schristos ND_PRINT(", urg %u", urp); 4370f74e101Schristos /* 4380f74e101Schristos * Handle any options. 4390f74e101Schristos */ 4400f74e101Schristos if (hlen > sizeof(*tp)) { 441c74ad251Schristos const u_char *cp; 442c74ad251Schristos u_int i, opt, datalen; 443c74ad251Schristos u_int len; 4440f74e101Schristos 4450f74e101Schristos hlen -= sizeof(*tp); 4460f74e101Schristos cp = (const u_char *)tp + sizeof(*tp); 447c74ad251Schristos ND_PRINT(", options ["); 4480f74e101Schristos while (hlen > 0) { 4490f74e101Schristos if (ch != '\0') 450c74ad251Schristos ND_PRINT("%c", ch); 451c74ad251Schristos opt = GET_U_1(cp); 452c74ad251Schristos cp++; 4530f74e101Schristos if (ZEROLENOPT(opt)) 4540f74e101Schristos len = 1; 4550f74e101Schristos else { 456c74ad251Schristos len = GET_U_1(cp); 457c74ad251Schristos cp++; /* total including type, len */ 4580f74e101Schristos if (len < 2 || len > hlen) 4590f74e101Schristos goto bad; 4600f74e101Schristos --hlen; /* account for length byte */ 4610f74e101Schristos } 4620f74e101Schristos --hlen; /* account for type byte */ 4630f74e101Schristos datalen = 0; 4640f74e101Schristos 4650f74e101Schristos /* Bail if "l" bytes of data are not left or were not captured */ 466c74ad251Schristos #define LENCHECK(l) { if ((l) > hlen) goto bad; ND_TCHECK_LEN(cp, l); } 4670f74e101Schristos 4680f74e101Schristos 469c74ad251Schristos ND_PRINT("%s", tok2str(tcp_option_values, "unknown-%u", opt)); 4700f74e101Schristos 4710f74e101Schristos switch (opt) { 4720f74e101Schristos 4730f74e101Schristos case TCPOPT_MAXSEG: 4740f74e101Schristos datalen = 2; 4750f74e101Schristos LENCHECK(datalen); 476c74ad251Schristos ND_PRINT(" %u", GET_BE_U_2(cp)); 4770f74e101Schristos break; 4780f74e101Schristos 4790f74e101Schristos case TCPOPT_WSCALE: 4800f74e101Schristos datalen = 1; 4810f74e101Schristos LENCHECK(datalen); 482c74ad251Schristos ND_PRINT(" %u", GET_U_1(cp)); 4830f74e101Schristos break; 4840f74e101Schristos 4850f74e101Schristos case TCPOPT_SACK: 4860f74e101Schristos datalen = len - 2; 4870f74e101Schristos if (datalen % 8 != 0) { 488c74ad251Schristos ND_PRINT(" invalid sack"); 4890f74e101Schristos } else { 490b3a00663Schristos uint32_t s, e; 4910f74e101Schristos 492c74ad251Schristos ND_PRINT(" %u ", datalen / 8); 4930f74e101Schristos for (i = 0; i < datalen; i += 8) { 4940f74e101Schristos LENCHECK(i + 4); 495c74ad251Schristos s = GET_BE_U_4(cp + i); 4960f74e101Schristos LENCHECK(i + 8); 497c74ad251Schristos e = GET_BE_U_4(cp + i + 4); 498870189d2Schristos if (rev) { 4990f74e101Schristos s -= thseq; 5000f74e101Schristos e -= thseq; 5010f74e101Schristos } else { 5020f74e101Schristos s -= thack; 5030f74e101Schristos e -= thack; 5040f74e101Schristos } 505c74ad251Schristos ND_PRINT("{%u:%u}", s, e); 5060f74e101Schristos } 5070f74e101Schristos } 5080f74e101Schristos break; 5090f74e101Schristos 5100f74e101Schristos case TCPOPT_CC: 5110f74e101Schristos case TCPOPT_CCNEW: 5120f74e101Schristos case TCPOPT_CCECHO: 5130f74e101Schristos case TCPOPT_ECHO: 5140f74e101Schristos case TCPOPT_ECHOREPLY: 5150f74e101Schristos 5160f74e101Schristos /* 5170f74e101Schristos * those options share their semantics. 5180f74e101Schristos * fall through 5190f74e101Schristos */ 5200f74e101Schristos datalen = 4; 5210f74e101Schristos LENCHECK(datalen); 522c74ad251Schristos ND_PRINT(" %u", GET_BE_U_4(cp)); 5230f74e101Schristos break; 5240f74e101Schristos 5250f74e101Schristos case TCPOPT_TIMESTAMP: 5260f74e101Schristos datalen = 8; 5270f74e101Schristos LENCHECK(datalen); 528c74ad251Schristos ND_PRINT(" val %u ecr %u", 529c74ad251Schristos GET_BE_U_4(cp), 530c74ad251Schristos GET_BE_U_4(cp + 4)); 5310f74e101Schristos break; 5320f74e101Schristos 5330f74e101Schristos case TCPOPT_SIGNATURE: 5340f74e101Schristos datalen = TCP_SIGLEN; 5350f74e101Schristos LENCHECK(datalen); 536c74ad251Schristos ND_PRINT(" "); 5370f74e101Schristos #ifdef HAVE_LIBCRYPTO 538b3a00663Schristos switch (tcp_verify_signature(ndo, ip, tp, 5390f74e101Schristos bp + TH_OFF(tp) * 4, length, cp)) { 5400f74e101Schristos 5410f74e101Schristos case SIGNATURE_VALID: 542c74ad251Schristos ND_PRINT("valid"); 5430f74e101Schristos break; 5440f74e101Schristos 5450f74e101Schristos case SIGNATURE_INVALID: 546c74ad251Schristos nd_print_invalid(ndo); 5470f74e101Schristos break; 5480f74e101Schristos 5490f74e101Schristos case CANT_CHECK_SIGNATURE: 550c74ad251Schristos ND_PRINT("can't check - "); 5510f74e101Schristos for (i = 0; i < TCP_SIGLEN; ++i) 552c74ad251Schristos ND_PRINT("%02x", 553c74ad251Schristos GET_U_1(cp + i)); 5540f74e101Schristos break; 5550f74e101Schristos } 5560f74e101Schristos #else 5570f74e101Schristos for (i = 0; i < TCP_SIGLEN; ++i) 558c74ad251Schristos ND_PRINT("%02x", GET_U_1(cp + i)); 5590f74e101Schristos #endif 5600f74e101Schristos break; 5610f74e101Schristos 562dc860a36Sspz case TCPOPT_SCPS: 563dc860a36Sspz datalen = 2; 564dc860a36Sspz LENCHECK(datalen); 565c74ad251Schristos ND_PRINT(" cap %02x id %u", GET_U_1(cp), 566c74ad251Schristos GET_U_1(cp + 1)); 5670f74e101Schristos break; 5680f74e101Schristos 569dc860a36Sspz case TCPOPT_TCPAO: 570dc860a36Sspz datalen = len - 2; 571dc860a36Sspz /* RFC 5925 Section 2.2: 572dc860a36Sspz * "The Length value MUST be greater than or equal to 4." 573dc860a36Sspz * (This includes the Kind and Length fields already processed 574dc860a36Sspz * at this point.) 575dc860a36Sspz */ 576dc860a36Sspz if (datalen < 2) { 577c74ad251Schristos nd_print_invalid(ndo); 578dc860a36Sspz } else { 579dc860a36Sspz LENCHECK(1); 580c74ad251Schristos ND_PRINT(" keyid %u", GET_U_1(cp)); 581dc860a36Sspz LENCHECK(2); 582c74ad251Schristos ND_PRINT(" rnextkeyid %u", 583c74ad251Schristos GET_U_1(cp + 1)); 584dc860a36Sspz if (datalen > 2) { 585c74ad251Schristos ND_PRINT(" mac 0x"); 586dc860a36Sspz for (i = 2; i < datalen; i++) { 587dc860a36Sspz LENCHECK(i + 1); 588c74ad251Schristos ND_PRINT("%02x", 589c74ad251Schristos GET_U_1(cp + i)); 590dc860a36Sspz } 591dc860a36Sspz } 592dc860a36Sspz } 593dc860a36Sspz break; 5940f74e101Schristos 5950f74e101Schristos case TCPOPT_EOL: 5960f74e101Schristos case TCPOPT_NOP: 5970f74e101Schristos case TCPOPT_SACKOK: 5980f74e101Schristos /* 5990f74e101Schristos * Nothing interesting. 6000f74e101Schristos * fall through 6010f74e101Schristos */ 6020f74e101Schristos break; 6030f74e101Schristos 6040f74e101Schristos case TCPOPT_UTO: 6050f74e101Schristos datalen = 2; 6060f74e101Schristos LENCHECK(datalen); 607c74ad251Schristos utoval = GET_BE_U_2(cp); 608c74ad251Schristos ND_PRINT(" 0x%x", utoval); 6090f74e101Schristos if (utoval & 0x0001) 6100f74e101Schristos utoval = (utoval >> 1) * 60; 6110f74e101Schristos else 6120f74e101Schristos utoval >>= 1; 613c74ad251Schristos ND_PRINT(" %u", utoval); 6140f74e101Schristos break; 6150f74e101Schristos 616870189d2Schristos case TCPOPT_MPTCP: 617c74ad251Schristos { 618c74ad251Schristos const u_char *snapend_save; 619c74ad251Schristos int ret; 620c74ad251Schristos 621870189d2Schristos datalen = len - 2; 622870189d2Schristos LENCHECK(datalen); 623c74ad251Schristos /* Update the snapend to the end of the option 624c74ad251Schristos * before calling mptcp_print(). Some options 625c74ad251Schristos * (MPTCP or others) may be present after a 626c74ad251Schristos * MPTCP option. This prevents that, in 627c74ad251Schristos * mptcp_print(), the remaining length < the 628c74ad251Schristos * remaining caplen. 629c74ad251Schristos */ 630c74ad251Schristos snapend_save = ndo->ndo_snapend; 631c74ad251Schristos ndo->ndo_snapend = ND_MIN(cp - 2 + len, 632c74ad251Schristos ndo->ndo_snapend); 633c74ad251Schristos ret = mptcp_print(ndo, cp - 2, len, flags); 634c74ad251Schristos ndo->ndo_snapend = snapend_save; 635c74ad251Schristos if (!ret) 636870189d2Schristos goto bad; 637870189d2Schristos break; 638c74ad251Schristos } 639870189d2Schristos 640fdccd7e4Schristos case TCPOPT_FASTOPEN: 641fdccd7e4Schristos datalen = len - 2; 642fdccd7e4Schristos LENCHECK(datalen); 643c74ad251Schristos ND_PRINT(" "); 644fdccd7e4Schristos print_tcp_fastopen_option(ndo, cp, datalen, FALSE); 645fdccd7e4Schristos break; 646fdccd7e4Schristos 647870189d2Schristos case TCPOPT_EXPERIMENT2: 648870189d2Schristos datalen = len - 2; 649870189d2Schristos LENCHECK(datalen); 650870189d2Schristos if (datalen < 2) 651870189d2Schristos goto bad; 652870189d2Schristos /* RFC6994 */ 653c74ad251Schristos magic = GET_BE_U_2(cp); 654c74ad251Schristos ND_PRINT("-"); 655870189d2Schristos 656870189d2Schristos switch(magic) { 657870189d2Schristos 658fdccd7e4Schristos case 0xf989: /* TCP Fast Open RFC 7413 */ 659fdccd7e4Schristos print_tcp_fastopen_option(ndo, cp + 2, datalen - 2, TRUE); 660870189d2Schristos break; 661870189d2Schristos 662870189d2Schristos default: 663870189d2Schristos /* Unknown magic number */ 664c74ad251Schristos ND_PRINT("%04x", magic); 665870189d2Schristos break; 666870189d2Schristos } 667870189d2Schristos break; 668870189d2Schristos 6690f74e101Schristos default: 6700f74e101Schristos datalen = len - 2; 671870189d2Schristos if (datalen) 672c74ad251Schristos ND_PRINT(" 0x"); 6730f74e101Schristos for (i = 0; i < datalen; ++i) { 674dc860a36Sspz LENCHECK(i + 1); 675c74ad251Schristos ND_PRINT("%02x", GET_U_1(cp + i)); 6760f74e101Schristos } 6770f74e101Schristos break; 6780f74e101Schristos } 6790f74e101Schristos 6800f74e101Schristos /* Account for data printed */ 6810f74e101Schristos cp += datalen; 6820f74e101Schristos hlen -= datalen; 6830f74e101Schristos 6840f74e101Schristos /* Check specification against observed length */ 6850f74e101Schristos ++datalen; /* option octet */ 6860f74e101Schristos if (!ZEROLENOPT(opt)) 6870f74e101Schristos ++datalen; /* size octet */ 6880f74e101Schristos if (datalen != len) 689c74ad251Schristos ND_PRINT("[len %u]", len); 6900f74e101Schristos ch = ','; 6910f74e101Schristos if (opt == TCPOPT_EOL) 6920f74e101Schristos break; 6930f74e101Schristos } 694c74ad251Schristos ND_PRINT("]"); 6950f74e101Schristos } 6960f74e101Schristos 6970f74e101Schristos /* 6980f74e101Schristos * Print length field before crawling down the stack. 6990f74e101Schristos */ 700c74ad251Schristos ND_PRINT(", length %u", length); 7010f74e101Schristos 702c74ad251Schristos if (length == 0) 7030f74e101Schristos return; 7040f74e101Schristos 7050f74e101Schristos /* 7060f74e101Schristos * Decode payload if necessary. 7070f74e101Schristos */ 708c74ad251Schristos header_len = TH_OFF(tp) * 4; 709c74ad251Schristos /* 710c74ad251Schristos * Do a bounds check before decoding the payload. 711c74ad251Schristos * At least the header data is required. 712c74ad251Schristos */ 713c74ad251Schristos if (!ND_TTEST_LEN(bp, header_len)) { 714c74ad251Schristos ND_PRINT(" [remaining caplen(%u) < header length(%u)]", 715c74ad251Schristos ND_BYTES_AVAILABLE_AFTER(bp), header_len); 716c74ad251Schristos nd_trunc_longjmp(ndo); 717c74ad251Schristos } 718c74ad251Schristos bp += header_len; 719b3a00663Schristos if ((flags & TH_RST) && ndo->ndo_vflag) { 720b3a00663Schristos print_tcp_rst_data(ndo, bp, length); 7210f74e101Schristos return; 7220f74e101Schristos } 7230f74e101Schristos 724b3a00663Schristos if (ndo->ndo_packettype) { 725b3a00663Schristos switch (ndo->ndo_packettype) { 726870189d2Schristos case PT_ZMTP1: 727b3a00663Schristos zmtp1_print(ndo, bp, length); 728870189d2Schristos break; 729fdccd7e4Schristos case PT_RESP: 730fdccd7e4Schristos resp_print(ndo, bp, length); 731fdccd7e4Schristos break; 732c74ad251Schristos case PT_DOMAIN: 733c74ad251Schristos /* over_tcp: TRUE, is_mdns: FALSE */ 734c74ad251Schristos domain_print(ndo, bp, length, TRUE, FALSE); 735c74ad251Schristos break; 736870189d2Schristos } 737870189d2Schristos return; 738870189d2Schristos } 739870189d2Schristos 740*26ba0b50Schristos if (IS_SRC_OR_DST_PORT(FTP_PORT)) { 741*26ba0b50Schristos ND_PRINT(": "); 742*26ba0b50Schristos ftp_print(ndo, bp, length); 743*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(SSH_PORT)) { 744*26ba0b50Schristos ssh_print(ndo, bp, length); 745*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(TELNET_PORT)) { 746b3a00663Schristos telnet_print(ndo, bp, length); 747fdccd7e4Schristos } else if (IS_SRC_OR_DST_PORT(SMTP_PORT)) { 748c74ad251Schristos ND_PRINT(": "); 749ba2ff121Schristos smtp_print(ndo, bp, length); 750c74ad251Schristos } else if (IS_SRC_OR_DST_PORT(WHOIS_PORT)) { 751c74ad251Schristos ND_PRINT(": "); 752c74ad251Schristos whois_print(ndo, bp, length); 753c74ad251Schristos } else if (IS_SRC_OR_DST_PORT(NAMESERVER_PORT)) { 754c74ad251Schristos /* over_tcp: TRUE, is_mdns: FALSE */ 755c74ad251Schristos domain_print(ndo, bp, length, TRUE, FALSE); 756*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(HTTP_PORT)) { 757*26ba0b50Schristos ND_PRINT(": "); 758*26ba0b50Schristos http_print(ndo, bp, length); 759*26ba0b50Schristos #ifdef ENABLE_SMB 760*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(NETBIOS_SSN_PORT)) { 761*26ba0b50Schristos nbt_tcp_print(ndo, bp, length); 762*26ba0b50Schristos #endif 763*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(BGP_PORT)) { 764*26ba0b50Schristos bgp_print(ndo, bp, length); 765fdccd7e4Schristos } else if (IS_SRC_OR_DST_PORT(RPKI_RTR_PORT)) { 766b3a00663Schristos rpki_rtr_print(ndo, bp, length); 767*26ba0b50Schristos #ifdef ENABLE_SMB 768*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(SMB_PORT)) { 769*26ba0b50Schristos smb_tcp_print(ndo, bp, length); 770*26ba0b50Schristos #endif 771*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(RTSP_PORT)) { 772*26ba0b50Schristos ND_PRINT(": "); 773*26ba0b50Schristos rtsp_print(ndo, bp, length); 774*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(MSDP_PORT)) { 775*26ba0b50Schristos msdp_print(ndo, bp, length); 776c74ad251Schristos } else if (IS_SRC_OR_DST_PORT(LDP_PORT)) { 777b3a00663Schristos ldp_print(ndo, bp, length); 778*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(PPTP_PORT)) 779*26ba0b50Schristos pptp_print(ndo, bp); 780*26ba0b50Schristos else if (IS_SRC_OR_DST_PORT(REDIS_PORT)) 781*26ba0b50Schristos resp_print(ndo, bp, length); 782*26ba0b50Schristos else if (IS_SRC_OR_DST_PORT(BEEP_PORT)) 783*26ba0b50Schristos beep_print(ndo, bp, length); 784*26ba0b50Schristos else if (IS_SRC_OR_DST_PORT(OPENFLOW_PORT_OLD) || IS_SRC_OR_DST_PORT(OPENFLOW_PORT_IANA)) { 785*26ba0b50Schristos openflow_print(ndo, bp, length); 786*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(HTTP_PORT_ALT)) { 787*26ba0b50Schristos ND_PRINT(": "); 788*26ba0b50Schristos http_print(ndo, bp, length); 789*26ba0b50Schristos } else if (IS_SRC_OR_DST_PORT(RTSP_PORT_ALT)) { 790*26ba0b50Schristos ND_PRINT(": "); 791*26ba0b50Schristos rtsp_print(ndo, bp, length); 792c74ad251Schristos } else if ((IS_SRC_OR_DST_PORT(NFS_PORT)) && 793c74ad251Schristos length >= 4 && ND_TTEST_4(bp)) { 794870189d2Schristos /* 795870189d2Schristos * If data present, header length valid, and NFS port used, 796870189d2Schristos * assume NFS. 797870189d2Schristos * Pass offset of data plus 4 bytes for RPC TCP msg length 798870189d2Schristos * to NFS print routines. 799870189d2Schristos */ 800b3a00663Schristos uint32_t fraglen; 801c74ad251Schristos const struct sunrpc_msg *rp; 802870189d2Schristos enum sunrpc_msg_type direction; 803870189d2Schristos 804c74ad251Schristos fraglen = GET_BE_U_4(bp) & 0x7FFFFFFF; 805870189d2Schristos if (fraglen > (length) - 4) 806870189d2Schristos fraglen = (length) - 4; 807fdccd7e4Schristos rp = (const struct sunrpc_msg *)(bp + 4); 808c74ad251Schristos if (ND_TTEST_4(rp->rm_direction)) { 809c74ad251Schristos direction = (enum sunrpc_msg_type) GET_BE_U_4(rp->rm_direction); 810870189d2Schristos if (dport == NFS_PORT && direction == SUNRPC_CALL) { 811c74ad251Schristos ND_PRINT(": NFS request xid %u ", 812c74ad251Schristos GET_BE_U_4(rp->rm_xid)); 813c74ad251Schristos nfsreq_noaddr_print(ndo, (const u_char *)rp, fraglen, (const u_char *)ip); 814870189d2Schristos return; 815870189d2Schristos } 816870189d2Schristos if (sport == NFS_PORT && direction == SUNRPC_REPLY) { 817c74ad251Schristos ND_PRINT(": NFS reply xid %u ", 818c74ad251Schristos GET_BE_U_4(rp->rm_xid)); 819c74ad251Schristos nfsreply_noaddr_print(ndo, (const u_char *)rp, fraglen, (const u_char *)ip); 820870189d2Schristos return; 821870189d2Schristos } 822870189d2Schristos } 823870189d2Schristos } 8240f74e101Schristos 8250f74e101Schristos return; 8260f74e101Schristos bad: 827c74ad251Schristos ND_PRINT("[bad opt]"); 8280f74e101Schristos if (ch != '\0') 829c74ad251Schristos ND_PRINT("]"); 8300f74e101Schristos return; 8310f74e101Schristos trunc: 832c74ad251Schristos nd_print_trunc(ndo); 8330f74e101Schristos if (ch != '\0') 834c74ad251Schristos ND_PRINT(">"); 8350f74e101Schristos } 8360f74e101Schristos 8370f74e101Schristos /* 8380f74e101Schristos * RFC1122 says the following on data in RST segments: 8390f74e101Schristos * 8400f74e101Schristos * 4.2.2.12 RST Segment: RFC-793 Section 3.4 8410f74e101Schristos * 8420f74e101Schristos * A TCP SHOULD allow a received RST segment to include data. 8430f74e101Schristos * 8440f74e101Schristos * DISCUSSION 8450f74e101Schristos * It has been suggested that a RST segment could contain 8460f74e101Schristos * ASCII text that encoded and explained the cause of the 8470f74e101Schristos * RST. No standard has yet been established for such 8480f74e101Schristos * data. 8490f74e101Schristos * 8500f74e101Schristos */ 8510f74e101Schristos 8520f74e101Schristos static void 853b3a00663Schristos print_tcp_rst_data(netdissect_options *ndo, 854c74ad251Schristos const u_char *sp, u_int length) 8550f74e101Schristos { 856c74ad251Schristos u_char c; 8570f74e101Schristos 858c74ad251Schristos ND_PRINT(ND_TTEST_LEN(sp, length) ? " [RST" : " [!RST"); 8590f74e101Schristos if (length > MAX_RST_DATA_LEN) { 8600f74e101Schristos length = MAX_RST_DATA_LEN; /* can use -X for longer */ 861c74ad251Schristos ND_PRINT("+"); /* indicate we truncate */ 8620f74e101Schristos } 863c74ad251Schristos ND_PRINT(" "); 864c74ad251Schristos while (length && sp < ndo->ndo_snapend) { 865c74ad251Schristos c = GET_U_1(sp); 866c74ad251Schristos sp++; 867c74ad251Schristos fn_print_char(ndo, c); 868c74ad251Schristos length--; 8690f74e101Schristos } 870c74ad251Schristos ND_PRINT("]"); 8710f74e101Schristos } 8720f74e101Schristos 873fdccd7e4Schristos static void 874c74ad251Schristos print_tcp_fastopen_option(netdissect_options *ndo, const u_char *cp, 875fdccd7e4Schristos u_int datalen, int exp) 876fdccd7e4Schristos { 877fdccd7e4Schristos u_int i; 878fdccd7e4Schristos 879fdccd7e4Schristos if (exp) 880c74ad251Schristos ND_PRINT("tfo"); 881fdccd7e4Schristos 882fdccd7e4Schristos if (datalen == 0) { 883fdccd7e4Schristos /* Fast Open Cookie Request */ 884c74ad251Schristos ND_PRINT(" cookiereq"); 885fdccd7e4Schristos } else { 886fdccd7e4Schristos /* Fast Open Cookie */ 887fdccd7e4Schristos if (datalen % 2 != 0 || datalen < 4 || datalen > 16) { 888c74ad251Schristos nd_print_invalid(ndo); 889fdccd7e4Schristos } else { 890c74ad251Schristos ND_PRINT(" cookie "); 891fdccd7e4Schristos for (i = 0; i < datalen; ++i) 892c74ad251Schristos ND_PRINT("%02x", GET_U_1(cp + i)); 893fdccd7e4Schristos } 894fdccd7e4Schristos } 895fdccd7e4Schristos } 896fdccd7e4Schristos 8970f74e101Schristos #ifdef HAVE_LIBCRYPTO 898c74ad251Schristos DIAG_OFF_DEPRECATION 8990f74e101Schristos static int 900b3a00663Schristos tcp_verify_signature(netdissect_options *ndo, 901b3a00663Schristos const struct ip *ip, const struct tcphdr *tp, 902c74ad251Schristos const u_char *data, u_int length, const u_char *rcvsig) 9030f74e101Schristos { 9040f74e101Schristos struct tcphdr tp1; 9050f74e101Schristos u_char sig[TCP_SIGLEN]; 9060f74e101Schristos char zero_proto = 0; 9070f74e101Schristos MD5_CTX ctx; 908b3a00663Schristos uint16_t savecsum, tlen; 909fdccd7e4Schristos const struct ip6_hdr *ip6; 910b3a00663Schristos uint32_t len32; 911b3a00663Schristos uint8_t nxt; 9120f74e101Schristos 913b3a00663Schristos if (data + length > ndo->ndo_snapend) { 914c74ad251Schristos ND_PRINT("snaplen too short, "); 9150f74e101Schristos return (CANT_CHECK_SIGNATURE); 9160f74e101Schristos } 9170f74e101Schristos 9180f74e101Schristos tp1 = *tp; 9190f74e101Schristos 920b3a00663Schristos if (ndo->ndo_sigsecret == NULL) { 921c74ad251Schristos ND_PRINT("shared secret not supplied with -M, "); 9220f74e101Schristos return (CANT_CHECK_SIGNATURE); 9230f74e101Schristos } 9240f74e101Schristos 9250f74e101Schristos MD5_Init(&ctx); 9260f74e101Schristos /* 9270f74e101Schristos * Step 1: Update MD5 hash with IP pseudo-header. 9280f74e101Schristos */ 9290f74e101Schristos if (IP_V(ip) == 4) { 930fdccd7e4Schristos MD5_Update(&ctx, (const char *)&ip->ip_src, sizeof(ip->ip_src)); 931fdccd7e4Schristos MD5_Update(&ctx, (const char *)&ip->ip_dst, sizeof(ip->ip_dst)); 932fdccd7e4Schristos MD5_Update(&ctx, (const char *)&zero_proto, sizeof(zero_proto)); 933fdccd7e4Schristos MD5_Update(&ctx, (const char *)&ip->ip_p, sizeof(ip->ip_p)); 934c74ad251Schristos tlen = GET_BE_U_2(ip->ip_len) - IP_HL(ip) * 4; 9350f74e101Schristos tlen = htons(tlen); 936fdccd7e4Schristos MD5_Update(&ctx, (const char *)&tlen, sizeof(tlen)); 9370f74e101Schristos } else if (IP_V(ip) == 6) { 938fdccd7e4Schristos ip6 = (const struct ip6_hdr *)ip; 939fdccd7e4Schristos MD5_Update(&ctx, (const char *)&ip6->ip6_src, sizeof(ip6->ip6_src)); 940fdccd7e4Schristos MD5_Update(&ctx, (const char *)&ip6->ip6_dst, sizeof(ip6->ip6_dst)); 941c74ad251Schristos len32 = htonl(GET_BE_U_2(ip6->ip6_plen)); 942fdccd7e4Schristos MD5_Update(&ctx, (const char *)&len32, sizeof(len32)); 9430f74e101Schristos nxt = 0; 944fdccd7e4Schristos MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt)); 945fdccd7e4Schristos MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt)); 946fdccd7e4Schristos MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt)); 9470f74e101Schristos nxt = IPPROTO_TCP; 948fdccd7e4Schristos MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt)); 9490f74e101Schristos } else { 950c74ad251Schristos ND_PRINT("IP version not 4 or 6, "); 9510f74e101Schristos return (CANT_CHECK_SIGNATURE); 9520f74e101Schristos } 9530f74e101Schristos 9540f74e101Schristos /* 9550f74e101Schristos * Step 2: Update MD5 hash with TCP header, excluding options. 9560f74e101Schristos * The TCP checksum must be set to zero. 9570f74e101Schristos */ 958c74ad251Schristos memcpy(&savecsum, tp1.th_sum, sizeof(savecsum)); 959c74ad251Schristos memset(tp1.th_sum, 0, sizeof(tp1.th_sum)); 960fdccd7e4Schristos MD5_Update(&ctx, (const char *)&tp1, sizeof(struct tcphdr)); 961c74ad251Schristos memcpy(tp1.th_sum, &savecsum, sizeof(tp1.th_sum)); 9620f74e101Schristos /* 9630f74e101Schristos * Step 3: Update MD5 hash with TCP segment data, if present. 9640f74e101Schristos */ 9650f74e101Schristos if (length > 0) 9660f74e101Schristos MD5_Update(&ctx, data, length); 9670f74e101Schristos /* 9680f74e101Schristos * Step 4: Update MD5 hash with shared secret. 9690f74e101Schristos */ 970b3a00663Schristos MD5_Update(&ctx, ndo->ndo_sigsecret, strlen(ndo->ndo_sigsecret)); 9710f74e101Schristos MD5_Final(sig, &ctx); 9720f74e101Schristos 9730f74e101Schristos if (memcmp(rcvsig, sig, TCP_SIGLEN) == 0) 9740f74e101Schristos return (SIGNATURE_VALID); 9750f74e101Schristos else 9760f74e101Schristos return (SIGNATURE_INVALID); 9770f74e101Schristos } 978c74ad251Schristos DIAG_ON_DEPRECATION 9790f74e101Schristos #endif /* HAVE_LIBCRYPTO */ 980