What you're looking at are not subnet masks. They are indications
of the length of the routing table¹ prefixes.
A naïve implementation of a routing table would list every possible IP
address so that, given any IP address, you'd look up that exact one
and get back the routing information² associated with it.
Clearly some sort of compression is needed. The nature of routing
information is that adjacent addresses are likely to use the same
information, so we can use a form of radix tree to compress these
together. Here, briefly, is how it works.
Given the numbers 0-7, we can represent them in binary as so:
0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111
Now if we have two routing table entries, one for addresses 0 and 1,
and another for addreses 2 and 3, we can store them under the binary
prefixes that these share. If we use a .
to indicate the "unused"
bit after the end of the prefix, we have 00.
for the range 0-1 and
01.
for the range 2-3.
A standard way of representing this is with the lowest number from the
range followed by the length of the prefix; in this case these would
be 0/2
for the range 0-1 and 2/2
for the range 2-3.
But what happens if we want to look up the routing information for
address 6? Normally we'd add a "default" set of routing information
with prefix 0/0
, i.e., matching any bits at all and then when we
search we look for the most specific information i.e, the longest
matching prefix, we can find. So the full routing table we've just
described is:
0/2 00. Matches addresses 1 and 2.
2/2 01. Matches addresses 3 and 4.
0/0 ... Matches any address.
Subnet masks can be described with prefixes in the same way, and so
this scheme is often used for that. But keep in mind that just because
this scheme can be used for describing subnets does not mean that
it's used only for describing subnets.
As an example of routing table prefixes not being subnets, you could
have two network interfaces connected to the same network, say,
192.168.2.0/24. (This could be implemented by connecting two separate
network cards to the same switch, each with its own cable.) You could
then set up the routing table to "balance" outgoing traffic across the
two interfaces by using two routing table entries:
192.168.2.0/25 eth0 # range ...2.0 to ...2.127
192.168.2.128/25 eth1 # range ...2.128 to ...2.255
This would send packets destined to addresses 0-127 on that network
out eth0
, but packets destined to addresses 128-255 on that network
out eth1
. This is a bad way of doing this (for reasons I won't get
into here), but demonstrates how routing prefixes and network
addresses might not match.
¹ The Wikipedia article on routing tables unfortunately says
that the prefix field holds the "Network ID." While this may be true
in certain specific implementations of routing tables, it's not
always a network ID in the general case, as seen in both the example
you provide and my example later in this answer.
² This routing information typically includes things like what
interface to use, what router to contact on that interface, if any,
the MAC address of a host for hosts directly reachable through that
interface, what source address we should put on the packet if the
host has multiple source addresses, security information, and so on.
There's a huge variety of data that could be there, but none of that
is important for the purposes of this discussion since we're talking
just about how you look up the correct data set for a given address,
not what's in the data set itself.