Ejemplo n.º 1
0
void
tcp_print(const u_char *bp, u_int length, const u_char *bp2)
{
	const struct tcphdr *tp;
	const struct ip *ip;
	u_char flags;
	int hlen;
	char ch;
	struct tcp_seq_hash *th = NULL;
	int rev = 0;
	u_int16_t sport, dport, win, urp;
	tcp_seq seq, ack;
#ifdef INET6
	const struct ip6_hdr *ip6;
#endif

	tp = (struct tcphdr *)bp;
	switch (((struct ip *)bp2)->ip_v) {
	case 4:
		ip = (struct ip *)bp2;
#ifdef INET6
		ip6 = NULL;
#endif
		break;
#ifdef INET6
	case 6:
		ip = NULL;
		ip6 = (struct ip6_hdr *)bp2;
		break;
#endif
	default:
		(void)printf("invalid ip version");
		return;
	}

	ch = '\0';
	if (length < sizeof(*tp)) {
		(void)printf("truncated-tcp %u", length);
		return;
	}

	if (!TTEST(tp->th_dport)) {
#ifdef INET6
		if (ip6) {
			(void)printf("%s > %s: [|tcp]",
				ip6addr_string(&ip6->ip6_src),
				ip6addr_string(&ip6->ip6_dst));
		} else
#endif /*INET6*/
		{
			(void)printf("%s > %s: [|tcp]",
				ipaddr_string(&ip->ip_src),
				ipaddr_string(&ip->ip_dst));
		}
		return;
	}

	sport = ntohs(tp->th_sport);
	dport = ntohs(tp->th_dport);

#ifdef INET6
	if (ip6) {
		if (ip6->ip6_nxt == IPPROTO_TCP) {
			(void)printf("%s.%s > %s.%s: ",
				ip6addr_string(&ip6->ip6_src),
				tcpport_string(sport),
				ip6addr_string(&ip6->ip6_dst),
				tcpport_string(dport));
		} else {
			(void)printf("%s > %s: ",
				tcpport_string(sport), tcpport_string(dport));
		}
	} else
#endif /*INET6*/
	{
		if (ip->ip_p == IPPROTO_TCP) {
			(void)printf("%s.%s > %s.%s: ",
				ipaddr_string(&ip->ip_src),
				tcpport_string(sport),
				ipaddr_string(&ip->ip_dst),
				tcpport_string(dport));
		} else {
			(void)printf("%s > %s: ",
				tcpport_string(sport), tcpport_string(dport));
		}
	}

	if (!qflag && TTEST(tp->th_seq) && !TTEST(tp->th_ack))
		(void)printf("%u ", ntohl(tp->th_seq));

	TCHECK(*tp);
	seq = ntohl(tp->th_seq);
	ack = ntohl(tp->th_ack);
	win = ntohs(tp->th_win);
	urp = ntohs(tp->th_urp);
	hlen = tp->th_off * 4;

	if (qflag) {
		(void)printf("tcp %d", length - tp->th_off * 4);
		return;
	} else if (packettype != PT_TCP) {

		/*
		 * If data present and NFS port used, assume NFS.
		 * Pass offset of data plus 4 bytes for RPC TCP msg length
		 * to NFS print routines.
		 */
		u_int len = length - hlen;
		if ((u_char *)tp + 4 + sizeof(struct rpc_msg) <= snapend &&
		    dport == NFS_PORT) {
			nfsreq_print((u_char *)tp + hlen + 4, len, bp2);
			return;
		} else if ((u_char *)tp + 4 + 
		    sizeof(struct rpc_msg) <= snapend && sport == NFS_PORT) {
			nfsreply_print((u_char *)tp + hlen + 4, len, bp2);
			return;
		}
	}
	if ((flags = tp->th_flags) & (TH_SYN|TH_FIN|TH_RST|TH_PUSH|
				      TH_ECNECHO|TH_CWR)) {
		if (flags & TH_SYN)
			putchar('S');
		if (flags & TH_FIN)
			putchar('F');
		if (flags & TH_RST)
			putchar('R');
		if (flags & TH_PUSH)
			putchar('P');
		if (flags & TH_CWR)
			putchar('W');	/* congestion _W_indow reduced (ECN) */
		if (flags & TH_ECNECHO)
			putchar('E');	/* ecn _E_cho sent (ECN) */
	} else
		putchar('.');

	if (!Sflag && (flags & TH_ACK)) {
		struct tha tha;
		/*
		 * Find (or record) the initial sequence numbers for
		 * this conversation.  (we pick an arbitrary
		 * collating order so there's only one entry for
		 * both directions).
		 */
#ifdef INET6
		bzero(&tha, sizeof(tha));
		rev = 0;
		if (ip6) {
			if (sport > dport) {
				rev = 1;
			} else if (sport == dport) {
			    int i;

			    for (i = 0; i < 4; i++) {
				if (((u_int32_t *)(&ip6->ip6_src))[i] >
				    ((u_int32_t *)(&ip6->ip6_dst))[i]) {
					rev = 1;
					break;
				}
			    }
			}
			if (rev) {
				tha.src = ip6->ip6_dst;
				tha.dst = ip6->ip6_src;
				tha.port = dport << 16 | sport;
			} else {
				tha.dst = ip6->ip6_dst;
				tha.src = ip6->ip6_src;
				tha.port = sport << 16 | dport;
			}
		} else {
			if (sport > dport ||
			    (sport == dport &&
			     ip->ip_src.s_addr > ip->ip_dst.s_addr)) {
				rev = 1;
			}
			if (rev) {
				*(struct in_addr *)&tha.src = ip->ip_dst;
				*(struct in_addr *)&tha.dst = ip->ip_src;
				tha.port = dport << 16 | sport;
			} else {
				*(struct in_addr *)&tha.dst = ip->ip_dst;
				*(struct in_addr *)&tha.src = ip->ip_src;
				tha.port = sport << 16 | dport;
			}
		}
#else
		if (sport < dport ||
		    (sport == dport &&
		     ip->ip_src.s_addr < ip->ip_dst.s_addr)) {
			tha.src = ip->ip_src, tha.dst = ip->ip_dst;
			tha.port = sport << 16 | dport;
			rev = 0;
		} else {
			tha.src = ip->ip_dst, tha.dst = ip->ip_src;
			tha.port = dport << 16 | sport;
			rev = 1;
		}
#endif

		for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
		     th->nxt; th = th->nxt)
			if (!memcmp((char *)&tha, (char *)&th->addr,
				  sizeof(th->addr)))
				break;

		if (!th->nxt || flags & TH_SYN) {
			/* didn't find it or new conversation */
			if (th->nxt == NULL) {
				th->nxt = calloc(1, sizeof(*th));
				if (th->nxt == NULL)
					error("tcp_print: calloc");
			}
			th->addr = tha;
			if (rev)
				th->ack = seq, th->seq = ack - 1;
			else
				th->seq = seq, th->ack = ack - 1;
		} else {
			if (rev)
				seq -= th->ack, ack -= th->seq;
			else
				seq -= th->seq, ack -= th->ack;
		}
	}
	hlen = tp->th_off * 4;
	if (hlen > length) {
		(void)printf(" [bad hdr length]");
		return;
	}

	if (ip && ip->ip_v == 4 && vflag) {
		if (TTEST2(tp->th_sport, length)) {
			u_int16_t sum, tcp_sum;
			sum = tcp_cksum(ip, tp, length);
			if (sum != 0) {
				tcp_sum = EXTRACT_16BITS(&tp->th_sum);
				(void)printf(" [bad tcp cksum %x! -> %x]", tcp_sum,
				    in_cksum_shouldbe(tcp_sum, sum));
			} else
				(void)printf(" [tcp sum ok]");
		}
	}
