WaddoupsNet - A TCP/IP Stack on Linux
In ECE 5600 I created a TCP/IP stack for a lab that we worked on throughout the semester. This is a document of what I did and how I did it. It’s also a good change to learn jekyll better. One other note, I started these labs before I started making notes here, so it’s definitely going to feel disjoint until I have time to clean this post up after the semester.
Lab 3 - Adding IP v4 and ICMP
Having added the ability to unwrap ether frames and handle ARP requests / replies, we are now ready to sprinkle some IP v4 all over our project with a dash of ICMP on top.
Here’s how our stack is going to look after we are done.
Code wise in our stack application we hopefully will be able to end up with something similar to this for our IP and ICMP handler:
1
2
3
4
5
auto ipv4_handler(
channel<ether_frame> ether_frames,
channel<ipv4_packet> ipv4_packets) -> void;
auto icmp_handler(channel<ipv4_packet> ipv4_packets) -> void;
Adding the IP v4 Packet Struct
In line with what we have done previously in creating a struct to represent ether frames we’ll do the same for IP v4 packets. For reference, the current description of an IP v4 packet is given in RFC 791, but I just used the wikipedia page as my reference. It gets tiresome to continually refer to the packet as an “IP v4” packet so from now on we’ll assume that anytime called IP is just referring to IP v4, unless otherwise stated.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct ipv4_packet
{
constexpr static octet version{ 0x4U };
octet ihl{};
octet dscp{};
octet ecn{};
uint16_t total_length{};
uint16_t identification{};
octet flags{};
uint16_t fragment_offset{};
octet time_to_live{};
octet protocol{};
uint16_t checksum{};
ip_v4_address source;
ip_v4_address dest;
std::vector<octet> options;
std::vector<octet> data;
constexpr ipv4_packet() = default;
constexpr explicit ipv4_packet(const raw_packet rpacket)
{
from_raw(rpacket);
}
constexpr auto from_raw(const raw_packet rpacket) -> void;
[[nodiscard]] constexpr auto to_raw() const -> std::vector<octet>;
};
One of the challenges with going from and ether frame into an IP packet is that a single
packet may be spread across several frames. We can see that the total_length
of a packet
is determined by a 16-bit unsigned integer, which means that an IP packet can be
about 65000 bytes long! Our ether frames are only limited to about 1500 bytes, so we will
have to handle the case where a single IP packet is scattered across many frames when
we build up a packet.