Introduction
NetAddr arose through my need as a network engineer for a back-end module that could easily handle such advanced tasks as automating the subnetting/supernetting of IP space, performing calculations on IP CIDR blocks, and other various items. At that time there were no modules that could do any of the things that I needed, so I set out to create my own. Since it has proven to be fairly useful to me, I have decided to share the code with the Ruby community.
I have added things that I find immediately useful for me. I am open to suggestions if there is something that I could add to make your life easier.
Dustin Spinhirne
Example Script
#!/usr/bin/ruby
# A script to parse static routes from a Cisco router.
# Performs the following tasks:
# - organizes statics by directly connected next-hop interface
# - corrects recursive static routes
# - reports statics with no directly connected next-hop
# - reports duplicate static routes
# - reports static routes that overlap directly connected interfaces
#
# Change the variable 'infile' to that of the name of the router configuration
# file that should be parsed.
require 'rubygems'
require_gem 'netaddr'
# change this variable to that of the router config file
infile = 'router_config.txt'
ip_interface_count = 0
static_route_count = 0
connected_routes = []
static_routes = []
statics = {}
grouped_routes = {}
duplicates = []
recursives = []
corrected_statics = []
overlapping_statics = []
# sort through router config. put connected ip interfaces into 'connected_routes'
# and static routes into 'statics'. Set aside duplicate static routes.
File.open(infile, 'r') do |file|
while line = file.gets
if (line =~/^interface .+/)
interface = line.chomp
elsif (line =~ /^\s*ip address/) # connected interface
ip_interface_count += 1
addr = line.split('ip address ').join # ['x.x.x.x y.y.y.y']
cidr = NetAddr::CIDR.create(addr, :Tag => {:interface => interface})
connected_routes.push(cidr)
elsif (line =~/^ip route/) # static route
static_route_count += 1
elements = line.split(' ') # ['ip','route','x.x.x.x','y.y.y.y','x.x.x.x',]
ip = elements[2]
netmask = elements[3]
nexthop = elements[4]
# keep all but loopbacks, nulls, or defaults
if ( (nexthop !~ /Loopback/) && (nexthop !~ /Null/) && (ip != '0.0.0.0') )
nexthop_cidr = NetAddr::CIDR.create(nexthop)
cidr = NetAddr::CIDR.create("#{ip} #{netmask}",
:Tag => {:route => line, :nexthop => nexthop_cidr})
if (!statics.has_key?(cidr.desc))
statics[cidr.desc] = cidr
else
msg = '! overlaps with - ' + statics[cidr.desc].tag[:route] + line + "!\n"
duplicates.push(msg)
end
end
end
end
end
# look for statics that overlap with a connected interface, and
# group static routes with their next-hop interface.
statics.each_key do |desc|
cidr = statics[desc]
nexthop = cidr.tag[:nexthop]
route = cidr.tag[:route]
overlaps_with = nil
nexthop_int = nil
connected_routes.each do |interface|
if (interface.contains?(cidr)) # overlapping static
overlaps_with = interface
break
elsif (interface.contains?(nexthop)) # next-hop directly connected
nexthop_int = interface
break
end
end
if (nexthop_int)
key = "#{nexthop_int.tag[:interface]} -> #{nexthop_int.desc}"
if (grouped_routes.has_key?(key))
grouped_routes[key].push(route)
else
grouped_routes[key] = [route]
end
static_routes.push(cidr)
elsif (overlaps_with)
overlap = "! overlaps with: #{overlaps_with.tag[:interface]} -> #{overlaps_with.desc}"
overlap << "\n#{route}!\n"
overlapping_statics.push(overlap)
else
recursives.push(cidr)
end
end
# process recursive routes. update next-hop so that it points
# to next hop of a directly connected ip. remove any that do not point to a
# directly connected ip. We must continually cycle through the list, as the
# 'static_routes' tree is constantly updated. We do this until our list of
# recursive static routes stops getting any shorter
recursives_count = 0
until (recursives_count == recursives.length)
recursives_count = recursives.length
recursives.each do |cidr|
nexthop = cidr.tag[:nexthop]
route = cidr.tag[:route]
found = nil
static_routes.each do |static|
if (static.contains?(nexthop))
found = static
break
end
end
if (found)
updated = 'no ' + route
updated << "ip route #{cidr.network} #{cidr.netmask_ext} #{found.tag[:nexthop].ip}\n"
updated << "!\n"
corrected_statics.push(updated)
static_routes.push(cidr)
recursives.delete(cidr)
end
end
end
# print results.
puts "--- STATISTICS ---"
puts "#{ip_interface_count} Connected IP Interfaces\n#{static_route_count} IP Static Routes\n"
print "\n\n"
puts "--- OVERLAPPING STATIC ROUTES ---"
overlapping_statics.each do |overlap|
puts overlap
end
print "\n\n"
puts "--- DUPLICATE STATIC ROUTES ---"
duplicates.each do |route|
puts route
end
print "\n\n"
puts "--- STATIC ROUTES WITH UPDATED NEXT-HOP ---"
corrected_statics.each do |route|
puts route
end
print "\n\n"
puts "--- STATIC ROUTES WITH UNKNOWN NEXT-HOP ---"
recursives.each do |cidr|
puts cidr.tag[:route]
end
print "\n\n"
puts "--- STATIC ROUTES GROUPED BY NEXT-HOP INTERFACE ---"
grouped_routes.each_key do |interface|
routes = grouped_routes[interface]
puts interface
routes.each do |route|
puts route
end
print "\n"
end