IP addressing (IPv4, IPv6)
Every device that sends or receives data on the internet needs an answer to one question: how does a packet know where to go? A MAC address only identifies a device on its own local network — it means nothing to a router three networks away. What does mean something everywhere is an IP address, the logical address that IP, the Internet layer protocol, uses to route a packet from any host to any other host, anywhere on the internet.
IPv4: 32 bits, four numbers
An IPv4 address is a 32-bit number. Written in binary it would be unreadable, so it's conventionally split into four 8-bit groups (called octets) and written in dotted-decimal notation — each octet converted to a decimal number from 0 to 255, separated by dots:
Each octet is 8 bits, and 8 bits can represent 2⁸ = 256 distinct values, numbered 0 through 255 — which is exactly why you'll never see a valid IPv4 octet above 255. The full 32-bit address space gives 2³² addresses, a little over 4.3 billion — a number that seemed enormous in the 1980s and turned out, decades later, to be nowhere near enough for a planet full of phones, servers, and IoT devices. What engineers did about that shortage is the subject of the IPv6 section below, and of Public vs Private IP later in this module.
Network portion and host portion
An IP address isn't one flat number — part of it identifies which network a host belongs to, and the rest identifies which specific host on that network. Which bits are "network" and which are "host" isn't fixed; it's determined by a companion value called a subnet mask or, more commonly today, expressed in CIDR notation as a suffix like /24.
192.168.1.10/24 means the first 24 bits (the first three octets, 192.168.1) identify the network, and the remaining 8 bits identify the specific host on it. This is the single most important idea in IP addressing, and it deserves proper treatment on its own — the next two articles work through it in full: CIDR explains the notation and how it defines a network's size, and Subnet mask calculation walks through computing it by hand.
Address classes — a legacy scheme worth knowing
Before CIDR existed, IPv4 addresses were divided into fixed-size classes — Class A, B, and C being the ones you'll actually encounter references to:
| Class | First octet range | Default network size | Typical use |
|---|---|---|---|
| A | 1–126 | /8 (16.7 million hosts) | Very large networks |
| B | 128–191 | /16 (65,536 hosts) | Mid-sized networks |
| C | 192–223 | /24 (256 hosts) | Small networks |
This scheme was rigid and wasteful — an organization needing 300 hosts had no choice but to take a whole Class B, wasting over 65,000 addresses — and it was formally replaced by CIDR in 1993 precisely because classful allocation was burning through the IPv4 address space far faster than it needed to. You won't configure a "Class C network" on any modern router, but the terminology persists in casual conversation and older documentation, so it's worth recognizing even though CIDR is what you'll actually use.
Reading an address's role at a glance
Not every IPv4 address behaves the same way. Most of the addresses you'll ever type or see in a config file are ordinary unicast addresses — one address, identifying one specific host, no special behavior attached. The other three categories below are special cases you'll recognize by their range rather than by anything marked on the address itself:
- Private addresses, like
192.168.1.10, are reserved for use inside local networks and aren't routable on the public internet. Public vs Private IP covers this in full. - Loopback addresses, the
127.0.0.0/8range, always refer back to the host itself.127.0.0.1means "this machine," and you'll use it constantly to test a local service without touching the network at all. - Broadcast addresses, typically the last address in a network's range, send one message to every host on that network simultaneously. CIDR, next in this module, explains exactly which address that is and why.
IPv6: why 32 bits wasn't enough
IPv4's 4.3 billion addresses sound like plenty until you account for how allocation actually happened: entire blocks handed out to organizations, universities, and early internet pioneers well before phones, smart TVs, and IoT sensors existed, with no way to reclaim the unused portions efficiently. By the 1990s it was clear the pool would run out, and the IETF designed a successor with a vastly larger address space: IPv6, standardized in 1998 and still being adopted today, uses 128-bit addresses — 2¹²⁸ possible addresses, a number so large that a commonly cited comparison is that it could assign a unique address to every grain of sand on Earth many times over.
An IPv6 address is written as eight groups of four hexadecimal digits, separated by colons:
Two shortening rules make these more manageable to write and read:
- Leading zeros within a group can be dropped:
0db8becomesdb8. - One consecutive run of all-zero groups can be collapsed to
::, but only once per address, since using it twice would make the address ambiguous about how many zero groups were omitted from each spot.
Applying both rules, the address above becomes:
IPv6 also has its own loopback address, ::1 (equivalent to IPv4's 127.0.0.1). It drops broadcast entirely in favour of two more targeted schemes: multicast, where one packet is delivered to every host that has chosen to subscribe to a particular group, and anycast, where one address is advertised from many locations at once and the network delivers each packet to whichever copy is closest. Both get proper treatment later in the course; for now it's enough to know that "send to everyone on this network" is not how IPv6 works.
IPv4 vs. IPv6 at a glance
| IPv4 | IPv6 | |
|---|---|---|
| Address length | 32 bits | 128 bits |
| Notation | Dotted decimal (192.168.1.10) |
Hexadecimal, colon-separated (2001:db8::1) |
| Address space | ~4.3 billion | ~340 undecillion |
| Loopback | 127.0.0.1 |
::1 |
| Header design | Includes fields IPv6 removed for simplicity | Simplified, fixed-size header |
IPv6 adoption has been gradual rather than a hard cutover — most networks today run dual stack, meaning hosts have both an IPv4 and an IPv6 address and can use either, which is why you'll see both notations in real command output for years to come.
What an IP packet actually carries
Everything above describes the address itself, but the address is only one field in the header IP actually attaches to your data. A simplified view of building one, from the application down:
flowchart TD
A["Application data"] --> B["Transport header added<br/>(TCP or UDP: source port, destination port)"]
B --> C["IP header added<br/>source IP, destination IP, TTL, protocol number, MTU-related flags"]
C --> D["Link-layer frame added<br/>(source MAC, destination MAC)"]
Two IP header fields matter far more in practice than their obscurity suggests. TTL (Time To Live) starts at a value set by the sending host and gets decremented by one at every router that forwards the packet; if it hits zero, the packet is dropped and the router that dropped it sends back an ICMP "time exceeded" message. That single mechanism is what traceroute/tracepath actually exploits — sending packets with deliberately low TTLs to make each router along the path reveal itself. The protocol field says what's inside the packet (6 for TCP, 17 for UDP, 1 for ICMP), which is how the receiving host's IP stack knows which transport-layer handler should even look at the payload.
It's worth being precise about what IP itself does and doesn't do with this packet once it's built: IP does not retransmit anything and does not reorder anything. Those are the accuracy traps worth avoiding out loud in an interview — reliability and ordering are TCP's job, layered on top; plain IP (and UDP riding on it) will silently let a packet vanish or arrive out of sequence and never notice. What IP does do, on its own, is decide whether a packet needs to be split up to fit through a link with a smaller frame size than the packet — which is the one piece of "handling" that belongs to IP alone.
MTU and fragmentation
Every link has a MTU (Maximum Transmission Unit) — the largest frame it can carry in one piece. Standard Ethernet's is 1500 bytes. If an IP packet is larger than the MTU of the next link it needs to cross, one of two things happens: IPv4 either fragments it into smaller pieces that get reassembled at the destination, or — if the packet's Don't Fragment (DF) bit is set — the router refuses to fragment it, drops the whole packet, and sends back an ICMP "Destination Unreachable, fragmentation needed" message telling the sender what MTU it should have used instead. That feedback loop is what modern systems use to run Path MTU Discovery (PMTUD): send with DF set, shrink the packet size whenever that ICMP message comes back, and settle on the largest size the whole path actually supports.
You can watch this from a Linux host directly:
PING 192.168.1.1 (192.168.1.1) 1472(1500) bytes of data.
1480 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.612 ms
1480 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.598 ms
-M do sets the Don't Fragment bit and -s 1472 requests a 1472-byte payload — with the 8-byte ICMP header and 20-byte IP header, that lands exactly at a 1500-byte frame, the standard Ethernet MTU. It succeeds because the whole path supports 1500. Push past it and the failure looks distinctive:
PING 192.168.1.1 (192.168.1.1) 1473(1501) bytes of data.
ping: local error: message too long, mtu=1500
Note that this particular failure was caught locally, before the packet even left the host — the kernel already knew the outgoing interface's MTU. A failure further out along the path (a smaller-MTU link a few hops away, common over some VPNs and tunnels) instead shows up as packets that vanish silently rather than an immediate local error, which is a much harder symptom to diagnose: everything looks fine for small requests and mysteriously hangs for large ones. tracepath is built specifically to hunt for that:
The pmtu 1500 in the output is tracepath actively probing the path for the smallest MTU any hop reports, which is exactly the number a mismatched tunnel or VPN interface would drop.
Reading IP addresses in real command output
On a Linux host, ip addr show lists the addresses assigned to each network interface. (You may still see ifconfig in older guides; it's deprecated on modern distributions and can report incomplete information about interfaces with multiple addresses, so prefer ip.)
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:4e:66:a1 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0
inet6 fe80::a00:27ff:fe4e:66a1/64 scope link
Reading this line by line: link/ether is the interface's MAC address (Layer 2, covered in MAC address); inet 192.168.1.10/24 is its IPv4 address with the CIDR prefix; brd 192.168.1.255 is the broadcast address for that network; and inet6 fe80::... is an automatically assigned IPv6 link-local address, valid only on the local network segment. The actual numbers on your own machine will differ — the MAC address, IP address, and interface name are all specific to your hardware and network.
Practical scenario: the same wire, two different ideas about the network
Two servers sit on the same office switch. One can reach the other; the other can't reach back. Nothing is unplugged, both addresses look correct, and both are obviously "in 192.168.1.x."
Those two prefixes disagree about what "the local network" means, and that's the whole bug. Host A's /24 covers 192.168.1.0 through 192.168.1.255, so A considers B a local neighbour and sends frames to it directly. Host B's /25 covers only 192.168.1.128 through 192.168.1.255 — and 192.168.1.10 falls outside it. So when B replies, it decides the destination is on some other network and hands the packet to its default gateway instead of putting it straight onto the wire.
Confirming it takes one more command:
default via 192.168.1.129 dev eth0
192.168.1.128/25 dev eth0 proto kernel scope link src 192.168.1.200
There it is in the second line: B's own idea of its directly connected network stops at .128. Traffic to .10 follows the default route instead, and whether it ever comes back depends on whether that router is willing to send a packet back out of the interface it arrived on. Often it isn't, and you get exactly this signature — one direction works, the other silently fails.
The fix is to make the prefixes agree, not to change either address. And the lesson generalizes: 192.168.1.200 is not an address, it's half of one. Without the prefix it doesn't specify which network the host belongs to, which means it doesn't specify who the host can talk to directly.
Common mistakes
- Writing an IPv4 octet above 255 (like
192.168.1.999) — an easy typo that's invalid because 8 bits can't represent a value that high. - Assuming an IP address alone tells you the network size.
192.168.1.10could belong to a/24network of 256 addresses or a/16network of 65,536 — the prefix length is not optional information, it's part of the address's meaning, as the scenario above shows the hard way. - Treating IPv6's
::shorthand as usable more than once per address.2001::db8::1is invalid and ambiguous — there's no way to know how many zero groups each::was supposed to represent. - Confusing a private IP address with a public one just because it "looks like a normal IP."
192.168.1.10and8.8.8.8are both syntactically valid IPv4 addresses, but only one of them is reachable from the public internet — see Public vs Private IP for the exact ranges that distinguish them.
Practice exercises
- Convert the IPv4 address
10.0.0.5to its 32-bit binary representation, one octet at a time, and verify each octet is a valid 8-bit value. - Expand the shortened IPv6 address
fe80::1back to its full eight-group, four-hex-digit form. - Run
ip addr showon a machine you have access to, and identify the IPv4 address, its CIDR prefix, and the MAC address of at least one interface. Then runip route showand find the line that corresponds to the network that address belongs to. - Using the scenario above, work out which of these pairs would have the same problem and which would work fine:
10.0.0.5/8talking to10.99.0.7/8;172.16.4.10/16talking to172.16.200.9/24;192.168.0.50/23talking to192.168.1.50/23. For each, say whether both hosts agree that the other is a local neighbour.
Exercise 4 is impossible to answer reliably by eye, and that's the point — you have to be able to turn a prefix length into an actual range of addresses. That skill is entirely mechanical once you know the arithmetic, and it's what the next two articles build: CIDR for what the number after the slash means and how it sizes a network, then Subnet mask calculation for computing ranges and boundaries by hand.
Sources
- IETF, RFC 791 – Internet Protocol
- IETF, RFC 8200 – Internet Protocol, Version 6 (IPv6) Specification
- IETF, RFC 4632 – Classless Inter-domain Routing (CIDR)
- IETF, RFC 5952 – A Recommendation for IPv6 Address Text Representation — the
::compression rules. - IETF, RFC 1191 – Path MTU Discovery — the Don't Fragment bit and the ICMP "fragmentation needed" response documented in the MTU section above.