PHASE III · SUBSTANCE / MODULE 06 ·

A Network Stack

Talk to the outside world using nothing you didn't write. Drive the Ethernet NIC at the register level, then hand-build ARP, IP, ICMP, and TCP — and watch your OS answer a ping and open a real socket.

Module 06 — walkthrough
module introcoming soon

Networking has a reputation for being impossibly complex. It isn't — it's a stack of simple layers, each wrapping the one below. You'll build it bottom-up: get raw frames on and off the wire, then Ethernet, then ARP, IP, ICMP, and finally TCP. Each layer is understandable on its own.

Lesson 6.1

6.1 The NIC and DMA rings

raw frames on and off the wire
6.1 — driving an Intel e1000
~22 mincoming soon

An Ethernet controller (the course uses the well-documented Intel e1000) moves packets via DMA ring buffers: a circular array of descriptors, each pointing at a buffer. You set up the rings, and the NIC writes received packets into them and reads packets to send from them. You talk to the NIC through memory-mapped registers.

e1000.c — receive ringcopy
struct rx_desc {
    UINT64 addr;      // physical buffer address
    UINT16 length;
    UINT16 checksum;
    UINT8  status;    // bit 0 = Descriptor Done
    UINT8  errors;
    UINT16 special;
} __attribute__((packed));

// poll for received packets
void e1000_poll_rx(void) {
    while (rx_ring[rx_tail].status & 1) {   // DD bit set = packet ready
        handle_frame(rx_buffers[rx_tail], rx_ring[rx_tail].length);
        rx_ring[rx_tail].status = 0;         // hand it back to the NIC
        rx_tail = (rx_tail + 1) % RX_COUNT;
    }
}
Lesson 6.2

6.2 Ethernet and ARP

who has this IP?
6.2 — frames and address resolution
~18 mincoming soon

Every frame starts with an Ethernet header: destination MAC, source MAC, and a type field. Before you can send to an IP, you need its MAC — that's ARP. You broadcast "who has 192.168.1.1?" and the owner replies with its MAC, which you cache. ARP is the smallest complete request/response protocol, which makes it the perfect first one to build.

arp.ccopy
void arp_request(UINT8 *target_ip) {
    struct arp_packet a = {0};
    a.htype = htons(1);          // Ethernet
    a.ptype = htons(0x0800);     // IPv4
    a.hlen  = 6; a.plen = 4;
    a.oper  = htons(1);          // request
    memcpy(a.sha, my_mac, 6);
    memcpy(a.spa, my_ip, 4);
    memcpy(a.tpa, target_ip, 4);
    eth_send_broadcast(ETH_ARP, &a, sizeof a);
}
Lesson 6.3

6.3 IP and ICMP — answer a ping

the first "it works!" moment on the network
6.3 — IPv4 and replying to ping
~20 mincoming soon

IP adds addressing and a checksum on top of Ethernet. ICMP rides on IP, and the simplest ICMP exchange is echo — ping. When a ping request arrives, you swap source and destination, change the type from request to reply, fix the checksum, and send it back. The moment another machine's ping gets a reply from your OS is genuinely thrilling.

icmp.c — echo replycopy
void icmp_handle(struct ip_hdr *ip, struct icmp_hdr *icmp) {
    if (icmp->type != 8) return;      // only echo requests
    icmp->type = 0;                    // 0 = echo reply
    icmp->checksum = 0;
    icmp->checksum = checksum16(icmp, icmp_len);
    ip_send(ip->src, IP_ICMP, icmp, icmp_len);  // straight back
}
ForgeOS proved its stack with a real port scan

Once ICMP and TCP worked, ForgeOS ran a port scan against a real home router and got correct results — the proof that the hand-written stack actually spoke to real network hardware, not just a loopback. When yours answers a ping from another machine on your LAN, you've hit the same milestone.

Lesson 6.4

6.4 TCP — the hard one

a reliable stream over unreliable packets
6.4 — the TCP state machine
~28 mincoming soon

TCP is the deep end: a state machine (SYN, SYN-ACK, ACK to connect; FIN to close), sequence numbers, acknowledgements, and retransmission. Start with just enough to open a connection and exchange data — the three-way handshake and basic send/receive. Don't build congestion control yet; get a socket open first.

tcp.c — the handshakecopy
void tcp_handle(struct tcp_hdr *t) {
    if (t->flags & SYN) {
        conn->their_seq = ntohl(t->seq) + 1;
        conn->my_seq    = random_isn();
        tcp_send(conn, SYN | ACK, 0, 0);   // SYN-ACK
        conn->state = SYN_RECEIVED;
    } else if ((t->flags & ACK) && conn->state == SYN_RECEIVED) {
        conn->state = ESTABLISHED;          // connection open!
    }
}
Lesson 6.5

6.5 Settings, Wi-Fi status, and NetMon

surface the network in the UI
6.5 — network UI
~16 mincoming soon

With a working stack, expose it. A Settings page shows the interface, IP, and gateway; a NetMon app shows live traffic and connection state. This is where Module 04's UI toolkit and Module 06's stack meet — the network becomes something the user can see, not just packets on a wire.

Module 06 checkpoint
  • A NIC driver with working DMA receive/transmit rings
  • Ethernet framing and ARP address resolution
  • IPv4 with correct checksums, and ICMP echo — your OS answers ping
  • A TCP handshake that opens a real connection
  • Network state surfaced in Settings and a NetMon app
the final module

Module 07 — Run DOOM

Port doomgeneric onto your OS: WAD loading off your filesystem, your framebuffer as its display, your keyboard as its input. The universal proof that you built something real.

Get lifetime access — $149 →
or see all plans →