Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Computer Networks  Module 7 – IPv4 addressing, subnetting and NAT

IPv4 addresses, masks and CIDR notation

How a 32-bit IPv4 address splits into network and host bits, with subnet masks, CIDR prefixes, network and broadcast addresses and the special ranges.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free: 25 minutes

What you will learn

  • Convert IPv4 addresses between dotted-decimal, binary, hexadecimal and integer forms
  • Find the network address, broadcast address and host range for any prefix length
  • Explain why classful addressing gave way to CIDR
  • Recognise private, shared (CGNAT), loopback, link-local, documentation and multicast ranges

Before you start

What you’ll learn · Before you start

What you will learn

  • Convert IPv4 addresses between dotted-decimal, binary, hexadecimal and integer forms
  • Find the network address, broadcast address and host range for any prefix length
  • Explain why classful addressing gave way to CIDR
  • Recognise private, shared (CGNAT), loopback, link-local, documentation and multicast ranges

Before you start

On this page

An IPv4 address is a 32-bit number that identifies one network interface. People write it as four decimal numbers from 0 to 255 separated by dots, such as 10.20.30.40. Every address has two parts: the first bits say which network it is on, and the rest pick one device (a host) inside that network. CIDR notation says where the split falls: 10.20.30.40/20 means the first 20 bits are the network part. A subnet mask states the same boundary as a 32-bit pattern of ones followed by zeros, so /20 is the mask 255.255.240.0. Once you know the split, everything else follows: the network address (all host bits 0), the broadcast address (all host bits 1), the usable host addresses between them and how many there are. This lesson works each step by hand, runs it in Python, and maps the address blocks that are reserved for special uses.

One address, four ways of writing it

Dotted decimal is only a convenient way to write a number. Each of the four parts is one octet (8 bits), and the four octets read one after another form a single 32-bit integer: the original Internet Protocol specification, RFC 791, defines an address as “four octets (32 bits)”. Binary is what masks work on, hexadecimal shows up in packet captures and code, and the integer form appears in databases and some programming interfaces.

10.20.30.40 in four notations Python · four_forms.py
# One IPv4 address written four ways: dotted decimal, binary, hexadecimal and one integer.
import ipaddress

address = ipaddress.IPv4Address("10.20.30.40")
octets = [int(part) for part in str(address).split(".")]

print("dotted :", address)
print("binary :", ".".join(f"{o:08b}" for o in octets))
print("hex    :", "0x" + "".join(f"{o:02x}" for o in octets))
print("integer:", int(address))

# The integer is the four octets read as one base-256 number.
value = octets[0] * 256**3 + octets[1] * 256**2 + octets[2] * 256 + octets[3]
print("by hand:", value, value == int(address))
print("back   :", ipaddress.IPv4Address(value))

Output

dotted : 10.20.30.40
binary : 00001010.00010100.00011110.00101000
hex    : 0x0a141e28
integer: 169090600
by hand: 169090600 True
back   : 10.20.30.40

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 four_forms.py

  • Binary: every octet is padded to 8 digits, so 20 is 00010100. Dropping the leading zeros of an octet is an easy slip in hand calculations, and it puts every later bit in the wrong place.
  • Hexadecimal: two hex digits per octet (0a 14 1e 28), since one hex digit is exactly 4 bits.
  • Integer: the octets are the digits of a base-256 number, so the value is 10 × 256³ + 20 × 256² + 30 × 256 + 40, which is 169,090,600.
Number Base Converter Convert an octet between decimal, binary and hexadecimal with the working shown, to check a conversion done by hand.

Older parsers accept other spellings too. The POSIX rules for inet_addr(), which the C function inet_aton() follows, allow fewer than four parts and let the last part fill all the remaining bytes, so 127.1 means 127.0.0.1. They also read a part that starts with 0x as hexadecimal and a part that starts with 0 as octal. Browsers read the host of a URL in the same lenient way, under the IPv4 parser of the WHATWG URL Standard. Python’s ipaddress module refuses all of these forms, and since Python 3.9.5 it treats a leading zero as an error so that nothing is read as octal by surprise:

A strict and a lenient parser read the same text Python · lenient_parsers.py
# The strict parser of the ipaddress module against the C library's inet_aton(), which still
# accepts the old shorthand forms: fewer than four parts, hexadecimal (0x) and octal (a leading 0).
import ipaddress
import socket

print(f"{'text':<12}{'ipaddress':<11}inet_aton")
for text in ["127.0.0.1", "127.1", "0x7f.1", "2130706433", "0177.0.0.1", "010.1.2.3"]:
    try:
        strict = str(ipaddress.ip_address(text))
    except ValueError:
        strict = "rejected"
    legacy = socket.inet_ntoa(socket.inet_aton(text))
    print(f"{text:<12}{strict:<11}{legacy}")

Output

text        ipaddress  inet_aton
127.0.0.1   127.0.0.1  127.0.0.1
127.1       rejected   127.0.0.1
0x7f.1      rejected   127.0.0.1
2130706433  rejected   127.0.0.1
0177.0.0.1  rejected   127.0.0.1
010.1.2.3   rejected   8.1.2.3

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 lenient_parsers.py

The last line is the dangerous one. A person reads 010.1.2.3 as 10.1.2.3, while inet_aton() reads it as 8.1.2.3. When two parts of a system parse addresses differently, a check in one part, such as a rule that refuses internal addresses, can be dodged by writing the address in a form that only the other part understands.

Check the parsed address, not the text

Parse an untrusted address once, with a strict parser such as ipaddress.ip_address(), reject what it cannot read, and use the parsed value for both the check and the connection. Comparing text is not enough: 0x7f.1, 2130706433 and 127.1 all reach the loopback address through a lenient parser.

IP Address Converter Paste an address in any notation, including shorthand such as 0x7f.1, to see its dotted, integer, hexadecimal and binary forms.

Prefixes and masks: where the network part ends

A prefix length /n says that the first n bits of the address are the network part and the remaining 32 − n bits are the host part. RFC 4632, the CIDR specification, writes it as the address, a slash and a number from 0 to 32. The subnet mask records the same boundary as a 32-bit value: n one-bits followed by 32 − n zero-bits, written in dotted decimal.

10.20.30.40 as 32 bits in two rows: the first 20 bits are the network part, the last 12 bits the host part.bits 1–1610000010102000010100bits 17–3230000111104000101000/20network part: the first 20 bitshost part: the last 12 bits

Where the /20 boundary falls in 10.20.30.40

Text description of the diagram

The diagram writes the address 10.20.30.40 as its 32 bits, in two rows of 16 bits with the decimal value of each octet above its 8 bits.

  • Row 1 holds bits 1 to 16: 10 is 00001010 and 20 is 00010100. All 16 bits are shaded as network bits.
  • Row 2 holds bits 17 to 32: 30 is 00011110 and 40 is 00101000. Only the first four bits of the third octet (0001) are shaded; a thick line after them is labelled /20.
  • The legend says that the shaded cells are the network part, the first 20 bits, and the plain cells are the host part, the last 12 bits.

Because the boundary falls inside the third octet, that octet is the only one that needs binary when you work out the network and broadcast addresses: its network bits are 0001, so the network address has 16 in the third octet, and with the four host bits set to 1 the broadcast address has 31.

For /20, twenty ones fill the first two octets (255.255) and carry four bits into the third: 11110000, which is 240. The fourth octet is all zeros. So /20 and 255.255.240.0 are two spellings of one boundary. Because the ones always come first, an octet of a mask can take only nine values: 0, 128, 192, 224, 240, 248, 252, 254 and 255. RFC 4632 requires the mask to be contiguous, which rules out a mask such as 255.0.255.0.

Flip every bit of the mask and you get the wildcard mask, also called the host mask: 0.0.15.255 for a /20. It marks the host bits instead of the network bits, and some configuration languages ask for it in place of the mask.

Subnet Mask Cheat Sheet Every prefix from /0 to /32 with its mask, wildcard mask, binary form and host count on one page.

Network address, broadcast address and host range

Two bitwise operations give the edges of a subnet:

  • network address = address AND mask, which clears every host bit to 0;
  • broadcast address = network address OR wildcard mask, which sets every host bit to 1.

Take 10.20.30.40/20. The first two octets lie wholly in the network part, so they stay 10.20. The fourth octet lies wholly in the host part, so it becomes 0 in the network address and 255 in the broadcast address. Only the third octet, where the boundary falls, needs binary:

  • the address’s third octet: 00011110, which is 30;
  • the mask’s third octet: 11110000, which is 240;
  • address AND mask: 00010000, which is 16, the third octet of the network address;
  • the same with the four host bits set to 1: 00011111, which is 31, the third octet of the broadcast address.

