Chapter 2: IP Addressing and IPv6


The Role of IP Addresses

Every device on a network needs an address — a unique identifier that allows other devices to find it and send data to it. At Layer 3 (the Network layer), this identifier is the IP address. IP addresses serve two purposes: identification (which device?) and location (which network?).

Two versions of the Internet Protocol coexist today: IPv4, which has powered the Internet since 1983, and IPv6, designed to replace it as the world runs out of IPv4 addresses.


IPv4 Addressing

Address Format

An IPv4 address is a 32-bit number, typically written in dotted-decimal notation — four octets (bytes) separated by dots:

192.168.1.42

Each octet ranges from 0 to 255. In binary, the address above is:

11000000.10101000.00000001.00101010

This gives IPv4 a total address space of $2^{32} = 4{,}294{,}967{,}296$ addresses — roughly 4.3 billion. This seemed enormous in 1981 but is now exhausted.

Network and Host Portions

Every IP address is divided into two parts:

The division is determined by the subnet mask.

Subnet Masks and CIDR

A subnet mask is a 32-bit value where the network bits are set to 1 and the host bits are set to 0:

CIDR Notation Subnet Mask Network Bits Host Bits Usable Hosts
/8 255.0.0.0 8 24 16,777,214
/16 255.255.0.0 16 16 65,534
/24 255.255.255.0 24 8 254
/28 255.255.255.240 28 4 14
/30 255.255.255.252 30 2 2
/32 255.255.255.255 32 0 1 (single host)

CIDR (Classless Inter-Domain Routing) notation appends the prefix length to the address: 192.168.1.0/24 means “the first 24 bits are the network portion.”

The number of usable hosts is $2^{(32 - \text{prefix})} - 2$ (subtracting the network address and broadcast address).

Subnetting Example

Suppose you have the network 10.0.0.0/8 and need to create 4 subnets:

Original:  10.0.0.0/8     → 16,777,214 hosts

Split into /10 subnets (borrow 2 bits → 2² = 4 subnets):
  10.0.0.0/10    → 10.0.0.1   to 10.63.255.254   (4,194,302 hosts)
  10.64.0.0/10   → 10.64.0.1  to 10.127.255.254  (4,194,302 hosts)
  10.128.0.0/10  → 10.128.0.1 to 10.191.255.254  (4,194,302 hosts)
  10.192.0.0/10  → 10.192.0.1 to 10.255.255.254  (4,194,302 hosts)

Special IPv4 Addresses

Address / Range Purpose
0.0.0.0 Default route / “any” address
127.0.0.0/8 Loopback (localhost)
10.0.0.0/8 Private network (Class A)
172.16.0.0/12 Private network (Class B)
192.168.0.0/16 Private network (Class C)
169.254.0.0/16 Link-local (auto-configuration)
224.0.0.0/4 Multicast
255.255.255.255 Broadcast

Private addresses (RFC 1918) are not routable on the public Internet. They’re used inside LANs and translated to public addresses via NAT (covered in Chapter 5).

Working with IPv4 in Python

Python’s ipaddress module makes IP calculations straightforward:

import ipaddress

# Parse an address and network
addr = ipaddress.ip_address("192.168.1.42")
net = ipaddress.ip_network("192.168.1.0/24")

print(addr.is_private)          # True
print(net.num_addresses)        # 256
print(addr in net)              # True
print(list(net.hosts())[:3])    # First 3 usable hosts

Full example: code/ip_address_calculator.py


IPv6 Addressing

Why IPv6?

IPv4’s 4.3 billion addresses are exhausted. While NAT has extended IPv4’s life by allowing private networks to share a single public address, this is a workaround — not a solution. NAT breaks end-to-end connectivity, complicates peer-to-peer applications, and adds latency.

IPv6 was designed to solve the address exhaustion problem permanently, while also improving routing efficiency, security, and auto-configuration.

Address Format

An IPv6 address is 128 bits — four times longer than IPv4. It’s written as eight groups of four hexadecimal digits, separated by colons:

2001:0db8:85a3:0000:0000:8a2e:0370:7334