#ifdef INET6
	if (ip6 && ip6->ip6_plen && vflag) {
		if (TTEST2(tp->th_sport, length)) {
			u_int16_t sum, tcp_sum;
			sum = tcp6_cksum(ip6, tp, length);
			if (sum != 0) {
				tcp_sum = EXTRACT_16BITS(&tp->th_sum);
				(void)printf(" [bad tcp cksum %x! -> %x]", tcp_sum,
				    in_cksum_shouldbe(tcp_sum, sum));
			} else
				(void)printf(" [tcp sum ok]");
		}
	}
#endif

	/* OS Fingerprint */
	if (oflag && (flags & (TH_SYN|TH_ACK)) == TH_SYN) {
		struct pf_osfp_enlist *head = NULL;
		struct pf_osfp_entry *fp;
		unsigned long left;
		left = (unsigned long)(snapend - (const u_char *)tp);

		if (left >= hlen)
			head = pf_osfp_fingerprint_hdr(ip, ip6, tp);
		if (head) {
			int prev = 0;
			printf(" (src OS:");
			SLIST_FOREACH(fp, head, fp_entry) {
				if (fp->fp_enflags & PF_OSFP_EXPANDED)
					continue;
				if (prev)
					printf(",");
				printf(" %s", fp->fp_class_nm);
				if (fp->fp_version_nm[0])
					printf(" %s", fp->fp_version_nm);
				if (fp->fp_subtype_nm[0])
					printf(" %s", fp->fp_subtype_nm);
				prev = 1;
			}
			printf(")");
		} else {
			if (left < hlen)
				printf(" (src OS: short-pkt)");
			else
				printf(" (src OS: unknown)");
		}
	}
