This is a static archive of our old Q&A Site. Please post any new questions and answers at

How to capture tcp 3 way handshake


I'm looking to capture the conversation between 2 hosts that contains the 3 way handshake. I'm not sure if this would be doable with a capture filter. Or maybe it's a display filter. I'm thinking something like: tcp.flags == 0x02 | tcp.flags == 0x10 But I don't know if this is just a display capture. It doesn't seem to be recognized in capture filter box. Or maybe the concept is to to set a display filter of tcp.flags == 0x02 | tcp.flags == 0x10 and then capture all traffic and only this syn, syn ack, or ack will be displayed.

asked 17 Oct '12, 07:08

gipper's gravatar image

accept rate: 0%

8 Answers:


You could try "tcp[13] & 2!=0" as a capture filter, which worked fine when I just tested it, at least for SYN and SYN/ACK packets. The third packet (ACK) of the handshake might be a problem because you can't just filter on ack flags - it would give you all further packets because they will probably all carry an ACK flag.

I think the other filters you mentioned are all display filters.

answered 17 Oct '12, 10:49

Jasper's gravatar image

Jasper ♦♦
accept rate: 18%

edited 17 Oct '12, 10:51


I was able to take advantage of what you said Kurt with LUA.

I think I have it working. Maybe someone can run my LUA script to capture TCP handshake.

My command to run tshark from DOS:

tshark -X lua_script:dumptofile_ack_packet.lua -i 4 -o tcp.relative_sequence_numbers:TRUE

where my interface number is 4. Run tshark -D to list interfaces.
This needs to be terminated with CTL-C

dumptofile_ack_packet.lua is LUA script as shown below

-- Create a file named ackpackets.cap (works for tshark only)
-- Dump file is created for all packets captured.
-- Display packets with a capture filter that adheres to display filter syntax 
firsttime = true
firstclose = false
setdumpers = true
dumpers = {}  

dumpfile={} –Set filter to use as capture filter on next line filter = "(tcp.flags == 0x02 && tcp.seq == 0) || (tcp.flags == 0x12 && tcp.seq == 0) || (tcp.flags == 0x10 && tcp.seq == 1)" – syn ack – tcp.flags – 0x10 = ack – 0x02 = syn – 0x12 = syn ack –first frame –syn, seq = 0 –tcp.flags = 0x02 tcp.seq = 0 –second frame –syn ack, seq = 0 –tcp.flags = 0x12 –tcp.seq = 0 –third frame –ack, seq = 1 –tcp.flags = 0x10 –tcp.seq = 1 – Run tshark as shown on the following line – tshark -X lua_script:dumptofile_ack_packet.lua -i 4 -o tcp.relative_sequence_numbers:TRUE do

    --local dumpers = {}    
    local function init_listener()
            local tap ="frame", filter)
            --tap ="frame", filter)
            --A Listener, is called once for every packet that matches a certain filter or has a certain tap. 
            --It can read the tree, the packet's Tvb eventually the tapped data but it cannot add elements to the tree. 
            --[tap], [filter])
            -- Creates a new Listener listener
            -- tap (optional)
            --The name of this tap 
            -- filter (optional)
            --A filter that when matches the tap.packet function gets called (use nil to be called for every packet) 
            -- This case I'm filtering for ip
            --The newly created Listener listener object

            -- we will be called once for every IP Header.
            -- If there's more than one IP header in a given packet we'll dump the packet once per every header
            function tap.packet(pinfo,tvb,ip)
            --A function that will be called once every packet matches the Listener listener filter. 
            --function tap.packet(pinfo,tvb,userdata) ... end 
            --Packet information

            --The number of this packet in the current file
            --The buffer to dissect 
                    -- local means a variable local to this function

                    dumpersindex = "ttt"
                    local filename
                    filename ="ackpackets.cap"
                    --local dumpfile

                   if setdumpers == true then

                    dumpfile = dumpers[dumpersindex]
                    setdumpers = false


                    -- Saving capture files 
                    -- dumpers
          , [filetype], [encap])
                    --Creates a file to write packets. Dumper:new_for_current() will probably be a better choice. 
                    --The name of the capture file to be created

                    --filetype (optional)
                    --The type of the file to be created

                    --encap (optional)
                    --The encapsulation to be used in the file to be created

                    -- The case below is just the file name
                    -- where dir is a variable of the directory
                    -- ip_src is a variable which was from
                    -- tap variable ip.src

                        if  firsttime == true then

                            dumpfile = Dumper.new_for_current( filename )

                        end  -- end if  firsttime == true then

                    --Dumps the current packet as it is


                    --Writes all unsaved data of a dumper to the disk
                   -- dumpfile:flush()

                    --Now same for destination IP address to a seperate file

            end  -- end function tap.packet(pinfo,tvb,ip)

            --A function that will be called once every few seconds to redraw the gui objects in tshark this funtion is 
            --called oly at the very end of the capture file. function tap.draw(userdata) ... end 
            function tap.draw()

            end  -- end function tap.draw()

           function tap.reset()
           -- A function that will be called at the end of the capture run. function tap.reset(userdata) ... end

                 --   dumpers = {}
            end  -- end function tap.reset()