The total address space is $2^{128} \approx 3.4 \times 10^{38}$ — enough to assign trillions of addresses to every grain of sand on Earth.

Address Simplification Rules

IPv6 addresses can be shortened using two rules:

  1. Leading zeros in each group can be omitted: 0db8db8, 00000
  2. One consecutive group of all-zero groups can be replaced with :: (only once per address)
Full:        2001:0db8:85a3:0000:0000:8a2e:0370:7334
Simplified:  2001:db8:85a3::8a2e:370:7334

IPv6 Address Types

Type Prefix Description
Global Unicast 2000::/3 Publicly routable (like IPv4 public addresses)
Link-Local fe80::/10 Auto-configured, valid only on local link
Unique Local fc00::/7 Private (like IPv4 RFC 1918 addresses)
Multicast ff00::/8 One-to-many delivery
Loopback ::1 Equivalent to 127.0.0.1
Unspecified :: Equivalent to 0.0.0.0

Every IPv6 interface automatically gets a link-local address (starting with fe80::) derived from its MAC address or randomly generated. This enables communication on the local network without any configuration.

IPv6 Subnetting

IPv6 subnetting follows the same principles as IPv4 but with much larger address blocks. The standard allocation for a site is a /48 prefix, which is then subnetted into /64 networks:

ISP assigns:       2001:db8:abcd::/48
                    ↓
Site creates subnets:
  2001:db8:abcd:0001::/64  → Office LAN
  2001:db8:abcd:0002::/64  → Server VLAN
  2001:db8:abcd:0003::/64  → IoT network
  ...
  (65,536 possible /64 subnets)

Within each /64 network, the host portion is 64 bits — $2^{64}$ possible addresses per subnet.

IPv6 Neighbor Discovery

IPv6 replaces ARP (used in IPv4 to map IP → MAC addresses) with Neighbor Discovery Protocol (NDP), which uses ICMPv6 messages:

Working with IPv6 in Python

The same ipaddress module handles IPv6 seamlessly:

import ipaddress

addr6 = ipaddress.ip_address("2001:db8:85a3::8a2e:370:7334")
net6 = ipaddress.ip_network("2001:db8:abcd::/48")

print(addr6.is_global)          # True (in real allocation)
print(addr6.exploded)           # Full form with all zeros
print(net6.num_addresses)       # 1,208,925,819,614,629,174,706,176
print(addr6 in net6)            # False (different prefix)

IPv4-to-IPv6 Transition Mechanisms

The Internet cannot switch to IPv6 overnight. Several transition mechanisms allow IPv4 and IPv6 to coexist:

Dual Stack

Devices run both IPv4 and IPv6 simultaneously. The OS chooses which protocol to use based on destination address availability. This is the most common approach today — most modern operating systems and routers support dual stack.

# Check dual-stack configuration on Linux
ip -4 addr show    # IPv4 addresses
ip -6 addr show    # IPv6 addresses

Tunneling

IPv6 packets are encapsulated inside IPv4 packets to traverse IPv4-only networks. Common tunneling methods include:

NAT64 and DNS64

NAT64 translates between IPv6 and IPv4 at a gateway, allowing IPv6-only clients to reach IPv4 servers. DNS64 synthesizes AAAA records (IPv6) from A records (IPv4) so that IPv6 clients can discover IPv4-only services.


Practical IP Address Management

Checking Your Addresses

# Show all IP addresses (IPv4 and IPv6)
ip addr show

# Show only IPv4
ip -4 addr show

# Show only IPv6
ip -6 addr show

# Resolve a hostname to IP
dig example.com A        # IPv4
dig example.com AAAA     # IPv6
host example.com

Testing Connectivity

# Ping using IPv4
ping -c 4 8.8.8.8

# Ping using IPv6
ping6 -c 4 2001:4860:4860::8888

# Check if IPv6 is working
curl -6 https://ifconfig.co

Key Takeaways


← Previous: The OSI Model and TCP/IP Stack Table of Contents Next: Network Configuration and Interfaces →