Ejemplo n.º 2
0
void
tcp_print(register const u_char *bp, register u_int length,
	  register const u_char *bp2, int fragmented)
{
        register const struct tcphdr *tp;
        register const struct ip *ip;
        register u_char flags;
        register u_int hlen;
        register char ch;
        u_int16_t sport, dport, win, urp;
        u_int32_t seq, ack, thseq, thack;
        u_int utoval;
        int threv;
#ifdef INET6
        register const struct ip6_hdr *ip6;
#endif

        tp = (struct tcphdr *)bp;
        ip = (struct ip *)bp2;
#ifdef INET6
        if (IP_V(ip) == 6)
                ip6 = (struct ip6_hdr *)bp2;
        else
                ip6 = NULL;
#endif /*INET6*/
        ch = '\0';
        if (!TTEST(tp->th_dport)) {
                (void)printf("%s > %s: [|tcp]",
                             ipaddr_string(&ip->ip_src),
                             ipaddr_string(&ip->ip_dst));
                return;
        }

        sport = EXTRACT_16BITS(&tp->th_sport);
        dport = EXTRACT_16BITS(&tp->th_dport);

        hlen = TH_OFF(tp) * 4;

        /*
	 * If data present, header length valid, and NFS port used,
	 * assume NFS.
	 * Pass offset of data plus 4 bytes for RPC TCP msg length
	 * to NFS print routines.
	 */
	if (!qflag && hlen >= sizeof(*tp) && hlen <= length &&
	    (length - hlen) >= 4) {
		u_char *fraglenp;
		u_int32_t fraglen;
		register struct sunrpc_msg *rp;
		enum sunrpc_msg_type direction;

		fraglenp = (u_char *)tp + hlen;
		if (TTEST2(*fraglenp, 4)) {
			fraglen = EXTRACT_32BITS(fraglenp) & 0x7FFFFFFF;
			if (fraglen > (length - hlen) - 4)
				fraglen = (length - hlen) - 4;
			rp = (struct sunrpc_msg *)(fraglenp + 4);
			if (TTEST(rp->rm_direction)) {
				direction = (enum sunrpc_msg_type)EXTRACT_32BITS(&rp->rm_direction);
				if (dport == NFS_PORT &&
				    direction == SUNRPC_CALL) {
					nfsreq_print((u_char *)rp, fraglen,
					    (u_char *)ip);
					return;
				}
				if (sport == NFS_PORT &&
				    direction == SUNRPC_REPLY) {
					nfsreply_print((u_char *)rp, fraglen,
					    (u_char *)ip);
					return;
				}
			}
                }
        }
#ifdef INET6
        if (ip6) {
                if (ip6->ip6_nxt == IPPROTO_TCP) {
                        (void)printf("%s.%s > %s.%s: ",
                                     ip6addr_string(&ip6->ip6_src),
                                     tcpport_string(sport),
                                     ip6addr_string(&ip6->ip6_dst),
                                     tcpport_string(dport));
                } else {
                        (void)printf("%s > %s: ",
                                     tcpport_string(sport), tcpport_string(dport));
                }
        } else
#endif /*INET6*/
        {
                if (ip->ip_p == IPPROTO_TCP) {
                        (void)printf("%s.%s > %s.%s: ",
                                     ipaddr_string(&ip->ip_src),
                                     tcpport_string(sport),
                                     ipaddr_string(&ip->ip_dst),
                                     tcpport_string(dport));
                } else {
                        (void)printf("%s > %s: ",
                                     tcpport_string(sport), tcpport_string(dport));
                }
        }

        if (hlen < sizeof(*tp)) {
                (void)printf(" tcp %d [bad hdr length %u - too short, < %lu]",
                             length - hlen, hlen, (unsigned long)sizeof(*tp));
                return;
        }

        TCHECK(*tp);

        seq = EXTRACT_32BITS(&tp->th_seq);
        ack = EXTRACT_32BITS(&tp->th_ack);
        win = EXTRACT_16BITS(&tp->th_win);
        urp = EXTRACT_16BITS(&tp->th_urp);

        if (qflag) {
                (void)printf("tcp %d", length - hlen);
                if (hlen > length) {
                        (void)printf(" [bad hdr length %u - too long, > %u]",
                                     hlen, length);
                }
                return;
        }

        flags = tp->th_flags;
        printf("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags));

        if (!Sflag && (flags & TH_ACK)) {
                register struct tcp_seq_hash *th;
                const void *src, *dst;
                register int rev;
                struct tha tha;
                /*
                 * Find (or record) the initial sequence numbers for
                 * this conversation.  (we pick an arbitrary
                 * collating order so there's only one entry for
                 * both directions).
                 */
#ifdef INET6
                rev = 0;
                if (ip6) {
                        src = &ip6->ip6_src;
                        dst = &ip6->ip6_dst;
                        if (sport > dport)
                                rev = 1;
                        else if (sport == dport) {
                                if (memcmp(src, dst, sizeof ip6->ip6_dst) > 0)
                                        rev = 1;
                        }
                        if (rev) {
                                memcpy(&tha.src, dst, sizeof ip6->ip6_dst);
                                memcpy(&tha.dst, src, sizeof ip6->ip6_src);
                                tha.port = dport << 16 | sport;
                        } else {
                                memcpy(&tha.dst, dst, sizeof ip6->ip6_dst);
                                memcpy(&tha.src, src, sizeof ip6->ip6_src);
                                tha.port = sport << 16 | dport;
                        }
                } else {
                        /*
                         * Zero out the tha structure; the src and dst
                         * fields are big enough to hold an IPv6
                         * address, but we only have IPv4 addresses
                         * and thus must clear out the remaining 124
                         * bits.
                         *
                         * XXX - should we just clear those bytes after
                         * copying the IPv4 addresses, rather than
                         * zeroing out the entire structure and then
                         * overwriting some of the zeroes?
                         *
                         * XXX - this could fail if we see TCP packets
                         * with an IPv6 address with the lower 124 bits
                         * all zero and also see TCP packes with an
                         * IPv4 address with the same 32 bits as the
                         * upper 32 bits of the IPv6 address in question.
                         * Can that happen?  Is it likely enough to be
                         * an issue?
                         */
                        memset(&tha, 0, sizeof(tha));
                        src = &ip->ip_src;
                        dst = &ip->ip_dst;
                        if (sport > dport)
                                rev = 1;
                        else if (sport == dport) {
                                if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
                                        rev = 1;
                        }
                        if (rev) {
                                memcpy(&tha.src, dst, sizeof ip->ip_dst);
                                memcpy(&tha.dst, src, sizeof ip->ip_src);
                                tha.port = dport << 16 | sport;
                        } else {
                                memcpy(&tha.dst, dst, sizeof ip->ip_dst);
                                memcpy(&tha.src, src, sizeof ip->ip_src);
                                tha.port = sport << 16 | dport;
                        }
                }
#else
                rev = 0;
                src = &ip->ip_src;
                dst = &ip->ip_dst;
                if (sport > dport)
                        rev = 1;
                else if (sport == dport) {
                        if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
                                rev = 1;
                }
                if (rev) {
                        memcpy(&tha.src, dst, sizeof ip->ip_dst);
                        memcpy(&tha.dst, src, sizeof ip->ip_src);
                        tha.port = dport << 16 | sport;
                } else {
                        memcpy(&tha.dst, dst, sizeof ip->ip_dst);
                        memcpy(&tha.src, src, sizeof ip->ip_src);
                        tha.port = sport << 16 | dport;
                }
#endif

                threv = rev;
                for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
                     th->nxt; th = th->nxt)
                        if (memcmp((char *)&tha, (char *)&th->addr,
                                   sizeof(th->addr)) == 0)
                                break;

                if (!th->nxt || (flags & TH_SYN)) {
                        /* didn't find it or new conversation */
                        if (th->nxt == NULL) {
                                th->nxt = (struct tcp_seq_hash *)
                                        calloc(1, sizeof(*th));
                                if (th->nxt == NULL)
                                        error("tcp_print: calloc");
                        }
                        th->addr = tha;
                        if (rev)
                                th->ack = seq, th->seq = ack - 1;
                        else
                                th->seq = seq, th->ack = ack - 1;
                } else {
                        if (rev)
                                seq -= th->ack, ack -= th->seq;
                        else
                                seq -= th->seq, ack -= th->ack;
                }

                thseq = th->seq;
                thack = th->ack;
        } else {
                /*fool gcc*/
                thseq = thack = threv = 0;
        }
        if (hlen > length) {
                (void)printf(" [bad hdr length %u - too long, > %u]",
                             hlen, length);
                return;
        }

        if (IP_V(ip) == 4 && vflag && !Kflag && !fragmented) {
                u_int16_t sum, tcp_sum;
                if (TTEST2(tp->th_sport, length)) {
                        sum = tcp_cksum(ip, tp, length);

                        (void)printf(", cksum 0x%04x",EXTRACT_16BITS(&tp->th_sum));
                        if (sum != 0) {
                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);
                                (void)printf(" (incorrect -> 0x%04x)",in_cksum_shouldbe(tcp_sum, sum));
                        } else
                                (void)printf(" (correct)");
                }
        }
#ifdef INET6
        if (IP_V(ip) == 6 && ip6->ip6_plen && vflag && !Kflag && !fragmented) {
                u_int16_t sum,tcp_sum;
                if (TTEST2(tp->th_sport, length)) {
                        sum = nextproto6_cksum(ip6, (u_short *)tp, length, IPPROTO_TCP);
                        (void)printf(", cksum 0x%04x",EXTRACT_16BITS(&tp->th_sum));
                        if (sum != 0) {
                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);
                                (void)printf(" (incorrect -> 0x%04x)",in_cksum_shouldbe(tcp_sum, sum));
                        } else
                                (void)printf(" (correct)");

                }
        }
#endif

        length -= hlen;
        if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) {
                (void)printf(", seq %u", seq);

                if (length > 0) {
                        (void)printf(":%u", seq + length);
                }
        }

        if (flags & TH_ACK) {
                (void)printf(", ack %u", ack);
        }

        (void)printf(", win %d", win);

        if (flags & TH_URG)
                (void)printf(", urg %d", urp);
        /*
         * Handle any options.
         */
        if (hlen > sizeof(*tp)) {
                register const u_char *cp;
                register u_int i, opt, datalen;
                register u_int len;

                hlen -= sizeof(*tp);
                cp = (const u_char *)tp + sizeof(*tp);
                printf(", options [");
                while (hlen > 0) {
                        if (ch != '\0')
                                putchar(ch);
                        TCHECK(*cp);
                        opt = *cp++;
                        if (ZEROLENOPT(opt))
                                len = 1;
                        else {
                                TCHECK(*cp);
                                len = *cp++;	/* total including type, len */
                                if (len < 2 || len > hlen)
                                        goto bad;
                                --hlen;		/* account for length byte */
                        }
                        --hlen;			/* account for type byte */
                        datalen = 0;

/* Bail if "l" bytes of data are not left or were not captured  */
#define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); }


                        printf("%s", tok2str(tcp_option_values, "Unknown Option %u", opt));

                        switch (opt) {

                        case TCPOPT_MAXSEG:
                                datalen = 2;
                                LENCHECK(datalen);
                                (void)printf(" %u", EXTRACT_16BITS(cp));
                                break;

                        case TCPOPT_WSCALE:
                                datalen = 1;
                                LENCHECK(datalen);
                                (void)printf(" %u", *cp);
                                break;

                        case TCPOPT_SACK:
                                datalen = len - 2;
                                if (datalen % 8 != 0) {
                                        (void)printf("malformed sack");
                                } else {
                                        u_int32_t s, e;

                                        (void)printf(" %d ", datalen / 8);
                                        for (i = 0; i < datalen; i += 8) {
                                                LENCHECK(i + 4);
                                                s = EXTRACT_32BITS(cp + i);
                                                LENCHECK(i + 8);
                                                e = EXTRACT_32BITS(cp + i + 4);
                                                if (threv) {
                                                        s -= thseq;
                                                        e -= thseq;
                                                } else {
                                                        s -= thack;
                                                        e -= thack;
                                                }
                                                (void)printf("{%u:%u}", s, e);
                                        }
                                }
                                break;

                        case TCPOPT_CC:
                        case TCPOPT_CCNEW:
                        case TCPOPT_CCECHO:
                        case TCPOPT_ECHO:
                        case TCPOPT_ECHOREPLY:

                                /*
                                 * those options share their semantics.
                                 * fall through
                                 */
                                datalen = 4;
                                LENCHECK(datalen);
                                (void)printf(" %u", EXTRACT_32BITS(cp));
                                break;

                        case TCPOPT_TIMESTAMP:
                                datalen = 8;
                                LENCHECK(datalen);
                                (void)printf(" val %u ecr %u",
                                             EXTRACT_32BITS(cp),
                                             EXTRACT_32BITS(cp + 4));
                                break;

                        case TCPOPT_SIGNATURE:
                                datalen = TCP_SIGLEN;
                                LENCHECK(datalen);
#ifdef HAVE_LIBCRYPTO
                                switch (tcp_verify_signature(ip, tp,
                                                             bp + TH_OFF(tp) * 4, length, cp)) {

                                case SIGNATURE_VALID:
                                        (void)printf("valid");
                                        break;

                                case SIGNATURE_INVALID:
                                        (void)printf("invalid");
                                        break;

                                case CANT_CHECK_SIGNATURE:
                                        (void)printf("can't check - ");
                                        for (i = 0; i < TCP_SIGLEN; ++i)
                                                (void)printf("%02x", cp[i]);
                                        break;
                                }
#else
                                for (i = 0; i < TCP_SIGLEN; ++i)
                                        (void)printf("%02x", cp[i]);
#endif
                                break;

                        case TCPOPT_AUTH:
                                (void)printf("keyid %d", *cp++);
                                datalen = len - 3;
                                for (i = 0; i < datalen; ++i) {
                                        LENCHECK(i);
                                        (void)printf("%02x", cp[i]);
                                }
                                break;


                        case TCPOPT_EOL:
                        case TCPOPT_NOP:
                        case TCPOPT_SACKOK:
                                /*
                                 * Nothing interesting.
                                 * fall through
                                 */
                                break;

                        case TCPOPT_UTO:
                                datalen = 2;
                                LENCHECK(datalen);
                                utoval = EXTRACT_16BITS(cp);
                                (void)printf("0x%x", utoval);
                                if (utoval & 0x0001)
                                        utoval = (utoval >> 1) * 60;
                                else
                                        utoval >>= 1;
                                (void)printf(" %u", utoval);
                                break;

                        default:
                                datalen = len - 2;
                                for (i = 0; i < datalen; ++i) {
                                        LENCHECK(i);
                                        (void)printf("%02x", cp[i]);
                                }
                                break;
                        }

                        /* Account for data printed */
                        cp += datalen;
                        hlen -= datalen;

                        /* Check specification against observed length */
                        ++datalen;			/* option octet */
                        if (!ZEROLENOPT(opt))
                                ++datalen;		/* size octet */
                        if (datalen != len)
                                (void)printf("[len %d]", len);
                        ch = ',';
                        if (opt == TCPOPT_EOL)
                                break;
                }
                putchar(']');
        }