So the subnet runs from 10.20.16.0, its network address, to 10.20.31.255, its broadcast address. Hosts use everything in between: 10.20.16.1 to 10.20.31.254. This program does the same arithmetic on the whole 32-bit number and then asks the ipaddress module:

The /20 worked with bit operations Python · network_by_hand.py
# Network, broadcast and host range of 10.20.30.40/20 with integer bit operations, then the same
# with the ipaddress module.
import ipaddress


def to_int(dotted):
    a, b, c, d = (int(x) for x in dotted.split("."))
    return (a << 24) | (b << 16) | (c << 8) | d


def to_dotted(n):
    return ".".join(str((n >> shift) & 0xFF) for shift in (24, 16, 8, 0))


address, prefix = "10.20.30.40", 20
mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF  # prefix one-bits, then zeros
wildcard = ~mask & 0xFFFFFFFF                      # the host bits
network = to_int(address) & mask                  # host bits cleared
broadcast = network | wildcard                     # host bits set

print("mask      ", to_dotted(mask))
print("wildcard  ", to_dotted(wildcard))
print("network   ", to_dotted(network))
print("broadcast ", to_dotted(broadcast))
print("hosts     ", to_dotted(network + 1), "to", to_dotted(broadcast - 1))
print("addresses ", 2 ** (32 - prefix), "usable", 2 ** (32 - prefix) - 2)

net = ipaddress.ip_network(f"{address}/{prefix}", strict=False)
print("ipaddress ", net, net.broadcast_address, net.num_addresses)
try:
    ipaddress.ip_network(f"{address}/{prefix}")
except ValueError as error:
    print("strict    ", error)

Output

mask       255.255.240.0
wildcard   0.0.15.255
network    10.20.16.0
broadcast  10.20.31.255
hosts      10.20.16.1 to 10.20.31.254
addresses  4096 usable 4094
ipaddress  10.20.16.0/20 10.20.31.255 4096
strict     10.20.30.40/20 has host bits set

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 network_by_hand.py

The last line shows a common trap. ip_network("10.20.30.40/20") raises ValueError because the address has host bits set: an address with host bits is a host, not a network. Pass strict=False when you mean “the network this address is in”, or use ipaddress.ip_interface(), which keeps the address and its network together.

The two edge addresses have jobs of their own. The network address names the subnet itself, for example in a routing table. The broadcast address reaches every host on the subnet at once, and RFC 1122 forbids it as the source of a packet. A packet sent to the broadcast address of a distant subnet is a directed broadcast, and routers no longer pass those on by default: RFC 2644 changed the default because almost all directed broadcasts crossing the Internet were attacks that used a whole subnet to multiply traffic towards a victim.

Subnet Calculator Type any address with a prefix or mask to see its network, broadcast address, host range and binary form, and check the answers you work out by hand.

How many hosts fit: 2^h − 2, and the /31 and /32 exceptions

A prefix of length n leaves h = 32 − n host bits, so its block holds 2^h addresses. An ordinary subnet gives two of them to the network and broadcast addresses, which leaves 2^h − 2 for hosts: 254 in a /24, 30 in a /27 and 2 in a /30.

Two prefix lengths break the rule:

  • /32 is a single address. It names one host, for example a route to one server or the loopback address of a router; the table in RFC 4632 calls a /32 a host route.
  • /31 has two addresses, no room for both a network and a broadcast address. RFC 3021 lets point-to-point links use it: on such a link both addresses are host addresses. A link then uses two addresses instead of the four of a /30, which halves the addresses spent on links between routers.
From /29 down to /32 in Python Python · small_prefixes.py
# The smallest prefixes: usable addresses, and what Python's ipaddress module reports for each.
import ipaddress

print(f"{'prefix':<14}{'size':>4}{'usable':>8}  broadcast_address")
for text in ["192.0.2.0/29", "192.0.2.0/30", "192.0.2.0/31", "192.0.2.7/32"]:
    net = ipaddress.ip_network(text)
    print(f"{text:<14}{net.num_addresses:>4}{len(list(net.hosts())):>8}  {net.broadcast_address}")
