108. Routing, VLAN, Wi-Fi and VPN
The network from section 105 ends at the gateway. Beyond it, routers carry the packet: each one looks into its own table and hands the packet to the next one, decreasing its time to live. A VLAN splits one cable into several networks, Wi-Fi replaces the cable with radio, and a VPN lays its own network on top of someone else's. The section's code picks a route, parses router replies, inserts a VLAN tag, and computes channels, path loss and the cost of a tunnel. The experiment: a Linux router between two Docker networks, with echo requests at TTL 1 and 2, as traceroute does.
Frames, ARP and ping are in section 105, TCP and MSS in section 106.
Terms used in the documentation
Term Meaning routing table the table of routes longest prefix match picking the longest matching prefix default route the route used when nothing else matches next hop the next node on the path time exceeded ICMP "time to live exceeded" destination unreachable ICMP "destination cannot be reached" VLAN tag, 802.1Q the virtual network tag VLAN ID (VID), priority (PCP) virtual network number, priority trunk / access port a port with tags / without tags channel, channel width a radio channel, its width free-space path loss (FSPL) attenuation in free space RSSI, dBm received signal level, decibels relative to a milliwatt tunnel, encapsulation a tunnel, wrapping a packet inside a packet MTU, MSS clamping largest packet, cutting MSS down to fit the tunnel
108.1 The routing table
A router searches its table for the entry whose network contains the destination address; if several match, the longest prefix wins:
int route_lookup(const route_t *t, unsigned n, uint32_t dst)
{
int best = -1;
for (unsigned i = 0; i < n; i++) {
uint32_t m = t[i].len ? 0xFFFFFFFFu << (32u - t[i].len) : 0u;
if ((dst & m) == t[i].net && (best < 0 || t[i].len > t[best].len))
best = (int)i;
}
return best;
}
| Network | Gateway | What it is |
|---|---|---|
| 0.0.0.0/0 | 192.168.1.1 | everything else — the default route |
| 192.168.1.0/24 | — | our own network, direct |
| 10.8.0.0/16 | 10.8.0.1 | VPN tunnel |
| 10.8.5.0/24 | 192.168.1.50 | through the neighbouring router |
| 10.8.5.7/32 | 192.168.1.60 | a single host — separately |
8.8.8.8 goes by the default route, 192.168.1.77 directly, 10.8.1.2 into the tunnel, 10.8.5.9 through .50, and 10.8.5.7 through .60: /32 is the longest prefix of all. Without the default route there is no entry for 8.8.8.8, and the router replies "destination unreachable".
108.2 Time to live and traceroute
Every router decreases the TTL by one. When it reaches zero, the packet is dropped, and the sender gets an ICMP "time to live exceeded" with the start of its packet inside: the IP header and the 8 bytes after it, where an echo carries its number. From that number the sender learns which request the reply belongs to:
} else if (r->type == ICMP_TIME_EXCEEDED || r->type == ICMP_UNREACHABLE) {
const uint8_t *in = m + 8; /* заголовок нашего пакета и 8 байт за ним */
if (n < ihl + 8 + 20)
return 0;
unsigned ihl2 = (in[0] & 15u) * 4u;
if (n < ihl + 8 + ihl2 + 8)
return 0;
r->orig_dst = g32(in + 16);
r->id = g16(in + ihl2 + 4);
r->seq = g16(in + ihl2 + 6);
}
The experiment: networks 10.108.1.0/24 and 10.108.2.0/24, with a container between them that has forwarding enabled, and the server behind it has a route back. The section's code builds the echo with the functions from section 105 and sends the frame to the router's MAC address. The router did not pass the echo with TTL 1 any further and replied itself — type 11, code 0, from 10.108.1.2, and inside is the start of our packet to 10.108.2.20 with our echo number. The echo with TTL 2 got through, and the server's reply came back with TTL 63: it started at 64 and passed one router. traceroute does the same: TTL 1, 2, 3 …, and each "time exceeded" reply names the next node on the path.
108.3 VLAN: one network becomes several
An 802.1Q tag is 4 bytes between the source address and the type: 0x8100, then 3 bits of priority, the DEI bit and 12 bits of network number:
unsigned vlan_tag(uint8_t *f, unsigned n, unsigned pcp, unsigned vid)
{
memmove(f + 16, f + 12, n - 12);
w16(f + 12, VLAN_TPID);
w16(f + 14, (pcp & 7u) << 13 | (vid & 0xFFFu)); /* приоритет 3 бита, DEI, номер сети 12 бит */
return n + 4;
}
Priority 5, network 10 gives 81 00 a0 0a, and the real type stays after the tag, 0x0806. There are 4096 numbers, but 0 and 4095 are reserved — 4094 networks. A tagged frame is up to 1522 bytes: old equipment that expects at most 1518 drops such a frame. A "tagged" switch port (trunk) carries many networks over one cable, an "untagged" port (access) carries one, and the tag is removed on it.
108.4 Wi-Fi: channels and path loss
unsigned wifi_mhz(unsigned ch)
{
if (ch == 14)
return 2484; /* особый канал, только в Японии */
if (ch >= 1 && ch <= 13)
return 2407 + 5 * ch;
return 5000 + 5 * ch; /* 5 ГГц: канал 36 — 5180 МГц */
}
| Channel | MHz |
|---|---|
| 1 | 2412 |
| 6 | 2437 |
| 11 | 2462 |
| 13 | 2472 |
| 14 | 2484 |
| 36 | 5180 |
Channels are 5 MHz apart but 20 MHz wide: neighbouring channels overlap. Channels 1, 6 and 11 do not overlap — there are 25 MHz between them. Where channels up to 13 are allowed, 802.11g/n can also use four — 1, 5, 9, 13: they are exactly 20 MHz apart.
Free-space path loss grows with distance and frequency:
float fspl_db(float d_m, float f_mhz)
{
return 20.0f * log10f(d_m / 1000.0f) + 20.0f * log10f(f_mhz) + 32.44f;
}
On channel 6 it is 40.2 dB at 1 m, 60.2 dB at 10 m, 80.2 dB at 100 m: twice the distance adds 6.02 dB, ten times adds 20. At 5180 MHz the same 10 m give 66.7 dB, 6.55 dB more: 5 GHz is faster but reaches less far. A 20 dBm transmitter at 100 m in an open field gives −60.2 dBm, and every wall takes away more.
108.5 VPN: a network on top of a network
A tunnel wraps a packet inside a packet: the inner one is encrypted and sent inside UDP. WireGuard adds 16 bytes of its own header and a 16-byte authentication tag, and pads the data to a multiple of 16:
unsigned wg_outer(unsigned n, int ipv6, unsigned mtu)
{
unsigned padded = (n + 15u) & ~15u;
if (padded > mtu)
padded = mtu;
return (ipv6 ? 40u : 20u) + 8u + WG_HDR + padded + WG_TAG;
}
A 60-byte sensor packet leaves the tunnel as 124 bytes — more than double; an 84-byte ping becomes 156. A 1420-byte inner packet with IPv6 outside gives exactly 1500, which is why the tunnel MTU is 1420; with IPv4 outside it comes to 1480. TCP inside the tunnel must negotiate an MSS of 1380, otherwise full-size segments will not get through.
108.6 On the device
The F407 MAC has a VLAN tag comparison register — MACVLANTR, offset 0x1C.
It only marks a matching tag with the VLAN bit in the receive descriptor status
and allows a frame of up to 1522 bytes; frames of other networks still land
in the buffers, and the software has to drop them based on that flag.
A hardware tag filter exists in the MAC of newer families — on the H7 it is the VTFE bit
in ETH_MACPFR. Wi-Fi is usually connected to an STM32
as a ready-made module over UART or SPI, and the path loss from 108.4
is computed for that module. A VPN on the device is rare; more often the router holds the tunnel,
and the device sees an ordinary network and lives with an MTU of 1420 if the packets
go through the tunnel.
_Static_assert(offsetof(ETH_TypeDef, MACVLANTR) == 0x1C, "ETH_MACVLANTR — смещение 0x1C");
108.7 Pitfalls
A reply from another network does not arrive, although the router passed the request. The recipient has no route back, and the reply goes to its default gateway. In the experiment the server had to be given a route to 10.108.1.0/24 through .2.
Small packets go through the VPN, but large ones get lost. The tunnel MTU is 1420: a 1500-byte packet does not fit into it. MTU 1420, MSS 1380.
A neighbour's Wi-Fi on channel 3 interferes more than one on channel 1. Neighbouring channels overlap: use 1, 6 or 11.
A frame with a VLAN tag does not get through. The port is untagged, or the equipment drops frames longer than 1518 bytes.
traceroute shows asterisks. A router is not obliged to reply "time exceeded" and limits the rate of such replies.
108.8 Summary
- A route is the longest matching prefix; the default route is /0.
- Every router decreases the TTL; at zero it replies "time to live exceeded" — traceroute is built on this. In the experiment: TTL 1 — a reply from the router, TTL 2 — a reply from the server with TTL 63.
- VLAN is a 4-byte tag: 4094 networks on one cable.
- Wi-Fi: channels every 5 MHz, 20 wide, no overlap for 1, 6, 11; path loss +6.02 dB for every doubling of distance.
- A tunnel costs bytes: 60 bytes → 124 with WireGuard, MTU 1420.
Next: section 109 "Git for the engineer: branches, history, firmware versions" — how not to lose firmware and remember which code each build came from.