Wireshark-users: Re: [Wireshark-users] how to get ACK only in handshake
From: Guy Harris <[email protected]>
Date: Wed, 11 Jun 2008 14:14:30 -0700
On Jun 11, 2008, at 12:37 PM, Chong Zhang wrote:

I am trying to use twireshark/tcpdump to get only the TCP 3way
handshake packets. There are two tapped interfaces, one for each
direction, so the SYN- ACK and ACK packets are on different
interfaces. I am wondering if there is way to only capture the ACK
belonging to a handshake, rather than all ACK packets for the whole
It should be easy to capture only the SYN+ACK:

$ man tcpdump

selects which packets will be dumped. If no expression is given, all packets on the net will be dumped. Otherwise, only
              packets for which expression is `true' will be dumped.


              expr relop expr
True if the relation holds, where relop is one of >, <, >=, <=, =, !=, and expr is an arithmetic expression com- posed of integer constants (expressed in standard C syn- tax), the normal binary operators [+, -, *, /, &, |, <<, >>], a length operator, and special packet data acces- sors. Note that all comparisons are unsigned, so that, for example, 0x80000000 and 0xffffffff are > 0. To access data inside the packet, use the following syntax:
                          proto [ expr : size ]
Proto is one of ether, fddi, tr, wlan, ppp, slip, link, ip, arp, rarp, tcp, udp, icmp, ip6 or radio, and indi- cates the protocol layer for the index operation. (ether, fddi, wlan, tr, ppp, slip and link all refer to the link layer. radio refers to the "radio header" added to some 802.11 captures.) Note that tcp, udp and other upper-layer protocol types only apply to IPv4, not IPv6 (this will be fixed in the future). The byte offset, relative to the indicated protocol layer, is given by expr. Size is optional and indicates the number of bytes in the field of interest; it can be either one, two, or four, and defaults to one. The length operator, indi- cated by the keyword len, gives the length of the packet.
                     For example, `ether[0] & 1 != 0'  catches  all   
                     traffic.   The  expression `ip[0] & 0xf != 5'  
catches all
                     IPv4 packets with options.   The  expression   
`ip[6:2]  &
                     0x1fff  = 0' catches only unfragmented IPv4  
datagrams and
                     frag zero of fragmented IPv4 datagrams.   This   
check  is
                     implicitly  applied  to the tcp and udp index  
                     For instance, tcp[0] always means the first  
byte  of  the
                     TCP  header,  and never means the first byte of  
an inter-
                     vening fragment.

Some offsets and field values may be expressed as names rather than as numeric values. The following protocol header field offsets are available: icmptype (ICMP type field), icmpcode (ICMP code field), and tcpflags (TCP
                     flags field).

The following ICMP type field values are available: icmp- echoreply, icmp-unreach, icmp-sourcequench, icmp-redi- rect, icmp-echo, icmp-routeradvert, icmp- routersolicit, icmp-timxceed, icmp-paramprob, icmp-tstamp, icmp-tstam- preply, icmp-ireq, icmp-ireqreply, icmp- maskreq, icmp-

The following TCP flags field values are available: tcp-
                     fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg.




To print the start and end packets (the SYN and FIN packets) of each
       TCP conversation that involves a non-local host.
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0 and not src and dst net localnet'

So a filter of

	tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)

would capture all packets with SYN and ACK set.

The harder part is capturing the ACK responding to the second SYN (the one in the SYN+ACK). Once the connection is established, pretty much *all* packets have ACK set; the trick is to catch the ACK from the three-way handshake.
RFC 793 says:

  The synchronization requires each side to send it's own initial
  sequence number and to receive a confirmation of it in acknowledgment
  from the other side.  Each side must also receive the other side's
  initial sequence number and send a confirming acknowledgment.

    1) A --> B  SYN my sequence number is X
    2) A <-- B  ACK your sequence number is X
    3) A <-- B  SYN my sequence number is Y
    4) A --> B  ACK your sequence number is Y

  Because steps 2 and 3 can be combined in a single message this is
  called the three way (or three message) handshake.

(Note, by the way, that they say "steps 2 and 3 *can* be combined in a single message", so there's no guarantee that it really *is* a 3-way handshake; the connected-to host might send a SYN in one packet and an ACK in a subsequent packet. Other than to test the robustness of TCP implementations, however, I'm not sure there's a good reason to do that, so, in practice, it's probably always SYN followed by SYN+ACK followed by ACK.)
This means that there's no guarantee what the sequence number is in  
the last ACK of the handshake, so you can't use that - and, as capture  
filters are stateless, you can't check against the sequence number  
from an earlier packet.
Checking for an ACK with no payload will capture other ACKs - and  
there is, as far as I know, nothing that prevents the final ACK of a  
connection handshake from containing data.
So, unless I've missed something, there's no capture filter that will  
just capture the ACKs from the handshake.