Thursday, July 23, 2009

netmask -- why doesn't this work?

So "Ulrich" was having trouble bringing networking up, and he showed me his network parameters:
ip address: 
default gateway:
Yow! That will never work, because the netmask won't let him get to the default gateway!

What does all this stuff mean, how could anyone tell, and more importantly what is the correct netmask to use?

Here's a little theory, then I'll give you the lazy man's way. The IP address is a sequence of 32 bits, represented usually as four 8-bit bytes rendered in decimal and separated by dots. When I want to send something to another "node", I compare its IP address with mine in conjunction with the netmask. If it matches, I just send it on my local LAN. If it doesn't match, I use the default gateway. (You can have other gateways, but for now we're just talking about the default gateway.)

Let's say that wants to talk to, with the netmask shown above. The comparison looks like this:
address bits
after mask?--------------------------------
So for↔, the addresses, masked, result in no difference. So we will just send on the local LAN.

But if wants to talk to, it looks like this:
address bits
after mask?------------XX-X---XXXXX--------
Even after masking, these don't match. So communicate with we have to use the default gateway. But wait, is that possible? The default gateway is, so repeating the conversion and comparison yields:
address bits
after mask?--------------------X-X---------
Even after masking, these still don't match. So we can't send to the default gateway just by sending to it on the local LAN. Instead we have to use a gateway--but waitaminute, this is the only gateway we've got!

What we must do is loosen up the netmask. If we change it to, say,, the result will instead look like this:
address bits
after mask?--------------------------------
Behold! After masking, the addresses match, so the IP routing software knows it can send to this gateway.

In other words, the way to figure out a netmask that will work is to do a bit-compare between the IP addresses of the interface and of the default gateway, and see how long a netmask you need in order to mask off the differences. The optimal netmask may be shorter ( or for example), but this procedure ought to give you one that at least lets you get to the default gateway.

The lazy man's answer

Just run this little script, lines composed while riding the train, umm, like this:
% ./
best case netmask is fffff000 or
% cat
#!/usr/bin/python -tt
# vim:et:sw=4
'''Given a list of IPv4 addresses, prints the longest netmask
that accommodates them all. Addresses can be in dotted quad
format (e.g., all numbers <=255) or hex (7-8 digits,
optionally preceded by '0x' -- case is ignored).

import re
import string
import sys

def main(argv):
# addresses can be supplied in hex format or dotted-quad
hexpat = re.compile('(?:0x)?([0-9a-f]{7,8})$', re.I)
dqpat = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)$')
FULL_MASK = 0xffffffffL # 32 bits
is_first = True
got_error = False # hope for the best
for an_addr in argv:
an_addr = an_addr.strip()

hexmatch = hexpat.match(an_addr) # Hex format?
if hexmatch:
# Yes! That was easy.
the_addr = string.atol(hexmatch.groups()[0], 16)
dqmatch = dqpat.match(an_addr)
if dqmatch: # Better be dotted-quad
the_addr = 0
for a_comp in dqmatch.groups():
if int(a_comp) > 255:
# That's an illegal number
print '*** In "' + an_addr + '":', a_comp, '> 255'
got_error = True
the_addr = (the_addr << 8) + int(a_comp)
print "*** couldn't" ' decode "' + an_addr + '"'
got_error = True
if got_error:
if is_first:
# Initialize...
to_match = the_addr
the_mask = FULL_MASK
is_first = False
# It's the (2nd or more) time through. to_match has the
# bits in common so far (possibly just the 1st address).
# the_mask holds the longest netmask that "works" so far.
# On entry: to_match & the_mask == to_match
# When loop is done: to_match & the_mask = the_addr & the_mask
while ((to_match & the_mask) != (the_addr & the_mask)):
the_mask = (the_mask << 1) & FULL_MASK
# Now re-establish entry condition
to_match = to_match & the_mask

# OK, now we've read all the addresses
if got_error:
# Don't process anything, just leave
print 'Not doing anything due to errors above'
if is_first:
# Nothing to do
print '??? No IP addresses supplied.'
# All good!
print 'best case netmask is', ('%08lx' % the_mask),
dqmask = `the_mask >> 24` + '.' + `(the_mask >> 16) & 0xff` + '.' + \
`(the_mask >> 8) & 0xff` + '.' + `the_mask & 0xff`
print 'or', dqmask.replace('L','')

if __name__ == '__main__':
That's it! You can snarf'n'barf the shaded part above and save it to a file on your Linux/UNIX™/Mac™ computer. You might need to adjust the first line (it works on Mac OS X 10.4 Tiger). Don't forget to set execute permissions on the script.


No comments: