104 lines
4.9 KiB
Markdown
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.
|