Code Vigorous


navigation
home
github
mozillians
email
about
Dustin J. Mitchell

Pretty IPSets

27 Mar 2015

As part of my work to develop fwunit and tests for Mozilla’s firewall rules written with it, I’ve run into some horrendous sets of IP addresses:

IPSet([IP('10.130.52.0/22'), IP('10.130.56.0/22'), IP('10.130.64.0/22'),
    IP('10.130.68.0/24'), IP('10.130.75.84'), IP('10.130.156.0/22'),
    IP('10.132.48.15'), IP('10.132.48.16'), IP('10.132.48.19'),
    IP('10.132.49.15'), IP('10.132.49.19'), IP('10.132.49.165'),
    IP('10.132.52.0/22'), IP('10.132.56.0/22'), IP('10.132.64.0/22'),
    IP('10.132.68.0/24'), IP('10.132.75.28'), IP('10.132.100.234'),
    IP('10.132.156.0/22'), IP('10.134.48.16'), IP('10.134.48.19'),
    IP('10.134.48.118'), IP('10.134.48.121'), IP('10.134.49.19'),
    IP('10.134.49.64'), IP('10.134.52.0/22'), IP('10.134.56.0/22'),
    IP('10.134.64.0/22'), IP('10.134.68.0/24'), IP('10.134.75.31'),
    IP('10.134.84.28'), IP('10.134.84.87'), IP('10.134.84.94'),
    IP('10.134.84.121'), IP('10.134.84.122'), IP('10.134.100.191'),
    IP('10.134.156.0/22')])

This is way too long, and also contains a lot of visual “noise” in the form of punctuation. Staring at this sort of thing while trying to figure out why a particular test failed induces headaches!

So I developed prettyip, a utility for pretty-printing IPSets. This utility can format non-CIDR ranges of IPs:

>>> IPSet([IP('10.120.13.11'), IP('10.120.13.12/30'), IP('10.120.13.16')])
IPSet([IP('10.120.13.11'), IP('10.120.13.12/30'), IP('10.120.13.16')])
>>> prettyip.pretty_ipset(_)
'10.120.13.1{1-6}'

and even large CIDR blocks with “holes” missing:

>>> IPSet([IP('1.0.0.0/8')]) - IPSet([IP('1.0.1.0/24')]) - IPSet([IP('1.0.9.0/24')])
IPSet([IP('1.0.0.0/24'), IP('1.0.2.0/23'), IP('1.0.4.0/22'),
    IP('1.0.8.0/24'), IP('1.0.10.0/23'), IP('1.0.12.0/22'), IP('1.0.16.0/20'),
    IP('1.0.32.0/19'), IP('1.0.64.0/18'), IP('1.0.128.0/17'),
    IP('1.1.0.0/16'), IP('1.2.0.0/15'), IP('1.4.0.0/14'), IP('1.8.0.0/13'),
    IP('1.16.0.0/12'), IP('1.32.0.0/11'), IP('1.64.0.0/10'),
    IP('1.128.0.0/9')])
>>> prettyip.pretty_ipset(_)
'1.0.0.0/8 except 1.0.1.0/24, 1.0.9.0/24'

Implementation

The implementation of the tool is built for expansion. It iterates over representations using a variety of techniques, scoring each representation and selecting the best score. Some representations, such as the exception shown above, are recursive; in this case the representation for the “holes” is generated using the same function.