end – do loop dumpfile:flush() dumper:close()

answered 28 Oct ‘12, 12:28

gipper's gravatar image

accept rate: 0%

edited 13 Jun ‘16, 06:50

Jaap's gravatar image

Jaap ♦

are you sure this is the whole script? It really does not do very much. Especially it does not filter on any flags, etc.

(29 Oct ‘12, 04:36) Kurt Knochner ♦

Try this link for 3 way handshake capture with LUA

(30 Oct ‘12, 17:11) gipper


I'm looking to capture the conversation between 2 hosts that contains the 3 way handshake. I'm not sure if this would be doable with a capture filter.

to be specific: it's not possible to capture only the full 3-way handshake (SYN,SYN-ACK,ACK), as it's impossible to identify the single ACK in the handshake with tcpdump. The best you can achive is what Jasper suggested. This will capture the SYN and the SYN-ACK, however not the final ACK of the 3-way handshake.

The same holds true for Wireshark display filters. Even there it is not possible to capture/filter the final ACK of the 3-way handshake, without getting the rest of the communication (ACK flag set) as well.

You could do it with a Listener in Lua, but that would require some programming.


answered 17 Oct '12, 11:08

Kurt%20Knochner's gravatar image

Kurt Knochner ♦
accept rate: 15%

edited 17 Oct '12, 11:10


You can't do this with a capture filter. Make sure Wireshark is using relative sequence numbers and then enter the following display filter:

(tcp.flags.syn==1 ) || (tcp.flags == 0x0010 && tcp.seq==1 && tcp.ack==1)

Update: Further testing shows that this display filter will display what you want most of the time, but it's not perfect. It will miss the third packet of the handshake if that packet contains data and the PSH bit is set, for example. It will also display the first packet in each direction of a TCP stream whose three-way handshake is not present in the trace file.

answered 17 Oct '12, 19:34

Jim%20Aragon's gravatar image

Jim Aragon
accept rate: 24%

edited 17 Oct '12, 21:14


How about this one?

((tcp.flags.syn eq 1) || (tcp.seq eq 1 && tcp.ack eq 1 && frame.protocols == "eth:ip:tcp" && !tcp.flags.fin eq 1))

Requires "Relative sequence numbers" in TCP Protocol Preferences.

answered 18 Oct '12, 06:22

holmahenkel's gravatar image

accept rate: 0%


I managed to come up with a pcap filter expression that captures the whole TCP setup 3-way handshake - it relies on knowing the value for window size that will be set in the 3rd packet of the handshake. For the Linux 3.8.11-ec2 kernel servers I was capturing on, this value is 0x01c9.

The capture expression matches: any packet containing the syn flag set (first two packets of the handshake) and packets that are < 68 bytes long, have only the ack flag set and have the window size set to 0x01c9 (captures only the third packet).

The capture filter expression is therefore: "( tcp[tcpflags] & tcp-syn != 0 ) or ( tcp[tcpflags] = tcp-ack and less 68 and tcp[14:2] == 0x01c9 )"

answered 27 Feb '14, 17:36

archaelus's gravatar image

accept rate: 0%


what about

tcp.flags==0x2 || tcp.flags==0x12 || tcp.flags==0x10 and tcp.seq<=1 and tcp.ack<=1 and not nbss

with relative sequence numbers?

answered 03 Feb '16, 05:37

Gian%20Matteo%20Esposito's gravatar image

Gian Matteo ...
accept rate: 0%


This works

((tcp.flags == 0x0002) && (tcp.seq == 0)) || ((tcp.flags == 0x0012) && (tcp.seq == 0)) || ((tcp.flags == 0x0010) && (tcp.seq == 1))

answered 12 Jun '16, 00:47

gopi1828's gravatar image

accept rate: 0%

it seems there are also some ACK-only packets not related to the 3-way handshake

maybe this should works better

((tcp.flags == 0x0002 || tcp.flags == 0x0012) && tcp.seq == 0) || (tcp.flags == 0x0010 && tcp.seq == 1 && tcp.ack <=1)


(13 Jun '16, 06:14) Gian Matteo ...

Both still give you too many packets in some situations, e.g. FTP data tranfers where the receiver/client doesn't send anything at all.

Check out

Also, the original question is about capture filtering, not display filtering.

(13 Jun '16, 09:47) Jasper ♦♦