Ejemplo n.º 3
0
void
tcp_print(register const u_char *bp, register u_int length,
	  register const u_char *bp2, int fragmented)
{
        register const struct tcphdr *tp;
        register const struct ip *ip;
        register u_char flags;
        register u_int hlen;
        register char ch;
        u_int16_t sport, dport, win, urp;
        u_int32_t seq, ack, thseq, thack;
        u_int utoval;
        int threv;
#ifdef INET6
        register const struct ip6_hdr *ip6;
#endif

        tp = (struct tcphdr *)bp;
        ip = (struct ip *)bp2;
#ifdef INET6
        if (IP_V(ip) == 6)
                ip6 = (struct ip6_hdr *)bp2;
        else
                ip6 = NULL;
#endif /*INET6*/
        ch = '\0';
        if (!TTEST(tp->th_dport)) {
                (void)printf("%s > %s: [|tcp]",
                             ipaddr_string(&ip->ip_src),
                             ipaddr_string(&ip->ip_dst));
                return;
        }

        sport = EXTRACT_16BITS(&tp->th_sport);
        dport = EXTRACT_16BITS(&tp->th_dport);

        hlen = TH_OFF(tp) * 4;

        /*
	 * If data present, header length valid, and NFS port used,
	 * assume NFS.
	 * Pass offset of data plus 4 bytes for RPC TCP msg length
	 * to NFS print routines.
	 */
	if (!qflag && hlen >= sizeof(*tp) && hlen <= length &&
	    (length - hlen) >= 4) {
		u_char *fraglenp;
		u_int32_t fraglen;
		register struct sunrpc_msg *rp;
		enum sunrpc_msg_type direction;

		fraglenp = (u_char *)tp + hlen;
		if (TTEST2(*fraglenp, 4)) {
			fraglen = EXTRACT_32BITS(fraglenp) & 0x7FFFFFFF;
			if (fraglen > (length - hlen) - 4)
				fraglen = (length - hlen) - 4;
			rp = (struct sunrpc_msg *)(fraglenp + 4);
			if (TTEST(rp->rm_direction)) {
				direction = (enum sunrpc_msg_type)EXTRACT_32BITS(&rp->rm_direction);
				if (dport == NFS_PORT &&
				    direction == SUNRPC_CALL) {
					nfsreq_print((u_char *)rp, fraglen,
					    (u_char *)ip);
					return;
				}
				if (sport == NFS_PORT &&
				    direction == SUNRPC_REPLY) {
					nfsreply_print((u_char *)rp, fraglen,
					    (u_char *)ip);
					return;
				}
			}
                }
        }
#ifdef INET6
        if (ip6) {
                if (ip6->ip6_nxt == IPPROTO_TCP) {
                        (void)printf("%s.%s > %s.%s: ",
                                     ip6addr_string(&ip6->ip6_src),
                                     tcpport_string(sport),
                                     ip6addr_string(&ip6->ip6_dst),
                                     tcpport_string(dport));
                } else {
                        (void)printf("%s > %s: ",
                                     tcpport_string(sport), tcpport_string(dport));
                }
        } else
#endif /*INET6*/
        {
                if (ip->ip_p == IPPROTO_TCP) {
                        (void)printf("%s.%s > %s.%s: ",
                                     ipaddr_string(&ip->ip_src),
                                     tcpport_string(sport),
                                     ipaddr_string(&ip->ip_dst),
                                     tcpport_string(dport));
                } else {
                        (void)printf("%s > %s: ",
                                     tcpport_string(sport), tcpport_string(dport));
                }
        }

        if (hlen < sizeof(*tp)) {
                (void)printf(" tcp %d [bad hdr length %u - too short, < %lu]",
                             length - hlen, hlen, (unsigned long)sizeof(*tp));
                return;
        }

        TCHECK(*tp);

        seq = EXTRACT_32BITS(&tp->th_seq);
        ack = EXTRACT_32BITS(&tp->th_ack);
        win = EXTRACT_16BITS(&tp->th_win);
        urp = EXTRACT_16BITS(&tp->th_urp);

        if (qflag) {
                (void)printf("tcp %d", length - hlen);
                if (hlen > length) {
                        (void)printf(" [bad hdr length %u - too long, > %u]",
                                     hlen, length);
                }
                return;
        }

        flags = tp->th_flags;
        printf("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags));

        if (!Sflag && (flags & TH_ACK)) {
                register struct tcp_seq_hash *th;
                const void *src, *dst;
                register int rev;
                struct tha tha;
                /*
                 * Find (or record) the initial sequence numbers for
                 * this conversation.  (we pick an arbitrary
                 * collating order so there's only one entry for
                 * both directions).
                 */
#ifdef INET6
                rev = 0;
                if (ip6) {
                        src = &ip6->ip6_src;
                        dst = &ip6->ip6_dst;
                        if (sport > dport)
                                rev = 1;
                        else if (sport == dport) {
                                if (memcmp(src, dst, sizeof ip6->ip6_dst) > 0)
                                        rev = 1;
                        }
                        if (rev) {
                                memcpy(&tha.src, dst, sizeof ip6->ip6_dst);
                                memcpy(&tha.dst, src, sizeof ip6->ip6_src);
                                tha.port = dport << 16 | sport;
                        } else {
                                memcpy(&tha.dst, dst, sizeof ip6->ip6_dst);
                                memcpy(&tha.src, src, sizeof ip6->ip6_src);
                                tha.port = sport << 16 | dport;
                        }
                } else {
                        /*
                         * Zero out the tha structure; the src and dst
                         * fields are big enough to hold an IPv6
                         * address, but we only have IPv4 addresses
                         * and thus must clear out the remaining 124
                         * bits.
                         *
                         * XXX - should we just clear those bytes after
                         * copying the IPv4 addresses, rather than
                         * zeroing out the entire structure and then
                         * overwriting some of the zeroes?
                         *
                         * XXX - this could fail if we see TCP packets
                         * with an IPv6 address with the lower 124 bits
                         * all zero and also see TCP packes with an
                         * IPv4 address with the same 32 bits as the
                         * upper 32 bits of the IPv6 address in question.
                         * Can that happen?  Is it likely enough to be
                         * an issue?
                         */
                        memset(&tha, 0, sizeof(tha));
                        src = &ip->ip_src;
                        dst = &ip->ip_dst;
                        if (sport > dport)
                                rev = 1;
                        else if (sport == dport) {
                                if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
                                        rev = 1;
                        }
                        if (rev) {
                                memcpy(&tha.src, dst, sizeof ip->ip_dst);
                                memcpy(&tha.dst, src, sizeof ip->ip_src);
                                tha.port = dport << 16 | sport;
                        } else {
                                memcpy(&tha.dst, dst, sizeof ip->ip_dst);
                                memcpy(&tha.src, src, sizeof ip->ip_src);
                                tha.port = sport << 16 | dport;
                        }
                }
#else
                rev = 0;
                src = &ip->ip_src;
                dst = &ip->ip_dst;
                if (sport > dport)
                        rev = 1;
                else if (sport == dport) {
                        if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
                                rev = 1;
                }
                if (rev) {
                        memcpy(&tha.src, dst, sizeof ip->ip_dst);
                        memcpy(&tha.dst, src, sizeof ip->ip_src);
                        tha.port = dport << 16 | sport;
                } else {
                        memcpy(&tha.dst, dst, sizeof ip->ip_dst);
                        memcpy(&tha.src, src, sizeof ip->ip_src);
                        tha.port = sport << 16 | dport;
                }
#endif

                threv = rev;
                for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
                     th->nxt; th = th->nxt)
                        if (memcmp((char *)&tha, (char *)&th->addr,
                                   sizeof(th->addr)) == 0)
                                break;

                if (!th->nxt || (flags & TH_SYN)) {
                        /* didn't find it or new conversation */
                        if (th->nxt == NULL) {
                                th->nxt = (struct tcp_seq_hash *)
                                        calloc(1, sizeof(*th));
                                if (th->nxt == NULL)
                                        error("tcp_print: calloc");
                        }
                        th->addr = tha;
                        if (rev)
                                th->ack = seq, th->seq = ack - 1;
                        else
                                th->seq = seq, th->ack = ack - 1;
                } else {
                        if (rev)
                                seq -= th->ack, ack -= th->seq;
                        else
                                seq -= th->seq, ack -= th->ack;
                }

                thseq = th->seq;
                thack = th->ack;
        } else {
                /*fool gcc*/
                thseq = thack = threv = 0;
        }
        if (hlen > length) {
                (void)printf(" [bad hdr length %u - too long, > %u]",
                             hlen, length);
                return;
        }

        if (vflag && !Kflag && !fragmented) {
                /* Check the checksum, if possible. */
                u_int16_t sum, tcp_sum;

                if (IP_V(ip) == 4) {
                        if (TTEST2(tp->th_sport, length)) {
                                sum = tcp_cksum(ip, tp, length);
                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);

                                (void)printf(", cksum 0x%04x", tcp_sum);
                                if (sum != 0)
                                        (void)printf(" (incorrect -> 0x%04x)",
                                            in_cksum_shouldbe(tcp_sum, sum));
                                else
                                        (void)printf(" (correct)");
                        }
                }
#ifdef INET6
                else if (IP_V(ip) == 6 && ip6->ip6_plen) {
                        if (TTEST2(tp->th_sport, length)) {
                                sum = nextproto6_cksum(ip6, (const u_int8_t *)tp, length, IPPROTO_TCP);
                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);

                                (void)printf(", cksum 0x%04x", tcp_sum);
                                if (sum != 0)
                                        (void)printf(" (incorrect -> 0x%04x)",
                                            in_cksum_shouldbe(tcp_sum, sum));
                                else
                                        (void)printf(" (correct)");

                        }
                }
