I read Julia’s article What happens if you write a TCP stack in Python? last year, and since then I really wanted to implement a TCP stack in Ruby language. I saved Julia’s article in my Pocket, then moved it to my browser bookmark folder, but never touched it again.
This week, I decided to give it a try and it turns out to be really fun. In this post, I’m going to follow Julia’s steps and blog some implementation details.
What we would like to do here is, I quote from Julia’s blog:
- open a raw network socket that lets me send TCP packets
- send a HTTP request to
- get and parse a response
I use a gem called PacketFu to read and write packets. I don’t think I could write the stack in such a short time without it, it’s really awesome.
Step 1: the TCP handshake
The TCP three-way handshake is:
- me: SYN
- google: SYNACK
- me: ACK
Pseduo codes could be something like this:
1 2 3
PacketFu, sending a packet is pretty simple:
1 2 3 4 5 6 7 8 9 10 11
For ack packet, just set
pkt.tcp_flags.ack = 1.
As to read response, we need to filter packets from the interface.
1 2 3 4 5 6 7 8 9 10 11 12
filter parameter for
PacketFu::Capture is very interesting, it’s a bpf filter and you could find the syntax documentation here.
tcp and src 18.104.22.168 means we would like to filter tcp packets from ip
22.214.171.124, which are exactly what we want to parse.
tcpdump also uses bpf filter to filter packets you want.
Let’s say we just want the
SYNACK packets. With tcpdump:
$ sudo tcpdump -i eth0 'tcp=18'
tcp=18 mean? Here is the TCP Header Format:
We could see that the last six bits of fourteenth byte stand for tcp flags, and the previous two bits are both 0. So for
SYNACK packets, tcp flags would be:
010010, the value would be 16 + 2 = 18.
Bpf is super powerful, and if you would like to know more examples, check
In this step, I’m gonna ignore the part of how SEQ and ACK number work, you could check this article to learn more.
Step 2: Kernel sends a RST after receiving the SYNACK packet
Julia described this in her article, instead of what we think of how it would work, it didn’t.
As the picture shown, after receiving
SYNACK packet from google.com, a
RST packet was sent (obviously not by us).
I will just quote Julia’s explaining here:
1 2 3 4
ARP spoofing to pretend a different IP address, and someone commented about using tap/tun interfaces instead. I tried the two ways both, but none of them worked for me (maybe my implementations are not good). I struggled to find a way to get my kernel just ignore the packet for, like the whole afternoon. Finally, when I used my nameserver’s ip as src_ip in the packet, it worked! I’m not exactly sure how this works, but it fixed my problem. I will leave this as a question and ask some network folks later.
Now, the three-way handshake works!
Step 3: get a web page!
This is pretty easy, what we should do is:
- send a packet containing a HTTP GET request
- listen for packets in response
- parse the packet
- decide what to do based on tcp flag
Implementing the last one will cost you some time probably, since it comes down to some parts of TCP Finite State Machine.
Constructing a HTTP
Get request is quite easy, just include
GET some_path HTTP/1.0\r\nHost: hostname\r\n\r\n in your
I use a seperate thread to listen for packets from the destination IP, and after parsing the packet, it will send it to main thread and let it respond based on its state and packet tcp flags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
I’m really glad that I finally give this a try and get it worked. Couldn’t say more thanks to Julia for her article and codes, I really learned a lot from them.
The last time I wrote packets related codes is about 5 or 6 years ago during collegue’s networking lesson. Mostly with C language back then. Thanks to
PacketFu gem, I really don’t need to some dirty work.
Anyway, this is much more fun than I expected. The moment it worked I was really beyond happy. Try it with your preferred language, have some fun with me too!