print("hosts() of the /31:", ", ".join(str(h) for h in ipaddress.ip_network("192.0.2.0/31").hosts()))

Output

prefix        size  usable  broadcast_address
192.0.2.0/29     8       6  192.0.2.7
192.0.2.0/30     4       2  192.0.2.3
192.0.2.0/31     2       2  192.0.2.1
192.0.2.7/32     1       1  192.0.2.7
hosts() of the /31: 192.0.2.0, 192.0.2.1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 small_prefixes.py

Python follows RFC 3021 in hosts(), which returns both addresses of a /31. Its broadcast_address attribute still reports the last address of the block for a /31 and a /32, though, so code that removes broadcast_address from a /31 throws away a working address. Check the prefix length before you treat the last address as a broadcast address. In exam questions, count 2^h − 2 hosts unless the question is about a /31 point-to-point link.

From address classes to CIDR

The first IPv4 specification fixed the size of the network part by the first bits of the address. RFC 791 defined classes A, B and C, and RFC 1112 later used the next range for multicast:

  • Class A: first bit 0, first octet 0 to 127, an 8-bit network part (/8); unicast.
  • Class B: first bits 10, first octet 128 to 191, a 16-bit network part (/16); unicast.
  • Class C: first bits 110, first octet 192 to 223, a 24-bit network part (/24); unicast.
  • Class D: first bits 1110, first octet 224 to 239; multicast groups, with no network part.
  • Class E: first bits 1111, first octet 240 to 255; reserved.

An organisation received a whole class A, B or C network and nothing in between. RFC 4632 describes what went wrong. A class C network (254 hosts) was too small for most organisations and a class B network (65,534 hosts) far too large, so class B numbers were running out by the early 1990s. At the same time the routing tables of the Internet’s core routers were growing faster than the hardware and software could keep up with. Classless Inter-Domain Routing dropped the classes. A block can be any power-of-two size, its prefix length is written down explicitly, and routing protocols carry the prefix length with every route. Neighbouring blocks can then be announced as one shorter prefix, which keeps routing tables small; a later lesson of this module, on VLSM and route summarisation, does exactly that.

CIDR slowed the use of addresses, but it could not make more of them. IANA’s free pool of IPv4 addresses ran out in 2011, when its last five /8 blocks went one to each of the five regional Internet registries. Sharing one address among many devices with NAT, and moving to IPv6, are the two answers, and both have their own lessons in this track.

Classes still appear in exam questions, in old documentation and in tool output, so it pays to recognise them by their first bits. They decide nothing in a modern network: 10.20.30.40 is a class A address, but its prefix in this lesson is /20.

GATE CS

The GATE 2027 syllabus for Computer Science lists “IPv4 - Fragmentation, CIDR Notation, Network Address Translation” in its computer networks section. This module covers CIDR notation and network address translation; fragmentation has its own lesson in the next module.

Addresses with special meanings

Some blocks are set aside by standards and are never used as ordinary public addresses. IANA keeps them in the IPv4 Special-Purpose Address Registry, whose rules are set out in RFC 6890. For each block, the registry records whether it may appear as a source or a destination, whether routers may forward it and whether it is globally reachable. None of these blocks is globally reachable:

  • 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16: private use inside one organisation (RFC 1918).
  • 100.64.0.0/10: shared space between a customer’s router and an ISP’s carrier-grade NAT (RFC 6598).
  • 127.0.0.0/8: loopback, the host itself; these addresses must never appear outside the host (RFC 1122).
  • 169.254.0.0/16: link-local, the addresses a host gives itself when nothing else configures one; routers do not forward them (RFC 3927).
  • 192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24: documentation and examples (RFC 5737).
  • 198.18.0.0/15: benchmarking network equipment (RFC 2544).
  • 0.0.0.0/8: “this network”; the single address 0.0.0.0 is the source of a host that has no address yet (RFC 791, RFC 1122).
  • 240.0.0.0/4: reserved (RFC 1112).
  • 255.255.255.255/32: the limited broadcast address, every host on the local network (RFC 919, RFC 8190).

The multicast range 224.0.0.0/4, class D above, is not a special-purpose block: its addresses name groups of receivers rather than single hosts (RFC 1112). Anyone may use the private-use blocks without asking a registry, so countless home and office networks use the same addresses at the same time; such addresses work only inside their own network and reach the Internet through NAT.

