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.