Internet Protocol

1. THE GIST

This lecture is about how data actually moves across a network at the lowest meaningful level. Every HTTP request, database query, and gRPC call you write eventually gets wrapped inside an IP packet — just a blob of data with a source and destination IP address. You'll learn how routers decide where to send packets using subnet masks, why packets have a time-to-live counter to prevent infinite loops, and how ICMP is the behind-the-scenes messenger protocol that powers ping and traceroute. The key practical payoff: if your database is on a different subnet than your app server, every query is making an extra hop through a router — and that router can become your hidden performance bottleneck.


2. THE BREAKDOWN

IP Address Structure

An IPv4 address is 32 bits (4 bytes), split into two logical parts: the network portion and the host portion. The /X (CIDR) notation tells you how many bits belong to the network. With /24, the first 3 bytes are network, the last byte is host — giving you up to 253 usable hosts.

Subnet mask: ANDing your IP with the subnet mask strips the host bits, leaving just the network address. This single bitwise operation answers: "Is this destination IP in my network, or do I need a router?"

Time to Live (TTL) & ICMP

Every packet carries a TTL byte — a hop counter. Each router decrements it by 1. When it hits zero, the router drops the packet and fires an ICMP message back to the source. This is exactly how traceroute works: it intentionally sends packets with TTL=1, then TTL=2, etc., and collects the ICMP "expired" responses from each router along the path.


3. ENGINEER'S NOTEBOOK

Core idea: Every byte you send in your app becomes an IP packet. The IP layer is stateless, simple, and routing-only — it doesn't care what's inside. Ports, encryption, sessions — all irrelevant at this layer.

Key moving parts:

  • IP packet = header (20–60 bytes) + data (up to ~1,500 bytes in practice). The header adds overhead you pay on every single packet.
  • Subnet mask = a bitwise AND filter. Apply to an IP to extract its network. Same result as yours = same subnet = no router needed.
  • Default gateway = your "I don't know" address. When a destination isn't in your subnet, the packet goes here first.
  • TTL = a hop counter. Decremented by every router. Hits zero → packet dropped, ICMP sent back. Prevents infinite routing loops.
  • ICMP = the out-of-band messenger. No ports, just IP. Carries error messages, ping echoes, traceroute replies.
  • MTU (1,500 bytes) = the real-world size ceiling. Even though the IP header says you can send 65KB, the network won't actually let you without fragmentation.
  • ECN = a 2-bit field the router sets to signal "I'm getting full" instead of silently dropping packets.

Gotchas and edge cases:

  • ICMP can be firewall-blocked. A machine can be alive but won't ping. Worse: blocking ICMP breaks fragmentation notifications, causing silent TCP black holes — connection opens but data never flows.
  • Routers can act as layer-2 switches when both sides are on the same subnet — no TTL decrement happens.
  • traceroute is not guaranteed accurate. Packets in different probes can take different routes, and some routers don't respond to ICMP at all (you get ***).
  • IP spoofing source addresses is easy locally, but your ISP's first router will drop packets whose source IP doesn't match your assigned range.
  • Fragmentation is almost always disabled (DF flag set) in modern stacks because reassembly is expensive, error-prone, and a security surface.

Terms worth knowing:

TermWhat it is
CIDR /24First 24 bits = network, last 8 = host. Up to 253 hosts.
Subnet maskBitwise AND template that extracts the network portion
Default gatewayThe router IP you fall back to for all non-local traffic
TTLHop counter in IP header, prevents infinite routing loops
MTUMax Transmission Unit — typically 1,500 bytes on Ethernet
ICMPControl message protocol, no ports, used for ping/traceroute/errors
FragmentationSplitting an IP packet to fit smaller MTU — dangerous, usually disabled
ECNExplicit Congestion Notification — router sets a bit instead of dropping
ARPMaps IP → MAC address in the same subnet (where ARP poisoning attacks happen)

Don't forget:

  • The router between your app and DB is a potential latency killer. Same subnet = switch speeds, no router involved.
  • Every packet pays a minimum 20-byte header tax.
  • ping failing ≠ host is down. ICMP might just be blocked.
  • TTL default is usually 64 or 128. If you see packets dying early in traceroute, network path is broken or ICMP is filtered.

4. QUICK REFERENCE CARD

IP PACKET
─────────────────────────────────────
Header:   20 bytes base, up to 60 bytes with options
Data:     up to 65,535 bytes total — but MTU caps real world at ~1,500
Key fields: src IP, dst IP, TTL, Protocol, Flags (DF/MF), ECN

SUBNET MATH
─────────────────────────────────────
IP AND subnet_mask = network_address
Same network address? → Talk directly (MAC/ARP)
Different?           → Send to default gateway

COMMON CIDR RANGES
/24  → 253 hosts   (255.255.255.0)
/16  → ~65K hosts  (255.255.0.0)
/32  → 1 host      (a single machine)

ICMP FAST FACTS
─────────────────────────────────────
Layer 3 only — NO ports
ping         = ICMP echo request / echo reply
traceroute   = TTL=1,2,3… + ICMP time exceeded replies
"host unreachable" / "port unreachable" / "frag needed" = ICMP errors
Blocked ICMP = broken ping, broken traceroute, possible TCP black hole

TTL DEFAULTS
─────────────────────────────────────
Linux/macOS: 64    Windows: 128    Many routers: 255
Each hop: -1       Reaches 0 → drop + ICMP expired sent to source

COMMANDS
─────────────────────────────────────
ping 8.8.8.8                      # Test reachability + RTT
traceroute google.com             # Map the route (Mac/Linux)
tracert google.com                # Same for Windows
ip route show                     # View routing table (Linux)
ifconfig / ip addr show           # See your IP + subnet

5. THE "AHA" MOMENT

Subnet mask = postal routing rules

Imagine you work in a giant office building. There's an internal mail room on every floor. The rule is: if you're sending a letter to someone on your floor, just walk it over yourself — no external post needed. But if the letter is going to a different floor or different building entirely, you drop it in the outgoing mail slot, and the building's central mailroom (the router/gateway) figures out the rest.

The subnet mask is the floor map. You AND your room number against the floor plan to answer: "Is this destination on my floor?" If yes → walk it over directly using the person's desk number (MAC address). If no → drop it at the mailroom (default gateway).

The clever part: you don't need to know the floor plan of every other floor. You just need to know your floor and the address of the mailroom. Everything else is the mailroom's problem — and if the mailroom doesn't know either, it passes it to another mailroom upstream.

This is exactly why putting your database on a different subnet from your app server is like making every SQL query go through the building's central mailroom — even though the database server is literally sitting in the next rack.


6. SHOW ME THE CODE

import socket
import struct
import os

# ─────────────────────────────────────────────────────────────────────
# Demonstrates subnet mask logic: the exact bitwise AND operation
# that every host runs to decide "same network or needs routing?"
# ─────────────────────────────────────────────────────────────────────

def ip_to_int(ip: str) -> int:
    """Convert dotted-decimal IP to a 32-bit integer for bitwise ops."""
    return struct.unpack("!I", socket.inet_aton(ip))[0]

def int_to_ip(n: int) -> str:
    """Convert 32-bit integer back to dotted-decimal IP."""
    return socket.inet_ntoa(struct.pack("!I", n))

def same_subnet(my_ip: str, target_ip: str, subnet_mask: str) -> bool:
    """
    The core decision every IP stack makes before routing.
    
    This is NOT a library call — it's the actual bitwise AND that the
    kernel runs on every outgoing packet. Understanding this is why
    /24 means '253 hosts' and why putting DB on a different subnet costs latency.
    """
    my_network     = ip_to_int(my_ip)     & ip_to_int(subnet_mask)
    target_network = ip_to_int(target_ip) & ip_to_int(subnet_mask)

    return my_network == target_network

def cidr_to_mask(prefix_len: int) -> str:
    """
    Convert /24 notation to 255.255.255.0 — same thing, different format.
    All 1s for the first N bits, 0s for the rest.
    """
    mask = (0xFFFFFFFF << (32 - prefix_len)) & 0xFFFFFFFF
    return int_to_ip(mask)

def subnet_info(ip: str, prefix_len: int):
    """Show how many hosts a given subnet supports — useful for config decisions."""
    mask = cidr_to_mask(prefix_len)
    network = int_to_ip(ip_to_int(ip) & ip_to_int(mask))
    host_bits = 32 - prefix_len
    # -2 because network address and broadcast are reserved
    usable_hosts = (2 ** host_bits) - 2
    return {
        "network":       network,
        "mask":          mask,
        "usable_hosts":  usable_hosts,
        "broadcast":     int_to_ip(ip_to_int(network) | ((1 << host_bits) - 1)),
    }


# ── Demo ─────────────────────────────────────────────────────────────

MY_IP   = "192.168.1.123"
MASK    = cidr_to_mask(24)    # /24 → 255.255.255.0

