thoughts/net/subnets.md

104 lines
4.9 KiB
Markdown

# Understanding IP Addresses, Subnet Masks, and CIDR
**Disclaimer:** Most, if not all, information in this article applies to both
IPv4 and IPv6. However, an IPv6 address is `4` times the size of an IPv4
address, which is 4 times the math the author is willing to do. Due to this, all
examples will be in the context of IPv4.
---
Cloudflare owns the number `16843009`. At least, as far as networks are
concerned they do. You'd be more likely to recognize it in the form `1.1.1.1`.
So how does the number `16843009` relate to the IP address `1.1.1.1`? Let's
start with what an IP address really is.
An IPv4 address is normally expressed as `A.B.C.D`, where each letter represents
an octet in decimal form. Since there are `4` octets, an IPv4 address is exactly
`32`-bits long. If you take the IP address `10.42.7.19` and write it in binary
form, you get `00001010.00101010.00000111.00010011`. This is referred to as a
bit string, which you'll notice contains exactly `32` bits as expected. Since
an octet will always be `8` bits, the dot separators aren't needed in a bit
string. This means that the bit string should really be written as
`00001010001010100000011100010011`. These bit strings can also be referred to as
`32`-bit integers. In other words, they are whole numbers that take up exactly
`32` bits of memory. The example bit string in this case could be written in
decimal form as the number `170526483`. We now have 3 ways to describe the same
IP address:
- `10.42.7.19`
- `00001010001010100000011100010011`
- `170526483`
You'll likely never need to write an IP address in any other form other than the
first one (commonly referred to as dotted decimal form), so why is this
important? For computers, it is fast and straight-forward to operate on IP
addresses when they are bit strings. Combined with an important tool called a
subnet mask, they can quickly compute the start and end of a range an IP address
is in.
A subnet mask is a 32-bit string, similar to an IPv4 address, with the
additional constraint that it must be a series of `1` bits immediately followed
by a series of `0` bits. This means that as soon as the first `0` bit appears,
all remaining bits must be `0`. The following bit string is a valid subnet mask:
`11111111111111111111111100000000`. If you split up the 32 bits into 4 groups of
octets, and convert those groups to decimal form, you get `255`, `255`, `255`,
and `0`, written as `255.255.255.0`. To drive the point home, both forms
represent the same subnet mask:
- `11111111111111111111111100000000`
- `255.255.255.0`
Another way of writing a subnet mask is CIDR notation, which is of the form
`/N`, where `N` represents the number of `1` bits at the beginning of the mask.
This means that writing `10.42.7.19/24` indicates you have the IP address
`10.42.7.19` with the subnet mask of `11111111111111111111111100000000` (or
`255.255.255.0` as we determined above). You'll notice the bit string contains
24 leading `1` bits, the value of `N`.
So how does a computer use a subnet mask to obtain information from an IP
address? For finding the first IP in a range defined by a subnet, the equation
is very straightforward. Take the given IP address and subnet mask, and perform
a bitwise AND on them. For example, using the IP address above, `10.42.7.19`,
and the subnet mask `255.255.255.0`, the equation would look like this:
```
00001010001010100000011100010011 (10.42.7.19)
& 11111111111111111111111100000000 (255.255.255.0)
--------------------------------
00001010001010100000011100000000 (10.42.7.0)
```
So we now know that the start of the range is `10.42.7.0`. With this
information, finding the end of the range is just as easy. First, compute the
inverse of the subnet mask. This can be done using a bitwise NOT:
```
~ 11111111111111111111111100000000 (255.255.255.0)
--------------------------------
00000000000000000000000011111111 (0.0.0.255)
```
Finally, take the inverted subnet mask and the first IP address in the range,
and apply a bitwise OR to them:
```
00001010001010100000011100000000 (10.42.7.0)
| 00000000000000000000000011111111 (0.0.0.255)
--------------------------------
00001010001010100000011111111111 (10.42.7.255)
```
Putting this all together, we are given the IP address `10.42.7.19` and the
subnet mask `255.255.255.0`, which can be rewritten as `10.42.7.19/24`. The
range this IP address belongs to (with that subnet mask) comes out to
`10.42.7.0-10.42.7.255`. What makes these operations even more useful is they
can be applied to any IP address in the range and get the same answer. In
conclusion:
- IPv4 addresses are 32-bit integers.
- Subnet masks define the range an IP address is in.
- The first IP address in the range can be found by applying a bitwise AND to
the given IP address and the subnet mask.
- The last IP address in the range can be found by applying a bitwise NOT to
the subnet mask, then a bitwise OR to the inverted subnet mask and the first
IP address.