Introduction: The Network Layer
In Part 2, we explored how data moves within local networks at Layers 1 and 2. Now we ascend to Layer 3—the Network Layer—where the magic of global connectivity happens. This is where:
- Logical addresses (IP) replace physical addresses (MAC)
- Routers make forwarding decisions across networks
- Packets find paths across the globe
- The Internet becomes possible
Series Context: This is Part 3 of 20 in the Complete Protocols Master series. We're building up from physical layers to application protocols.
1
Part 1: OSI Model & Protocol Foundations
Network layers, encapsulation, TCP/IP model
2
Physical & Data Link Layers
Ethernet, Wi-Fi, VLANs, MAC addressing
3
Network Layer & IP
IPv4, IPv6, ICMP, routing protocols
You Are Here
4
Transport Layer
TCP, UDP, QUIC, ports, sockets
5
Session & Presentation Layers
TLS handshake, encryption, serialization
6
Web Protocols
HTTP/1.1, HTTP/2, HTTP/3, WebSockets
7
API Protocols
REST, GraphQL, gRPC, SOAP
8
DNS Deep Dive
DNS hierarchy, records, DNSSEC
9
Email Protocols
SMTP, IMAP, POP3, SPF/DKIM/DMARC
10
File Transfer Protocols
FTP, SFTP, SCP, rsync
11
Real-Time Protocols
WebRTC, SIP, RTP, VoIP
12
Streaming Protocols
HLS, DASH, RTMP, media delivery
13
IoT Protocols
MQTT, CoAP, Zigbee, LoRaWAN
14
VPN & Tunneling
IPsec, OpenVPN, WireGuard
15
Authentication Protocols
OAuth, SAML, OIDC, Kerberos
16
Network Management
SNMP, NetFlow, Syslog
17
Security Protocols
TLS/SSL, certificates, PKI
18
Cloud Provider Protocols
AWS, Azure, GCP APIs
19
Emerging Protocols
QUIC, HTTP/3, WebTransport
20
Web Security Standards
CORS, CSP, HSTS, SRI
Layer 3
Network Layer
Network Layer Responsibilities
- Logical Addressing: IP addresses identify devices globally
- Routing: Determining the best path for packets
- Packet Forwarding: Moving packets hop-by-hop toward destination
- Fragmentation: Breaking large packets to fit link MTU
- Error Reporting: ICMP for network diagnostics
Key Protocols: IP (IPv4, IPv6), ICMP, IGMP, IPsec
Key Devices: Routers, Layer 3 switches
MAC vs IP: MAC addresses work on local networks (Layer 2). IP addresses work globally (Layer 3). When a packet crosses a router, the MAC addresses change at each hop, but the IP addresses stay the same end-to-end.
IPv4 Addressing
Internet Protocol version 4 (IPv4) has been the backbone of the internet since 1983. Despite IPv6's availability, IPv4 still carries the majority of internet traffic.
IPv4
IPv4 Address Structure
An IPv4 address is a 32-bit number, typically written in dotted decimal notation:
IPv4 Address: 192.168.1.100
Binary representation:
192 .168 .1 .100
11000000 .10101000 .00000001 .01100100
Each octet: 8 bits (0-255)
Total: 32 bits = 4 bytes
Maximum addresses: 2^32 = 4,294,967,296 (~4.3 billion)
# Understanding IPv4 addresses in Python
import ipaddress
# Create an IPv4 address object
ip = ipaddress.IPv4Address('192.168.1.100')
print(f"Address: {ip}")
print(f"Integer value: {int(ip)}")
print(f"Binary: {bin(int(ip))}")
print(f"Packed bytes: {ip.packed}")
print(f"Is private: {ip.is_private}")
print(f"Is global: {ip.is_global}")
print(f"Is loopback: {ip.is_loopback}")
print(f"Is multicast: {ip.is_multicast}")
# Convert between formats
ip_from_int = ipaddress.IPv4Address(3232235876)
print(f"\nFrom integer 3232235876: {ip_from_int}")
# Binary to decimal conversion for each octet
def ip_to_binary(ip_str):
"""Convert IP address to binary representation"""
octets = ip_str.split('.')
binary = [format(int(octet), '08b') for octet in octets]
return '.'.join(binary)
print(f"\n192.168.1.100 in binary: {ip_to_binary('192.168.1.100')}")
IPv4 Address Classes (Historical)
Originally, IPv4 addresses were divided into classes. While classful addressing is obsolete, understanding it helps comprehend legacy systems and network fundamentals:
Historical Reference
Classful Address Ranges
| Class |
First Octet |
Range |
Default Mask |
Networks |
Hosts/Network |
| A |
0xxxxxxx |
1.0.0.0 - 126.255.255.255 |
/8 (255.0.0.0) |
126 |
16,777,214 |
| B |
10xxxxxx |
128.0.0.0 - 191.255.255.255 |
/16 (255.255.0.0) |
16,384 |
65,534 |
| C |
110xxxxx |
192.0.0.0 - 223.255.255.255 |
/24 (255.255.255.0) |
2,097,152 |
254 |
| D |
1110xxxx |
224.0.0.0 - 239.255.255.255 |
N/A |
Multicast |
| E |
1111xxxx |
240.0.0.0 - 255.255.255.255 |
N/A |
Reserved/Experimental |
Note: Classful addressing wasted IP addresses massively. A Class A network gave you 16 million hosts even if you needed 1,000. Modern networks use CIDR (Classless Inter-Domain Routing) for flexible address allocation.
Private and Special Addresses
RFC 1918
Private Address Ranges
These addresses are not routable on the public internet and can be reused by any organization:
| Range |
CIDR |
Addresses |
Typical Use |
| 10.0.0.0 - 10.255.255.255 |
10.0.0.0/8 |
16,777,216 |
Large enterprises, cloud VPCs |
| 172.16.0.0 - 172.31.255.255 |
172.16.0.0/12 |
1,048,576 |
Medium organizations |
| 192.168.0.0 - 192.168.255.255 |
192.168.0.0/16 |
65,536 |
Home networks, small offices |
Special Addresses
Other Reserved Addresses
| Range |
Purpose |
127.0.0.0/8 |
Loopback - localhost (usually 127.0.0.1) |
169.254.0.0/16 |
Link-Local - Auto-assigned when DHCP fails (APIPA) |
0.0.0.0/8 |
This Network - Used during boot before IP assigned |
255.255.255.255 |
Limited Broadcast - Local network broadcast |
100.64.0.0/10 |
Carrier-Grade NAT - ISP shared address space |
192.0.2.0/24 |
Documentation - TEST-NET-1 for examples |
203.0.113.0/24 |
Documentation - TEST-NET-3 for examples |
Subnetting & CIDR
Subnetting divides a network into smaller, manageable pieces. CIDR (Classless Inter-Domain Routing) provides flexible allocation by specifying exactly how many bits identify the network.
Subnet Mask
Understanding Subnet Masks
A subnet mask separates the network portion from the host portion of an IP address:
IP Address: 192.168.1.100
Subnet Mask: 255.255.255.0
Binary:
IP: 11000000.10101000.00000001.01100100
Mask: 11111111.11111111.11111111.00000000
|______Network______|___Host__|
Network bits (1s): Identify the network
Host bits (0s): Identify devices within the network
Network Address: 192.168.1.0 (all host bits = 0)
Broadcast: 192.168.1.255 (all host bits = 1)
Usable Hosts: 192.168.1.1 - 192.168.1.254 (254 hosts)
CIDR Notation
CIDR notation uses a slash followed by the number of network bits:
CIDR Reference
Common CIDR Blocks
| CIDR |
Subnet Mask |
Addresses |
Usable Hosts |
| /8 |
255.0.0.0 |
16,777,216 |
16,777,214 |
| /16 |
255.255.0.0 |
65,536 |
65,534 |
| /20 |
255.255.240.0 |
4,096 |
4,094 |
| /24 |
255.255.255.0 |
256 |
254 |
| /25 |
255.255.255.128 |
128 |
126 |
| /26 |
255.255.255.192 |
64 |
62 |
| /27 |
255.255.255.224 |
32 |
30 |
| /28 |
255.255.255.240 |
16 |
14 |
| /29 |
255.255.255.248 |
8 |
6 |
| /30 |
255.255.255.252 |
4 |
2 |
| /31 |
255.255.255.254 |
2 |
2* |
| /32 |
255.255.255.255 |
1 |
1 (host route) |
* /31 is special - used for point-to-point links (RFC 3021), no network/broadcast addresses needed
Subnet Calculations
# Comprehensive subnet calculator in Python
import ipaddress
def analyze_network(network_str):
"""
Analyze an IPv4 network and display all relevant information
"""
network = ipaddress.IPv4Network(network_str, strict=False)
print(f"Network Analysis: {network_str}")
print("=" * 50)
# Basic info
print(f"Network Address: {network.network_address}")
print(f"Broadcast Address: {network.broadcast_address}")
print(f"Subnet Mask: {network.netmask}")
print(f"Wildcard Mask: {network.hostmask}")
print(f"CIDR Notation: /{network.prefixlen}")
# Address counts
print(f"\nTotal Addresses: {network.num_addresses}")
print(f"Usable Hosts: {network.num_addresses - 2 if network.num_addresses > 2 else network.num_addresses}")
# Host range
hosts = list(network.hosts())
if hosts:
print(f"First Usable Host: {hosts[0]}")
print(f"Last Usable Host: {hosts[-1]}")
# Binary representation
print(f"\nBinary Network: {bin(int(network.network_address))[2:].zfill(32)}")
print(f"Binary Mask: {bin(int(network.netmask))[2:].zfill(32)}")
return network
# Example analysis
print("EXAMPLE 1: Standard /24 Network")
print("-" * 50)
analyze_network("192.168.1.0/24")
print("\n\nEXAMPLE 2: /27 Subnet")
print("-" * 50)
analyze_network("10.0.0.0/27")
print("\n\nEXAMPLE 3: Check if IP is in network")
print("-" * 50)
network = ipaddress.IPv4Network("192.168.1.0/24")
test_ips = ["192.168.1.50", "192.168.2.50", "192.168.1.255"]
for ip_str in test_ips:
ip = ipaddress.IPv4Address(ip_str)
in_network = ip in network
print(f"{ip_str} in 192.168.1.0/24: {in_network}")
# Subnet division example
import ipaddress
def divide_network(network_str, new_prefix):
"""
Divide a network into smaller subnets
"""
network = ipaddress.IPv4Network(network_str)
if new_prefix <= network.prefixlen:
raise ValueError(f"New prefix must be larger than {network.prefixlen}")
subnets = list(network.subnets(new_prefix=new_prefix))
print(f"Dividing {network_str} into /{new_prefix} subnets:")
print("=" * 60)
for i, subnet in enumerate(subnets):
hosts = list(subnet.hosts())
first_host = hosts[0] if hosts else "N/A"
last_host = hosts[-1] if hosts else "N/A"
print(f"Subnet {i+1}: {subnet}")
print(f" Network: {subnet.network_address}")
print(f" Broadcast: {subnet.broadcast_address}")
print(f" Range: {first_host} - {last_host}")
print(f" Hosts: {len(hosts)}")
print()
return subnets
# Divide a /24 into four /26 subnets
print("Scenario: Divide 192.168.1.0/24 into 4 departments")
print("-" * 60)
divide_network("192.168.1.0/24", 26)
Quick Math:
- Hosts per subnet = 2(32 - prefix) - 2
- /24 = 28 - 2 = 254 hosts
- /26 = 26 - 2 = 62 hosts
- /28 = 24 - 2 = 14 hosts
- Subnets when dividing = 2(new_prefix - old_prefix)
IPv6 Addressing
IPv6 was designed to solve IPv4 address exhaustion. With 128-bit addresses, it provides 340 undecillion addresses—enough for every atom on Earth's surface!
Why IPv6?
IPv4 vs IPv6 Comparison
| Feature |
IPv4 |
IPv6 |
| Address Size |
32 bits (4 bytes) |
128 bits (16 bytes) |
| Address Space |
~4.3 billion |
~340 undecillion (3.4 × 1038) |
| Notation |
Dotted decimal (192.168.1.1) |
Hexadecimal (2001:db8::1) |
| Header Size |
20-60 bytes (variable) |
40 bytes (fixed) |
| Fragmentation |
Routers and hosts |
Source only (no router fragmentation) |
| Broadcast |
Yes |
No (uses multicast instead) |
| IPsec |
Optional |
Mandatory support |
| Auto-configuration |
DHCP required |
SLAAC built-in |
Format Rules
IPv6 Address Structure
Full IPv6 Address:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
Structure:
- 8 groups of 4 hexadecimal digits
- Separated by colons
- Each group = 16 bits
- Total = 128 bits
Compression Rules:
1. Leading zeros in a group can be omitted:
2001:0db8:0001:0000:0000:0000:0000:0001
→ 2001:db8:1:0:0:0:0:1
2. One sequence of consecutive all-zero groups can be replaced with ::
2001:db8:1:0:0:0:0:1
→ 2001:db8:1::1
3. :: can only be used ONCE in an address
(otherwise ambiguous how many zeros)
Examples:
::1 = Loopback (like 127.0.0.1)
:: = All zeros (unspecified)
2001:db8:: = 2001:db8:0:0:0:0:0:0
fe80::1 = Link-local address
2001:db8:85a3::8a2e:370:7334 = Typical compressed form
# Working with IPv6 in Python
import ipaddress
# Create IPv6 address objects
full_addr = ipaddress.IPv6Address('2001:0db8:85a3:0000:0000:8a2e:0370:7334')
compressed = ipaddress.IPv6Address('2001:db8:85a3::8a2e:370:7334')
loopback = ipaddress.IPv6Address('::1')
print("IPv6 Address Analysis")
print("=" * 60)
for addr in [full_addr, compressed, loopback]:
print(f"\nAddress: {addr}")
print(f" Compressed: {addr.compressed}")
print(f" Exploded: {addr.exploded}")
print(f" Is loopback: {addr.is_loopback}")
print(f" Is link-local: {addr.is_link_local}")
print(f" Is global: {addr.is_global}")
print(f" Is private: {addr.is_private}")
# IPv6 network analysis
print("\n\nIPv6 Network Analysis")
print("=" * 60)
network = ipaddress.IPv6Network('2001:db8::/32')
print(f"Network: {network}")
print(f"Prefix: /{network.prefixlen}")
print(f"Num addresses: {network.num_addresses}")
print(f"First addr: {network.network_address}")
print(f"Last addr: {network.broadcast_address}")
# Common IPv6 prefix sizes
print("\n\nCommon IPv6 Allocations")
print("=" * 60)
allocations = [
("/32", "Regional Internet Registry (RIR) to ISP"),
("/48", "ISP to Customer site"),
("/56", "ISP to Residential customer"),
("/64", "Single subnet (standard)"),
("/128", "Single host (loopback, etc.)")
]
for prefix, description in allocations:
print(f"{prefix:8} - {description}")
IPv6 Address Types
Address Types
IPv6 Address Categories
| Type |
Prefix |
Description |
| Global Unicast |
2000::/3 |
Routable on the internet (like public IPv4) |
| Link-Local |
fe80::/10 |
Auto-configured, not routed (local network only) |
| Unique Local (ULA) |
fc00::/7 (fd00::/8 used) |
Private addresses (like RFC 1918 in IPv4) |
| Multicast |
ff00::/8 |
One-to-many communication |
| Loopback |
::1/128 |
Localhost (like 127.0.0.1) |
| Unspecified |
::/128 |
No address assigned yet |
| Documentation |
2001:db8::/32 |
For examples and documentation |
IPv6 in Practice: Every IPv6 interface typically has multiple addresses:
- Link-local (fe80::) - Always present, auto-configured
- Global unicast (2xxx::) - For internet communication
- Privacy address - Temporary, rotates for privacy (SLAAC)
ICMP: Internet Control Message Protocol
ICMP provides error reporting and diagnostic functions for IP. It's the protocol behind ping and traceroute.
ICMP Messages
Common ICMP Message Types
| Type |
Name |
Description |
| 0 |
Echo Reply |
Response to ping (Type 8) |
| 3 |
Destination Unreachable |
Packet couldn't be delivered |
| 4 |
Source Quench |
Congestion control (deprecated) |
| 5 |
Redirect |
Better route available |
| 8 |
Echo Request |
Ping request |
| 11 |
Time Exceeded |
TTL expired (used by traceroute) |
| 12 |
Parameter Problem |
Bad IP header |
Ping and Traceroute
# Ping - Test connectivity using ICMP Echo Request/Reply
# Basic ping
ping google.com
# Ping with count
ping -c 4 google.com # Linux/macOS
ping -n 4 google.com # Windows
# Ping with specific TTL
ping -t 10 google.com # Linux
ping -i 10 google.com # macOS
# Continuous ping with timestamp
ping -D google.com # Linux (show timestamp)
# Traceroute - Discover the path packets take
# Linux
traceroute google.com
# Windows
tracert google.com
# Using ICMP instead of UDP (Linux)
sudo traceroute -I google.com
# Show AS numbers
traceroute -A google.com
# MTR - Combined ping + traceroute (live updating)
mtr google.com
# Implement ping in Python (requires root/admin)
import socket
import struct
import time
import select
def calculate_checksum(data):
"""Calculate ICMP checksum"""
if len(data) % 2:
data += b'\x00'
checksum = 0
for i in range(0, len(data), 2):
word = (data[i] << 8) + data[i + 1]
checksum += word
checksum = (checksum >> 16) + (checksum & 0xFFFF)
checksum = ~checksum & 0xFFFF
return checksum
def create_icmp_packet(icmp_id, sequence):
"""Create an ICMP Echo Request packet"""
# Type 8 = Echo Request, Code 0
icmp_type = 8
icmp_code = 0
checksum = 0
# Header without checksum
header = struct.pack('!BBHHH', icmp_type, icmp_code, checksum, icmp_id, sequence)
# Data payload (timestamp)
data = struct.pack('!d', time.time())
# Calculate checksum
checksum = calculate_checksum(header + data)
# Rebuild header with checksum
header = struct.pack('!BBHHH', icmp_type, icmp_code, checksum, icmp_id, sequence)
return header + data
def ping(host, timeout=2):
"""
Send ICMP Echo Request and measure RTT
Note: Requires root/administrator privileges
"""
try:
# Resolve hostname
dest_ip = socket.gethostbyname(host)
# Create raw socket for ICMP
icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
icmp_socket.settimeout(timeout)
# Create and send packet
icmp_id = 1
sequence = 1
packet = create_icmp_packet(icmp_id, sequence)
send_time = time.time()
icmp_socket.sendto(packet, (dest_ip, 0))
# Wait for reply
ready = select.select([icmp_socket], [], [], timeout)
if ready[0]:
recv_packet, addr = icmp_socket.recvfrom(1024)
recv_time = time.time()
# Parse reply (skip IP header - 20 bytes)
icmp_header = recv_packet[20:28]
icmp_type, code, checksum, p_id, seq = struct.unpack('!BBHHH', icmp_header)
if icmp_type == 0: # Echo Reply
rtt = (recv_time - send_time) * 1000
return {
'success': True,
'host': host,
'ip': dest_ip,
'rtt_ms': round(rtt, 2),
'ttl': recv_packet[8] # TTL from IP header
}
return {'success': False, 'host': host, 'error': 'Timeout'}
except PermissionError:
return {'success': False, 'error': 'Root/Admin privileges required'}
except socket.gaierror:
return {'success': False, 'error': f'Cannot resolve {host}'}
except Exception as e:
return {'success': False, 'error': str(e)}
# Example (run with sudo/admin)
print("Note: Running ping requires root/administrator privileges")
print("Example usage: sudo python script.py")
print()
print("Expected output format:")
print({
'success': True,
'host': 'google.com',
'ip': '142.250.185.46',
'rtt_ms': 15.23,
'ttl': 117
})
Traceroute Mechanism: Traceroute works by sending packets with increasing TTL values (1, 2, 3...). Each router decrements TTL and sends back ICMP "Time Exceeded" when TTL reaches 0, revealing its IP address. This maps the entire path to the destination.
Routing Fundamentals
Routing is the process of selecting paths for traffic in a network. Routers use routing tables to decide where to forward packets.
Routing Basics
How Routing Works
- Router receives a packet
- Examines destination IP address
- Looks up longest matching prefix in routing table
- Forwards packet to next hop or directly to destination
- Decrements TTL (drops if TTL = 0)
# View routing table on different systems
# Linux
ip route show
# or
route -n
# or
netstat -rn
# Windows
route print
# or
netstat -rn
# macOS
netstat -rn
# Example Linux output:
# default via 192.168.1.1 dev eth0 proto dhcp metric 100
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
# Explanation:
# default - Default route (0.0.0.0/0)
# via 192.168.1.1 - Next hop (gateway)
# dev eth0 - Outgoing interface
# metric 100 - Route preference (lower = preferred)
# Simulate a simple routing table lookup
class Router:
"""Simple router simulation with longest prefix matching"""
def __init__(self, name):
self.name = name
self.routes = [] # List of (network, next_hop, interface)
def add_route(self, network, next_hop, interface):
"""Add a route to the routing table"""
import ipaddress
net = ipaddress.IPv4Network(network)
self.routes.append({
'network': net,
'prefix': network,
'next_hop': next_hop,
'interface': interface
})
# Sort by prefix length (longest first) for proper matching
self.routes.sort(key=lambda r: r['network'].prefixlen, reverse=True)
def lookup(self, destination):
"""Find the best route for a destination (longest prefix match)"""
import ipaddress
dest_ip = ipaddress.IPv4Address(destination)
for route in self.routes:
if dest_ip in route['network']:
return {
'destination': destination,
'matched_route': route['prefix'],
'next_hop': route['next_hop'],
'interface': route['interface']
}
return {'destination': destination, 'result': 'No route found'}
def show_routes(self):
"""Display routing table"""
print(f"\nRouting Table for {self.name}")
print("=" * 60)
print(f"{'Network':<20} {'Next Hop':<16} {'Interface':<12}")
print("-" * 60)
for route in self.routes:
print(f"{route['prefix']:<20} {route['next_hop']:<16} {route['interface']:<12}")
# Create a router with typical routes
router = Router("R1")
# Add routes (specific to general)
router.add_route("192.168.1.0/24", "directly_connected", "eth0")
router.add_route("192.168.2.0/24", "192.168.1.254", "eth0")
router.add_route("10.0.0.0/8", "192.168.1.253", "eth0")
router.add_route("0.0.0.0/0", "192.168.1.1", "eth0") # Default route
router.show_routes()
# Test lookups
print("\nRoute Lookups:")
print("-" * 60)
test_destinations = [
"192.168.1.50", # Local network
"192.168.2.100", # Adjacent network
"10.50.100.200", # Remote private network
"8.8.8.8", # Internet (Google DNS)
]
for dest in test_destinations:
result = router.lookup(dest)
print(f"\n{dest}:")
for key, value in result.items():
print(f" {key}: {value}")
Routing Protocols
Protocol Types
Interior vs Exterior Gateway Protocols
IGP (Interior Gateway Protocol): Used within an organization (Autonomous System)
- RIP (Routing Information Protocol): Simple, distance-vector, max 15 hops. Legacy.
- OSPF (Open Shortest Path First): Link-state, uses Dijkstra's algorithm. Most common IGP.
- EIGRP (Enhanced Interior Gateway Routing Protocol): Cisco proprietary, advanced distance-vector.
- IS-IS (Intermediate System to Intermediate System): Link-state, popular with ISPs.
EGP (Exterior Gateway Protocol): Used between organizations
- BGP (Border Gateway Protocol): The protocol that routes the entire internet!
BGP: The Internet's Routing Protocol
BGP
Border Gateway Protocol
BGP is a path-vector protocol that exchanges routing information between Autonomous Systems (AS)—networks under single administrative control.
- AS Numbers: Unique identifiers (e.g., Google is AS15169)
- BGP Peers: Routers exchange routes via TCP port 179
- Path Selection: Based on policies, AS path length, and other attributes
- Full Internet Table: ~900,000+ routes (2024)
# Explore BGP and AS information
# Look up ASN for an IP
whois -h whois.radb.net 8.8.8.8
# View BGP looking glass (web tools)
# https://bgp.he.net/
# https://www.ripe.net/analyse/internet-measurements/routing-information-service-ris
# Check your own public IP's ASN
curl ipinfo.io
# Traceroute with AS numbers
traceroute -A google.com
# Example output shows AS path:
# 1 192.168.1.1 [AS0] 0.5ms
# 2 10.0.0.1 [AS12345] 5.2ms
# 3 72.14.238.1 [AS15169] 10.1ms # Google's AS
BGP Security: BGP was designed without security! Notable incidents:
- BGP Hijacking: Malicious AS announces routes it doesn't own
- Route Leaks: Accidental propagation of internal routes
- Solutions: RPKI (Resource Public Key Infrastructure), BGPsec
NAT: Network Address Translation
NAT allows multiple devices to share a single public IP address. It's what lets your entire home network access the internet through one IP from your ISP.
How NAT Works
NAT Translation Process
Outbound (Private → Public):
1. Device 192.168.1.100 sends packet to 8.8.8.8
2. Router receives packet
3. Router changes source IP: 192.168.1.100 → 203.0.113.50 (public IP)
4. Router records mapping in NAT table
5. Packet sent to internet with public source IP
Inbound (Public → Private):
1. Response arrives for 203.0.113.50
2. Router looks up NAT table
3. Router changes dest IP: 203.0.113.50 → 192.168.1.100
4. Packet forwarded to internal device
NAT Table Example:
┌─────────────────────┬──────────────────────┬───────────────┐
│ Internal │ External │ Destination │
│ 192.168.1.100:54321 │ 203.0.113.50:12345 │ 8.8.8.8:53 │
│ 192.168.1.101:54322 │ 203.0.113.50:12346 │ 8.8.8.8:53 │
└─────────────────────┴──────────────────────┴───────────────┘
Types of NAT
NAT Variants
NAT Types Explained
| Type |
Description |
Use Case |
| Static NAT |
1:1 mapping of private to public IP |
Servers that need consistent public IP |
| Dynamic NAT |
Pool of public IPs assigned dynamically |
Organizations with multiple public IPs |
| PAT/NAPT (Overload) |
Many private IPs share one public IP using ports |
Home routers, most common type |
| Port Forwarding |
External port → internal IP:port |
Hosting servers behind NAT |
# NAT configuration examples (Linux iptables)
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# Basic SNAT (Source NAT) - Masquerade
# All traffic from 192.168.1.0/24 gets NAT'd to eth0's IP
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# SNAT with specific public IP
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.50
# DNAT (Destination NAT) - Port forwarding
# Forward port 8080 on public IP to internal web server
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# View NAT rules
iptables -t nat -L -n -v
# View active NAT connections
conntrack -L
NAT Drawbacks:
- Breaks end-to-end connectivity: Devices behind NAT can't be reached directly
- Complicates protocols: FTP, SIP, gaming require special NAT traversal
- Adds latency: Translation takes time
- IPv6 eliminates need: Enough addresses for every device to have public IP
Hands-On Exercises
Exercise 1: Network Exploration Tools
# Complete IP investigation toolkit
# 1. Find your IP configuration
# Linux
ip addr show
ip route show
# Windows
ipconfig /all
# macOS
ifconfig
netstat -rn
# 2. Test connectivity
ping -c 4 google.com
ping -c 4 8.8.8.8
# 3. Trace the path
traceroute google.com
# 4. DNS lookup
nslookup google.com
dig google.com
# 5. Check what's listening
netstat -tlnp # Linux
netstat -an # Windows
# 6. Check your public IP
curl ifconfig.me
curl ipinfo.io/json
Exercise 2: Python IP Tools
# Comprehensive IP utility library
import ipaddress
import socket
class IPTools:
"""Collection of IP utility functions"""
@staticmethod
def get_network_info(ip_cidr):
"""Get comprehensive network information"""
network = ipaddress.IPv4Network(ip_cidr, strict=False)
return {
'network': str(network.network_address),
'broadcast': str(network.broadcast_address),
'netmask': str(network.netmask),
'wildcard': str(network.hostmask),
'prefix_length': network.prefixlen,
'total_hosts': network.num_addresses,
'usable_hosts': max(0, network.num_addresses - 2),
'first_usable': str(list(network.hosts())[0]) if list(network.hosts()) else None,
'last_usable': str(list(network.hosts())[-1]) if list(network.hosts()) else None,
'is_private': network.is_private
}
@staticmethod
def ip_in_range(ip, network):
"""Check if IP is in a network range"""
addr = ipaddress.IPv4Address(ip)
net = ipaddress.IPv4Network(network, strict=False)
return addr in net
@staticmethod
def get_ip_class(ip):
"""Determine classful address class (historical)"""
first_octet = int(ip.split('.')[0])
if first_octet < 128:
return 'A'
elif first_octet < 192:
return 'B'
elif first_octet < 224:
return 'C'
elif first_octet < 240:
return 'D (Multicast)'
else:
return 'E (Reserved)'
@staticmethod
def resolve_hostname(hostname):
"""Resolve hostname to IP address"""
try:
ip = socket.gethostbyname(hostname)
return {'hostname': hostname, 'ip': ip, 'success': True}
except socket.gaierror as e:
return {'hostname': hostname, 'error': str(e), 'success': False}
@staticmethod
def reverse_lookup(ip):
"""Reverse DNS lookup"""
try:
hostname = socket.gethostbyaddr(ip)[0]
return {'ip': ip, 'hostname': hostname, 'success': True}
except socket.herror as e:
return {'ip': ip, 'error': str(e), 'success': False}
@staticmethod
def summarize_networks(networks):
"""Summarize multiple networks into supernets"""
nets = [ipaddress.IPv4Network(n) for n in networks]
return list(ipaddress.collapse_addresses(nets))
# Examples
tools = IPTools()
# Network analysis
print("Network Analysis: 192.168.1.0/24")
print("=" * 50)
info = tools.get_network_info("192.168.1.0/24")
for key, value in info.items():
print(f" {key}: {value}")
# IP range check
print("\n\nIP Range Checks:")
print("=" * 50)
test_ips = ["192.168.1.50", "192.168.2.50", "10.0.0.1"]
for ip in test_ips:
in_range = tools.ip_in_range(ip, "192.168.1.0/24")
print(f" {ip} in 192.168.1.0/24: {in_range}")
# Hostname resolution
print("\n\nHostname Resolution:")
print("=" * 50)
hosts = ["google.com", "github.com", "invalid.hostname.test"]
for host in hosts:
result = tools.resolve_hostname(host)
if result['success']:
print(f" {host} → {result['ip']}")
else:
print(f" {host} → Error: {result['error']}")
# Network summarization (route aggregation)
print("\n\nNetwork Summarization:")
print("=" * 50)
networks_to_summarize = [
"192.168.0.0/24",
"192.168.1.0/24",
"192.168.2.0/24",
"192.168.3.0/24"
]
print(f" Original: {networks_to_summarize}")
summarized = tools.summarize_networks(networks_to_summarize)
print(f" Summarized: {[str(n) for n in summarized]}")
Self-Assessment
Quiz: Test Your Knowledge
- How many bits in an IPv4 address? (Answer: 32 bits)
- What's the subnet mask for /27? (Answer: 255.255.255.224)
- How many usable hosts in a /28 network? (Answer: 14)
- What private range is 172.16.0.0 in? (Answer: 172.16.0.0/12)
- What ICMP type is Echo Reply? (Answer: Type 0)
- What protocol runs the internet's routing? (Answer: BGP)
- What's the IPv6 loopback address? (Answer: ::1)
- What does NAT stand for? (Answer: Network Address Translation)
Summary & Next Steps
Key Takeaways:
- IPv4 uses 32-bit addresses; IPv6 uses 128-bit addresses
- CIDR notation (/24, /16) specifies network size flexibly
- Subnetting divides networks; calculate hosts with 2(32-prefix)-2
- ICMP provides diagnostics (ping, traceroute)
- Routers use longest prefix match to forward packets
- BGP routes between autonomous systems (the internet)
- NAT allows private IPs to share public IPs
Quick Reference
IP Addressing Cheat Sheet
- Private Ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
- /24: 256 IPs, 254 hosts (most common subnet)
- /30: 4 IPs, 2 hosts (point-to-point links)
- IPv6 Prefix: /64 is standard for subnets
- TTL: Default 64 or 128, prevents loops
Next in the Series
In Part 4: Transport Layer, we'll explore TCP, UDP, and QUIC. You'll learn about the three-way handshake, flow control, congestion control, ports, sockets, and how reliable (and unreliable) data delivery works end-to-end.
Continue the Series
Navigation
Part 2: Physical & Data Link Layers
Review Ethernet, Wi-Fi, MAC addressing, and switches.
Read Article
Part 4: Transport Layer
Deep dive into TCP, UDP, QUIC, ports, and reliable delivery.
Read Article
Part 5: Session & Presentation Layers
Explore TLS handshakes, encryption, and data serialization.
Read Article