targets = [
    ("192.168.1.200", "App server — same rack"),
    ("192.168.2.50",  "DB on different subnet — will hit router!"),
    ("10.0.0.1",      "Completely different network"),
]

print(f"My IP:      {MY_IP}")
print(f"Subnet:     {MASK}  (/24)\n")

for ip, label in targets:
    local = same_subnet(MY_IP, ip, MASK)
    route = "direct via MAC/ARP" if local else "→ DEFAULT GATEWAY (router hop!)"
    print(f"  {ip:18s}  {label}")
    print(f"    Same subnet: {local}  |  Route: {route}\n")

print("Subnet details for 192.168.1.0/24:")
info = subnet_info("192.168.1.0", 24)
for k, v in info.items():
    print(f"  {k:15s}: {v}")

Output:

My IP:      192.168.1.123
Subnet:     255.255.255.0  (/24)

  192.168.1.200       App server — same rack
    Same subnet: True  |  Route: direct via MAC/ARP

  192.168.2.50        DB on different subnet — will hit router!
    Same subnet: False  |  Route: → DEFAULT GATEWAY (router hop!)

  10.0.0.1            Completely different network
    Same subnet: False  |  Route: → DEFAULT GATEWAY (router hop!)

Subnet details for 192.168.1.0/24:
  network        : 192.168.1.0
  mask           : 255.255.255.0
  usable_hosts   : 254
  broadcast      : 192.168.1.255

7. WHERE YOU'LL SEE THIS IN THE WILD

Database latency investigations. When a backend engineer reports "my queries are slow and I don't know why," the first infrastructure check is: are the app servers and database in the same subnet? If they're on different subnets — even in the same data center — every TCP connection and every query response is making a router hop. A congested router adds milliseconds. At scale, across thousands of requests per second, this becomes a serious problem.

VPC and cloud networking. In AWS, GCP, and Azure, you explicitly configure subnets inside a VPC. The rule holds: resources in the same subnet communicate at wire speed. Resources in different subnets cross a virtual router. Security groups and NACLs operate at these subnet boundaries — understanding subnets is a prerequisite for understanding cloud security.

Kubernetes networking. Pod-to-pod communication within a cluster uses overlay networks. Understanding why a pod on Node A can't directly ARP to a pod on Node B, and why all that traffic flows through CNI plugins (Flannel, Calico, Cilium), requires this subnet knowledge.

Firewall rules and ICMP blocking. Production environments almost universally block ICMP at the perimeter. This is why your ping to a production server fails but the service is up. It also explains why traceroute shows *** hops — each of those is a router that filters ICMP. The danger: if your MTU misconfiguration causes fragmentation-needed ICMP messages that are blocked, you get the TCP black hole — connections establish (the tiny SYN/ACK fits through) but data transfers silently stall.

Observability and traceroute in production. When latency spikes to an external API, traceroute or mtr tells you where in the path the delay is introduced. If the delay appears at hop 3, that's your ISP's backbone. If it's hop 1, it's your own gateway. Without understanding TTL and ICMP, these tools are just magic — with it, they're a surgical debugging tool.

TCP black hole debugging. A classic production mystery: service works fine internally, but external clients see connections that open then hang. Root cause is often Path MTU Discovery failing because ICMP "fragmentation needed" messages are dropped by a firewall. The fix is to either open ICMP type 3 code 4, or clamp the TCP MSS at the firewall.


8. EXPLAIN IT TO MY NON-TECH FRIEND

Imagine the internet is like a massive postal network. Your data — whether it's a photo, a message, or a Google search — gets stuffed into a standard envelope called an IP packet. On the front of that envelope, there's a "from" address (your IP) and a "to" address (the destination IP). That's it. The postal service doesn't care what's inside.

Now, the postal workers (routers) don't know every address in the world. They work like this: "Is this envelope for someone in my local neighbourhood? Great, I'll deliver it personally. Is it going somewhere far away? I'll pass it to the sorting office, and they'll figure out the next step." This is exactly what the subnet and default gateway do — they define your "neighbourhood" and identify your local "sorting office."

Every envelope also has a little counter on it called TTL — think of it as the number of stamps. Each postal worker peels off one stamp when they handle it. If all the stamps are gone and the envelope still hasn't arrived, the last postal worker throws it away and sends you a note saying "sorry, couldn't deliver this." That note is ICMP. When you use ping, you're essentially sending an envelope that says "please send me back a confirmation" — and if you get one, you know the postal route is working.