Update: Added MIT License.
IP sub-netting is one of the first things one learns about network administration.
You have a /22, you want /24′s, 2 bits give you 4 sub-nets. Or you want one /24, so you break the /22 into two /24′s and a /23. Not rocket science. It’s the kind of thing you can do in your head and keep track of in a spreadsheet or a text file if you don’t have too many networks.
When you’re planning an IP addressing scheme, you probably have the luxury of time, to think about it. But what if you need to do this in a crisis situation? Fortunately it has never happened to me, but I can think of scenarios where you need to do this very quickly:
Apparently, if you use BGP to advertise a network to the Internet and specific hosts within that network become the target of a DDoS attack, one way to mitigate the attack could be to stop advertising the /24 sub-nets being attacked. Although this means the attacker still succeeds in taking down his intended targets (because he made you take them down), at least you can remove the attack traffic from your link, and the rest of your network can remain available.
Another scenario might be someone hijacking part of your network by advertising a more specific route than you are (either intentionally, or due to a BGP filtering misconfiguration). This happened to youtube last year.
Either way, you’ll need to split the network into at least two (in the case of a /23) sub-nets to get a /24, which is generally the longest prefix accepted on the Internet. In the former case, you want to withdraw the more specific network, and in the latter you want to advertise it.
To this end, I have written a script that accepts a list of networks (in CIDR format, one per line) from STDIN, and the desired sub-net as the first command line argument. It loops through the input subnets, looking for the one that overlaps with the desired prefix. If a network doesn’t match, the script just prints it out untouched, if it does, then the script will de-aggregate that network into the minimum number of sub-nets in order to get the desired sub-net as one of the outputs. It prints the surrounding pieces, and unless you specify “-exclude” as the second argument, the desired sub-net itself is also added to the output:
#!/usr/bin/python # deaggregate.py - deaggregate a network for a specific subnet, yielding the minimum number of subnets # Add -exclude to only output the surrounding subnets. Subnet is read from stdin, all non matching subnets # are output untouched. Use as a filter, rinse, repeat. # Simeon Miteff # #Copyright (c) 2008 Simeon Miteff # #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to #deal in the Software without restriction, including without limitation the #rights to use, copy, modify, merge, publish, distribute, sublicense, and/or #sell copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: # #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL #THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS #IN THE SOFTWARE. from IPy import IP import sys def split(ip,sub,exclude): if ip==sub: if not exclude: print ip else: a = IP(str(ip.net())+'/'+str(ip.prefixlen()+1)) b = IP(str(IP(ip.net().int()|2**(32-(ip.prefixlen()+1))))+'/'+str(ip.prefixlen()+1)) if a.overlaps(sub): print b split(a,sub,exclude) elif b.overlaps(sub): print a split(b,sub,exclude) if __name__=="__main__": if len(sys.argv)<2: sys.stderr.write("Usage: %s CIDR_prefix [-exclude]\n" % sys.argv[0]) sys.exit(1) sub = IP(sys.argv[1]) exclude = False if len(sys.argv)==3: if sys.argv[2]=='-exclude': exclude = True for line in sys.stdin: pre = IP(line.strip()) if not pre.overlaps(sub): print line.strip() else: split(pre,sub,exclude)
An example run:
simeon@capybara:~/personal$ cat routes.txt 10.0.0.0/10 192.168.0.0/16 simeon@capybara:~/personal$ python deaggregate.py 10.0.100.0/24 < routes.txt 10.32.0.0/11 10.16.0.0/12 10.8.0.0/13 10.4.0.0/14 10.2.0.0/15 10.1.0.0/16 10.0.128.0/17 10.0.0.0/18 10.0.64.0/19 10.0.112.0/20 10.0.104.0/21 10.0.96.0/22 10.0.102.0/23 10.0.101.0/24 10.0.100.0/24 192.168.0.0/16 simeon@capybara:~/personal$
It requires the IPy module (apt-get install python-ipy). To handle multiple desired sub-nets, run the script again for each subsequent sub-net, using the output of the previous run as the input. Note that your (and your upstream’s) filters might need to be updated (unless your original prefix is allowed with something like "le 24").
I’ve added this to the code page. Thanks for listening :-)

Post a Comment