Which block is an address in? Python · special_ranges.py
# Which special-purpose block an address is in, from a table of the IANA IPv4 Special-Purpose
# Address Registry (RFC 6890) and the multicast range of RFC 1112. When two blocks contain an
# address, the longer prefix (the more specific block) wins.
import ipaddress

SPECIAL = [
    ("0.0.0.0/8", "this network"),
    ("10.0.0.0/8", "private-use (RFC 1918)"),
    ("100.64.0.0/10", "shared CGN space (RFC 6598)"),
    ("127.0.0.0/8", "loopback"),
    ("169.254.0.0/16", "link-local (RFC 3927)"),
    ("172.16.0.0/12", "private-use (RFC 1918)"),
    ("192.0.2.0/24", "documentation (RFC 5737)"),
    ("192.168.0.0/16", "private-use (RFC 1918)"),
    ("198.18.0.0/15", "benchmarking (RFC 2544)"),
    ("198.51.100.0/24", "documentation (RFC 5737)"),
    ("203.0.113.0/24", "documentation (RFC 5737)"),
    ("224.0.0.0/4", "multicast (RFC 1112)"),
    ("240.0.0.0/4", "reserved (RFC 1112)"),
    ("255.255.255.255/32", "limited broadcast"),
]
TABLE = [(ipaddress.ip_network(block), name) for block, name in SPECIAL]


def kind(text):
    address = ipaddress.ip_address(text)
    matches = [(net.prefixlen, name) for net, name in TABLE if address in net]
    return max(matches)[1] if matches else "public unicast"


for text in ["10.1.2.3", "172.31.255.254", "172.32.0.1", "192.168.1.1", "100.72.160.1", "100.128.0.1",
             "127.0.0.53", "169.254.10.20", "198.51.100.7", "224.0.0.251", "255.255.255.255", "0.0.0.0"]:
    print(f"{text:<16} {kind(text)}")

Output

10.1.2.3         private-use (RFC 1918)
172.31.255.254   private-use (RFC 1918)
172.32.0.1       public unicast
192.168.1.1      private-use (RFC 1918)
100.72.160.1     shared CGN space (RFC 6598)
100.128.0.1      public unicast
127.0.0.53       loopback
169.254.10.20    link-local (RFC 3927)
198.51.100.7     documentation (RFC 5737)
224.0.0.251      multicast (RFC 1112)
255.255.255.255  limited broadcast
0.0.0.0          this network

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 special_ranges.py

Watch the edges. 172.31.255.254 is private but 172.32.0.1 is not, because 172.16.0.0/12 ends at 172.31.255.255. In the same way 100.127.255.255 is the last shared address, so 100.128.0.1 is ordinary public space. The program picks the longest matching prefix, so it reports 255.255.255.255 as the limited broadcast address although that address also lies inside the reserved block 240.0.0.0/4. Routers choose between overlapping routes the same way.

The ipaddress module has flags for these blocks, but their names can mislead. Its documentation defines is_private as “not globally reachable” in the IANA registries, which includes the documentation blocks, and the shared block 100.64.0.0/10 is neither private nor global:

is_private is wider than RFC 1918 Python · python_flags.py
# is_private in Python's ipaddress module means "not globally reachable" in the IANA registry,
# which is wider than the three private-use blocks of RFC 1918.
import ipaddress

RFC1918 = [ipaddress.ip_network(n) for n in ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")]

print(f"{'address':<14} {'is_private':<11} {'is_global':<10} in RFC 1918")
for text in ["192.168.1.1", "198.51.100.7", "100.72.160.1", "100.128.0.1"]:
    a = ipaddress.ip_address(text)
    print(f"{text:<14} {a.is_private!s:<11} {a.is_global!s:<10} {any(a in n for n in RFC1918)}")

Output

address        is_private  is_global  in RFC 1918
192.168.1.1    True        False      True
198.51.100.7   True        False      False
100.72.160.1   False       False      False
100.128.0.1    False       True       False

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 python_flags.py

To test for the three RFC 1918 blocks, compare with the blocks themselves, as the last column does.

Documentation addresses in examples

RFC 5737 sets aside three blocks for documentation: 192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24. Examples, slides and test data that use them can never point at a real server, which is why this track uses them wherever it needs a public address.

Interview questions

What is the difference between a subnet mask and a prefix length?

They carry the same information in two notations. A prefix length /n says that the first n bits of the address are the network part. A subnet mask writes that boundary as a 32-bit number with n one-bits followed by zeros, in dotted decimal: /20 is 255.255.240.0, because twenty ones fill two octets (255.255) and four more make 11110000, which is 240. To go back, count the one-bits: 8 + 8 + 4 = 20. The ones must be contiguous, so 255.0.255.0 is not a valid mask. Which form you type depends on the software; CIDR notation uses the prefix length because it is shorter.

Key takeaways

  • An IPv4 address is a 32-bit number; dotted decimal, binary, hexadecimal and the integer are four ways to write it.
  • /n puts the first n bits in the network part, and the mask is n one-bits: /20 is 255.255.240.0.
  • Network address = address AND mask; broadcast address = network address OR wildcard mask; hosts use the addresses in between.
  • A /n block has 2^(32 − n) addresses and 2^(32 − n) − 2 usable hosts, except a /31 link (2 hosts, RFC 3021) and a /32 (1 host).
  • Address classes are history: CIDR writes the prefix length explicitly so that blocks of any power-of-two size can be assigned and aggregated.
  • Know the special blocks: the RFC 1918 private blocks, 100.64.0.0/10 shared space, 127.0.0.0/8 loopback, 169.254.0.0/16 link-local, the three documentation /24 blocks and 224.0.0.0/4 multicast.
  • Parse untrusted addresses with one strict parser: lenient parsers read 0x7f.1 and 010.1.2.3 in ways people do not expect.

Exercise

Exercise · Easy · Python

Describe the subnet an address belongs to

Write subnet_info(address, prefix), which describes the subnet that an IPv4 address belongs to. address is a dotted-decimal string such as "10.20.30.40" and prefix is the prefix length, a whole number from 0 to 32.

Return a dictionary with these five keys:

- "network": the network address, as a dotted-decimal string (the host bits all 0). - "broadcast": the broadcast address (the host bits all 1), or None for a /31 or a /32, which have no broadcast address. - "first" and "last": the first and last usable host addresses. - "usable": how many usable host addresses the subnet has, as an int.

Follow the rules of the lesson: an ordinary subnet loses its network and broadcast addresses, a /31 point-to-point link uses both of its addresses (RFC 3021), and a /32 is one host. For subnet_info("10.20.30.40", 20) the answer is {"network": "10.20.16.0", "broadcast": "10.20.31.255", "first": "10.20.16.1", "last": "10.20.31.254", "usable": 4094}.

You may do the bit arithmetic yourself or use the ipaddress module, but watch what it reports for a /31. The sample tests import your function from subnet_info.py.

Starter code · subnet_info.py

def subnet_info(address, prefix):
    """Describe the subnet of address/prefix: network, broadcast, first, last and usable."""
    # Replace this line with your code.
    return {}
The sample tests · test_subnet_info.py
from subnet_info import subnet_info


def test_inside_a_slash_20():
    """finds the subnet of an address inside a /20"""
    assert subnet_info("10.20.30.40", 20) == {
        "network": "10.20.16.0", "broadcast": "10.20.31.255",
        "first": "10.20.16.1", "last": "10.20.31.254", "usable": 4094,
    }


def test_slash_24():
    """works when the boundary falls between octets"""
    assert subnet_info("192.168.1.77", 24) == {
        "network": "192.168.1.0", "broadcast": "192.168.1.255",
        "first": "192.168.1.1", "last": "192.168.1.254", "usable": 254,
    }


def test_network_and_broadcast_given():
    """accepts the network or the broadcast address itself"""
    expected = {
        "network": "192.168.10.64", "broadcast": "192.168.10.95",
        "first": "192.168.10.65", "last": "192.168.10.94", "usable": 30,
    }
    assert subnet_info("192.168.10.64", 27) == expected
    assert subnet_info("192.168.10.95", 27) == expected


def test_slash_30():
    """leaves two usable hosts in a /30"""
    assert subnet_info("10.0.0.5", 30) == {
        "network": "10.0.0.4", "broadcast": "10.0.0.7",
        "first": "10.0.0.5", "last": "10.0.0.6", "usable": 2,
    }


def test_slash_31():
    """follows RFC 3021 on a /31: no broadcast address, both addresses usable"""
    assert subnet_info("192.0.2.1", 31) == {
        "network": "192.0.2.0", "broadcast": None,
        "first": "192.0.2.0", "last": "192.0.2.1", "usable": 2,
    }


def test_slash_32():
    """treats a /32 as a single host"""
    assert subnet_info("198.51.100.9", 32) == {
        "network": "198.51.100.9", "broadcast": None,
        "first": "198.51.100.9", "last": "198.51.100.9", "usable": 1,
    }


def test_short_prefixes():
    """works for /8 and for the whole address space"""
    assert subnet_info("10.200.1.2", 8) == {
        "network": "10.0.0.0", "broadcast": "10.255.255.255",
        "first": "10.0.0.1", "last": "10.255.255.254", "usable": 16777214,
    }
    assert subnet_info("203.0.113.9", 0)["usable"] == 4294967294
    assert subnet_info("203.0.113.9", 0)["broadcast"] == "255.255.255.255"


def test_boundary_inside_the_second_octet():
    """handles a boundary inside the second octet"""
    assert subnet_info("172.20.130.5", 14) == {
        "network": "172.20.0.0", "broadcast": "172.23.255.255",
        "first": "172.20.0.1", "last": "172.23.255.254", "usable": 262142,
    }
A hint

Turn the address into one 32-bit integer first (int(ipaddress.ip_address(address)), or shift the four octets together). The mask is prefix one-bits followed by zeros: (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF. Then the network is value & mask and the last address of the block is network | (~mask & 0xFFFFFFFF). Handle prefix 31 and 32 before the general case.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 How many usable host addresses does an ordinary /27 subnet have?

    Type a number.

    Show the answer to question 1

    Answer: 30 hosts

    A /27 leaves 32 − 27 = 5 host bits, so the block has 2⁵ = 32 addresses. The network address and the broadcast address are taken, which leaves 32 − 2 = 30 for hosts.

  2. Question 2 of 6 What is the network address of 172.16.77.200/19?

    Choose one answer.

    Show the answer to question 2

    Answer: 172.16.64.0

    A /19 puts the boundary in the third octet after 3 bits, so the mask is 255.255.224.0. 77 is 01001101 and 224 is 11100000; AND keeps 01000000, which is 64. The fourth octet is all host bits and becomes 0.

  3. Question 3 of 6 Which of these addresses are in the private-use blocks of RFC 1918?

    Choose every answer that is right.

    Show the answer to question 3

    Answer:

    • 10.255.0.1
    • 192.168.255.1
    • 172.20.1.1

    The private-use blocks are 10.0.0.0/8, 172.16.0.0/12 (172.16.0.0 to 172.31.255.255) and 192.168.0.0/16. 172.32.1.1 is just past the /12, 100.64.1.1 is shared space for carrier-grade NAT (RFC 6598) and 169.254.1.1 is link-local (RFC 3927).

  4. Question 4 of 6 This code runs on Python 3.14. What does it print, and is the second address a broadcast address on a point-to-point link?

    Read the code, then choose one answer.

    import ipaddress
    link = ipaddress.ip_network("192.0.2.0/31")
    print(len(list(link.hosts())), link.broadcast_address)
    Show the answer to question 4

    Answer: 2 192.0.2.1, but on a /31 link 192.0.2.1 is a host address

    hosts() follows RFC 3021 and returns both addresses of a /31, but broadcast_address still reports the last address of the block. On a /31 point-to-point link both addresses are hosts, so do not exclude broadcast_address there.

  5. Question 5 of 6 Which problem did classless addressing (CIDR) set out to fix?

    Choose one answer.

    Show the answer to question 5

    Answer: Address classes gave only three network sizes, so class B space ran out and routing tables grew too fast

    With classes, a network got 254, 65,534 or 16,777,214 host addresses and nothing in between. CIDR lets a block be any power-of-two size and carries the prefix length in routing, so neighbouring blocks can be advertised as one route. It did not make addresses longer: that is what IPv6 does.

  6. Question 6 of 6 A lenient parser such as inet_aton() reads the text 010.1.2.3. Which address does it produce?

    Choose one answer.

    Show the answer to question 6

    Answer: 8.1.2.3

    A leading 0 marks an octal number in the old C parsing rules, and octal 10 is 8. Python's ipaddress module rejects the text instead, which is why one strict parser should check every untrusted address.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.