#endif
        }

        length -= hlen;
        if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) {
                (void)printf(", seq %u", seq);

                if (length > 0) {
                        (void)printf(":%u", seq + length);
                }
        }

        if (flags & TH_ACK) {
                (void)printf(", ack %u", ack);
        }

        (void)printf(", win %d", win);

        if (flags & TH_URG)
                (void)printf(", urg %d", urp);
        /*
         * Handle any options.
         */
        if (hlen > sizeof(*tp)) {
                register const u_char *cp;
                register u_int i, opt, datalen;
                register u_int len;

                hlen -= sizeof(*tp);
                cp = (const u_char *)tp + sizeof(*tp);
                printf(", options [");
                while (hlen > 0) {
                        if (ch != '\0')
                                putchar(ch);
                        TCHECK(*cp);
                        opt = *cp++;
                        if (ZEROLENOPT(opt))
                                len = 1;
                        else {
                                TCHECK(*cp);
                                len = *cp++;	/* total including type, len */
                                if (len < 2 || len > hlen)
                                        goto bad;
                                --hlen;		/* account for length byte */
                        }
                        --hlen;			/* account for type byte */
                        datalen = 0;

/* Bail if "l" bytes of data are not left or were not captured  */
#define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); }


                        printf("%s", tok2str(tcp_option_values, "Unknown Option %u", opt));

                        switch (opt) {

                        case TCPOPT_MAXSEG:
                                datalen = 2;
                                LENCHECK(datalen);
                                (void)printf(" %u", EXTRACT_16BITS(cp));
                                break;

                        case TCPOPT_WSCALE:
                                datalen = 1;
                                LENCHECK(datalen);
                                (void)printf(" %u", *cp);
                                break;

                        case TCPOPT_SACK:
                                datalen = len - 2;
                                if (datalen % 8 != 0) {
                                        (void)printf("malformed sack");
                                } else {
                                        u_int32_t s, e;

                                        (void)printf(" %d ", datalen / 8);
                                        for (i = 0; i < datalen; i += 8) {
                                                LENCHECK(i + 4);
                                                s = EXTRACT_32BITS(cp + i);
                                                LENCHECK(i + 8);
                                                e = EXTRACT_32BITS(cp + i + 4);
                                                if (threv) {
                                                        s -= thseq;
                                                        e -= thseq;
                                                } else {
                                                        s -= thack;
                                                        e -= thack;
                                                }
                                                (void)printf("{%u:%u}", s, e);
                                        }
                                }
                                break;

                        case TCPOPT_CC:
                        case TCPOPT_CCNEW:
                        case TCPOPT_CCECHO:
                        case TCPOPT_ECHO:
                        case TCPOPT_ECHOREPLY:

                                /*
                                 * those options share their semantics.
                                 * fall through
                                 */
                                datalen = 4;
                                LENCHECK(datalen);
                                (void)printf(" %u", EXTRACT_32BITS(cp));
                                break;

                        case TCPOPT_TIMESTAMP:
                                datalen = 8;
                                LENCHECK(datalen);
                                (void)printf(" val %u ecr %u",
                                             EXTRACT_32BITS(cp),
                                             EXTRACT_32BITS(cp + 4));
                                break;

                        case TCPOPT_SIGNATURE:
                                datalen = TCP_SIGLEN;
                                LENCHECK(datalen);
#ifdef HAVE_LIBCRYPTO
                                switch (tcp_verify_signature(ip, tp,
                                                             bp + TH_OFF(tp) * 4, length, cp)) {

                                case SIGNATURE_VALID:
                                        (void)printf("valid");
                                        break;

                                case SIGNATURE_INVALID:
                                        (void)printf("invalid");
                                        break;

                                case CANT_CHECK_SIGNATURE:
                                        (void)printf("can't check - ");
                                        for (i = 0; i < TCP_SIGLEN; ++i)
                                                (void)printf("%02x", cp[i]);
                                        break;
                                }
#else
                                for (i = 0; i < TCP_SIGLEN; ++i)
                                        (void)printf("%02x", cp[i]);
#endif
                                break;

                        case TCPOPT_AUTH:
                                (void)printf("keyid %d", *cp++);
                                datalen = len - 3;
                                for (i = 0; i < datalen; ++i) {
                                        LENCHECK(i);
                                        (void)printf("%02x", cp[i]);
                                }
                                break;


                        case TCPOPT_EOL:
                        case TCPOPT_NOP:
                        case TCPOPT_SACKOK:
                                /*
                                 * Nothing interesting.
                                 * fall through
                                 */
                                break;

                        case TCPOPT_UTO:
                                datalen = 2;
                                LENCHECK(datalen);
                                utoval = EXTRACT_16BITS(cp);
                                (void)printf("0x%x", utoval);
                                if (utoval & 0x0001)
                                        utoval = (utoval >> 1) * 60;
                                else
                                        utoval >>= 1;
                                (void)printf(" %u", utoval);
                                break;

			case TCPOPT_MPTCP: {
				uint8_t subtype;
				
				datalen = 1;
				LENCHECK(datalen);
				
				subtype = (*cp) >> 4;
				
				printf(" %s ", tok2str(mptcp_subtypes, "Unknown MPTCP subtype %u", subtype));
				
				switch (subtype) {
					case TCPOPT_MPTCP_MP_CAPABLE: {
						uint8_t version = (*cp) & 0x0f;
						uint8_t mpflags;
						
						if (version != 0) {
							printf(" version %u ", version);
							for (i = 0; i < datalen; ++i) {
								LENCHECK(i);
								(void)printf("%02x", cp[i]);
							}
							break;
						}
						
						datalen += 1;
						LENCHECK(datalen);
						mpflags = cp[1];
						printf("%s%s%s%s%s%s%s%s%s",
						       (mpflags) ? "flags:" : "",
						       (mpflags & 0x80) ? "A" : "",
						       (mpflags & 0x40) ? "B" : "",
						       (mpflags & 0x20) ? "C" : "",
						       (mpflags & 0x10) ? "D" : "",
						       (mpflags & 0x08) ? "E" : "",
						       (mpflags & 0x04) ? "F" : "",
						       (mpflags & 0x02) ? "G" : "",
						       (mpflags & 0x01) ? "H" : "");
						
						if (len == 12 || len == 20) {
							printf(" sndkey:");
							for (i = 0; i < 8; ++i) {
								datalen++;
								LENCHECK(datalen);
								(void)printf("%02x", cp[2 + i]);
							}
							
							if (len == 20) {
								printf(" rcvkey:");
								for (i = 0; i < 8; ++i) {
									datalen++;
									LENCHECK(datalen);
									(void)printf("%02x", cp[10 + i]);
								}
							}
						} else {
							printf(" unknown:");
							datalen = len - 2;
							for (i = 0; i < datalen; ++i) {
								LENCHECK(i);
								(void)printf("%02x", cp[i]);
							}
						}
						
						break;
					}
					case TCPOPT_MPTCP_MP_JOIN: {
						uint8_t mpflags = (*cp) & 0x0f;
						
						/* Flags on SYN only */
						if (flags & TH_SYN) {
							printf("%s%s%s%s%s",
							       (mpflags) ? "flags:" : "",
							       (mpflags & 0x08) ? "0" : "",
							       (mpflags & 0x04) ? "1" : "",
							       (mpflags & 0x02) ? "2" : "",
							       (mpflags & 0x01) ? "B" : "");
						}
						/* Address ID on SYN only, otherwise ignored */
						datalen += 1;
						LENCHECK(datalen);
						if ((flags & TH_SYN))
							printf(" addrid:%0x", cp[1]);
						
						if (flags == TH_SYN && len == 12) {
							/* Initial SYN */
							printf(" rcvtok:");
							for (i = 0; i < 4; ++i) {
								datalen++;
								LENCHECK(datalen);
								(void)printf("%02x", cp[2 + i]);
							}
							printf(" sndrand:");
							for (i = 0; i < 4; ++i) {
								datalen++;
								LENCHECK(datalen);
								(void)printf("%02x", cp[6 + i]);
							}
						} else if ((flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK) && len == 16) {
							/* Responding SYN/ACK */
							printf(" sndhmac:");
							for (i = 0; i < 8; ++i) {
								datalen++;
								LENCHECK(datalen);
								(void)printf("%02x", cp[2 + i]);
							}
							printf(" sndrand:");
							for (i = 0; i < 4; ++i) {
								datalen++;
								LENCHECK(datalen);
								(void)printf("%02x", cp[8 + i]);
							}
						} else if ((flags & (TH_SYN | TH_ACK)) == TH_ACK && len == 24) {
							/* Third ACK */
							printf(" sndhmac:");
							for (i = 0; i < 20; ++i) {
								datalen++;
								LENCHECK(i);
								(void)printf("%02x", cp[2 + i]);
							}
						} else {
							datalen = len - 2;
							for (i = 0; i < datalen; ++i) {
								LENCHECK(i);
								(void)printf("%02x", cp[i]);
							}
						}
						break;
					}
					case TCPOPT_MPTCP_DSS: {
						uint8_t mpflags;
						u_int ack_len = 0;
						u_int dsn_len = 0;
						u_int64_t dack;
						u_int64_t dsn;
						u_int32_t sfsn;
						u_int16_t dlen;
						u_int16_t csum;
						
						datalen += 1;
						LENCHECK(datalen);
						mpflags = cp[1] & 0x1f;
						
						printf("%s%s%s%s%s%s",
						       (mpflags) ? "flags:" : "",
						       (mpflags & 0x10) ? "F" : "",
						       (mpflags & 0x08) ? "m" : "",
						       (mpflags & 0x04) ? "M" : "",
						       (mpflags & 0x02) ? "a" : "",
						       (mpflags & 0x01) ? "A" : "");
						
						if ((mpflags & MPDSS_FLAG_A)) {
							if ((mpflags & MPDSS_FLAG_a)) {
								ack_len = 8;
								datalen += ack_len;
								LENCHECK(datalen);
								dack = EXTRACT_64BITS(cp + 2);
							} else {
								ack_len = 4;
								datalen += ack_len;
								LENCHECK(datalen);
								dack = EXTRACT_32BITS(cp + 2);
							}
							(void)printf(" dack: %" PRIu64, dack);
						}
						if ((mpflags & MPDSS_FLAG_M)) {
							if ((mpflags & MPDSS_FLAG_m)) {
								dsn_len = 8;
								datalen += dsn_len;
								LENCHECK(datalen);
								dsn = EXTRACT_64BITS(cp + 2 + ack_len);
							} else {
								dsn_len = 4;
								datalen += dsn_len;
								LENCHECK(datalen);
								dsn = EXTRACT_32BITS(cp + 2 + ack_len);
							}
							(void)printf(" dsn: %" PRIu64, dsn);
							
							datalen += 4;
							LENCHECK(datalen);
							sfsn = EXTRACT_32BITS(cp + 2 + ack_len + dsn_len);
							(void)printf(" sfsn: %" PRIu32, sfsn);
							
							datalen += 2;
							LENCHECK(datalen);
							dlen = EXTRACT_16BITS(cp + 2 + ack_len + dsn_len + 4);
							(void)printf(" dlen: %" PRIu16, dlen);
							
							/*
							 * Use the length of the option to find out if
							 * the checksum is present
							 */
							if (datalen < len - 2) {
								datalen += 2;
								LENCHECK(datalen);
								csum = EXTRACT_16BITS(cp + 2 + ack_len + dsn_len + 6);
								(void)printf(" csum: %" PRIu16, csum);
							}
						}
						break;
					}
					case TCPOPT_MPTCP_ADD_ADDR: {
						uint8_t ipvers;
						u_int addrlen = 0;
						u_int16_t port;
						
						ipvers = cp[1] & 0xf0;
						printf(" vers:%u", ipvers);
						
						datalen = 2;
						LENCHECK(datalen);
						printf(" addrid:%0x", cp[1]);
						
						switch (ipvers) {
							case 4: {
								datalen = 6;
								LENCHECK(datalen);
								ipaddr_string(cp + 2);
								break;
							}
							case 6: {
								datalen = 18;
								LENCHECK(datalen);
#ifdef INET6
								ip6addr_string(cp + 2);
#endif
								break;
							}
							default:
								goto bad;
						}
						/*
						 * Use the length of the option to find out if
						 * the port is present
						 */
						if (datalen < len - 2) {
							datalen += 2;
							LENCHECK(datalen);
							
							port = EXTRACT_16BITS(cp + 2 + addrlen);
							
							printf(" port: %u", port);
						}
						break;
					}
					case TCPOPT_MPTCP_REMOVE_ADDR:
						datalen = len - 2;
						for (i = 0; i < datalen; ++i) {
							LENCHECK(i);
							(void)printf(" %u", cp[i]);
						}
						break;
						
					case TCPOPT_MPTCP_MP_PRIO: {
						uint8_t mpflags = (*cp) & 0x0f;
						
						if (mpflags == 0x01)
							printf("flag B");
						else if (mpflags != 0) {
							printf("flag %c%c%c%c%c%c%c%c",
							       mpflags & 0x80 ? '1' : '0',
							       mpflags & 0x40 ? '1' : '0',
							       mpflags & 0x20 ? '1' : '0',
							       mpflags & 0x10 ? '1' : '0',
							       mpflags & 0x08 ? '1' : '0',
							       mpflags & 0x04 ? '1' : '0',
							       mpflags & 0x02 ? '1' : '0',
							       mpflags & 0x01 ? 'B' : '0');
						}
						
						if (len == 4) {
							datalen = 2;
							LENCHECK(datalen);
							printf("%saddrid:%u", mpflags ? " " : "", cp[1]);
						}
						break;
					}
					case TCPOPT_MPTCP_MP_FAIL:
						datalen = 10;
						LENCHECK(datalen);
						
						printf(" dsn:");
						for (i = 0; i < 8; ++i) {
							(void)printf("%02x", cp[2 + i]);
						}
						break;
						
					case TCPOPT_MPTCP_MP_FASTCLOSE:
						datalen = 10;
						LENCHECK(datalen);
						
						printf(" rcvrkey:");
						for (i = 0; i < 8; ++i) {
							(void)printf("%02x", cp[2 + i]);
						}
						break;
						
					default:
						datalen = len - 2;
						for (i = 0; i < datalen; ++i) {
							LENCHECK(i);
							(void)printf("%02x", cp[i]);
						}
						break;
				}
				break;
			}
					
                        default:
                                datalen = len - 2;
                                for (i = 0; i < datalen; ++i) {
                                        LENCHECK(i);
                                        (void)printf("%02x", cp[i]);
                                }
                                break;
                        }

                        /* Account for data printed */
                        cp += datalen;
                        hlen -= datalen;

                        /* Check specification against observed length */
                        ++datalen;			/* option octet */
                        if (!ZEROLENOPT(opt))
                                ++datalen;		/* size octet */
                        if (datalen != len)
                                (void)printf("[len %d]", len);
                        ch = ',';
                        if (opt == TCPOPT_EOL)
                                break;
                }
                putchar(']');
        }