class NetAddr::Util

Contains various internal util functions

Private Class Methods

backfill(ipnet,limit) click to toggle source

backfill generates subnets between given IPv4Net/IPv6Net and the limit address. limit should be < ipnet. will create subnets up to and including limit.

# File lib/util.rb, line 9
def Util.backfill(ipnet,limit)
        nets = []
        cur = ipnet
        while true do
                net = cur.prev
                if (net == nil || net.network.addr < limit)
                        break
                end
                nets.unshift(net)
                cur = net
        end
        return nets
end
discard_subnets(list) click to toggle source

discard_subnets returns a copy of the IPv4NetList with any entries which are subnets of other entries removed.

# File lib/util.rb, line 25
def Util.discard_subnets(list)
        keepers = []
        last = list[list.length-1]
        keep_last = true
        list.each do |net|
                rel = last.rel(net)
                if (!rel) # keep unrelated nets
                        keepers.push(net)
                elsif (rel == -1) # keep supernets, but do not keep last
                        keepers.push(net)
                        keep_last = false
                end
        end
        
        # recursively clean up keepers
        if (keepers.length > 0)
                keepers = discard_subnets(keepers)
        end
        if keep_last
                keepers.unshift(last)
        end
        return keepers
end
fill(ipnet,list) click to toggle source

fill returns a copy of the given Array, stripped of any networks which are not subnets of ipnet and with any missing gaps filled in.

# File lib/util.rb, line 51
def Util.fill(ipnet,list)
        # sort & get rid of non subnets
        subs = []
        discard_subnets(list).each do |sub|
                r = ipnet.rel(sub)
                if (r == 1)
                        subs.push(sub)
                end
        end
        subs = quick_sort(subs)
        
        filled = []
        if (subs.length > 0)
                # bottom fill if base missing
                base = ipnet.network.addr
                if (subs[0].network.addr != base)
                        filled = backfill(subs[0],base)
                end
                
                # fill gaps between subnets
                0.upto(subs.length-1) do |i|
                        sub = subs[i]
                        if (i+1 < subs.length)
                                filled.concat( fwdfill(sub,ipnet,subs[i+1]) )
                        else
                                filled.concat( fwdfill(sub,ipnet,nil) )
                        end
                end
        end
        return filled
end
filter_IPv4(list) click to toggle source

filter_IPv4 returns a copy of list with only IPv4 objects

# File lib/util.rb, line 84
def Util.filter_IPv4(list)
        filtered = []
        list.each do |ip|
                if (ip.kind_of?(IPv4))
                        filtered.push(ip)
                end
        end
        return filtered
end
filter_IPv4Net(list) click to toggle source

filter_IPv4Net returns a copy of list with only IPv4Net objects

# File lib/util.rb, line 95
def Util.filter_IPv4Net(list)
        filtered = []
        list.each do |ip|
                if (ip.kind_of?(IPv4Net))
                        filtered.push(ip)
                end
        end
        return filtered
end
filter_IPv6(list) click to toggle source

filter_IPv6 returns a copy of list with only IPv6 objects

# File lib/util.rb, line 106
def Util.filter_IPv6(list)
        filtered = []
        list.each do |ip|
                if (ip.kind_of?(IPv6))
                        filtered.push(ip)
                end
        end
        return filtered
end
filter_IPv6Net(list) click to toggle source

filter_IPv6Net returns a copy of list with only IPv4Net objects

# File lib/util.rb, line 117
def Util.filter_IPv6Net(list)
        filtered = []
        list.each do |ip|
                if (ip.kind_of?(IPv6Net))
                        filtered.push(ip)
                end
        end
        return filtered
end
fwdfill(ipnet,supernet,limit) click to toggle source

fwdfill returns subnets between given IPv4Net/IPv6Nett and the limit address. limit should be > ipnet.

# File lib/util.rb, line 128
def Util.fwdfill(ipnet,supernet,limit)
        nets = [ipnet]
        cur = ipnet
        if (limit != nil) # if limit, then fill gaps between net and limit
                while true do
                        nextSub = cur.next()
                        # ensure we've not exceed the total address space
                        if (nextSub == nil)
                                break
                        end
                        # ensure we've not exceeded the address space of supernet
                        if (supernet.rel(nextSub) == nil)
                                break
                        end
                        # ensure we've not hit limit
                        if (nextSub.network.addr == limit.network.addr)
                                break
                        end
                        
                        # check relationship to limit
                        if (nextSub.rel(limit) != nil) # if related, then nextSub must be a supernet of limit. we need to shrink it.
                                prefixLen = nextSub.netmask.prefix_len
                                while true do
                                        prefixLen += 1
                                        if (nextSub.kind_of?(IPv4Net))
                                                nextSub = IPv4Net.new(nextSub.network, Mask32.new(prefixLen))
                                        else
                                                nextSub = IPv6Net.new(nextSub.network, Mask128.new(prefixLen))
                                        end
                                        if (nextSub.rel(limit) == nil) # stop when we no longer overlap with limit
                                                break
                                        end
                                end
                        else # otherwise, if unrelated then grow until we hit the limit
                                prefixLen = nextSub.netmask.prefix_len
                                mask = nextSub.netmask.mask
                                while true do
                                        prefixLen -= 1
                                        if (prefixLen == supernet.netmask.prefix_len) # break if we've hit the supernet boundary
                                                break
                                        end
                                        mask = mask << 1
                                        if (nextSub.network.addr|mask != mask) # break when bit boundary crossed (there are '1' bits in the host portion)
                                                break
                                        end
                                        if (nextSub.kind_of?(IPv4Net))
                                                grown = IPv4Net.new(nextSub.network, Mask32.new(prefixLen))
                                        else
                                                grown = IPv6Net.new(nextSub.network, Mask128.new(prefixLen))
                                        end
                                        if (grown.rel(limit) != nil) # if we've overlapped with limit in any way, then break
                                                break
                                        end
                                        nextSub = grown
                                end
                        end
                        nets.push(nextSub)
                        cur = nextSub
                end
        else # if no limit, then get next largest sibs until we've exceeded supernet
                while true do
                        nextSub = cur.next()
                        # ensure we've not exceed the total address space
                        if (nextSub == nil)
                                break
                        end
                        # ensure we've not exceeded the address space of supernet
                        if (supernet.rel(nextSub) == nil)
                                break
                        end
                        nets.push(nextSub)
                        cur = nextSub
                end
        end
        return nets
end
int_to_IPv4(i) click to toggle source

int_to_IPv4 converts an Integer into an IPv4 address String

# File lib/util.rb, line 206
def Util.int_to_IPv4(i)
        octets = []
        3.downto(0) do |x|
                octet = (i >> 8*x) & 0xFF 
                octets.push(octet.to_s)
        end
        return octets.join('.')
end
parse_IPv4(ip) click to toggle source

parse_IPv4 parses an IPv4 address String into an Integer

# File lib/util.rb, line 216
def Util.parse_IPv4(ip)
# check that only valid characters are present
        if (ip =~ /[^0-9\.]/)
                raise ValidationError, "#{ip} contains invalid characters."
        end
        
        octets = ip.strip.split('.')
        if (octets.length != 4)
                raise ValidationError, "IPv4 requires (4) octets."
        end

        ipInt = 0
        i = 4
        octets.each do |octet|
                octetI = octet.to_i()
                if ( (octetI < 0) || (octetI >= 256) )
                        raise ValidationError, "#{ip} is out of bounds for IPv4."
                end
                i -= 1 
                ipInt = ipInt | (octetI << 8*i)
        end
        return ipInt
end
parse_IPv6(ip) click to toggle source

parse_IPv6 parses an IPv6 address String into an Integer

# File lib/util.rb, line 241
def Util.parse_IPv6(ip)
# check that only valid characters are present
        if (ip =~ /[^0-9a-fA-F\:.]/)
                raise ValidationError, "#{ip} contains invalid characters."
        end
        
        ip = ip.strip
        if (ip == "::")
                return 0 # zero address
        end
        ipv4Int = nil
        if (ip.include?(".")) # check for ipv4 embedded addresses
                words = ip.split(":")
                begin
                        ipv4Int = Util.parse_IPv4(words.last)
                rescue
                        raise ValidationError, "IPv4-embedded IPv6 address is invalid."
                end
                ip = ip.sub(words.last,"0:0") # temporarily remove the ipv4 portion
        end
        words = []
        if (ip.include?("::")) # short format
                if (ip =~ /:{3,}/) # make sure only i dont have ":::"
                        raise ValidationError, "#{ip} contains invalid field separator."
                end
                if (ip.scan(/::/).length != 1)
                        raise ValidationError, "#{ip} contains multiple '::' sequences."
                end
                
                halves = ip.split("::")
                if (halves[0] == nil) # cases such as ::1
                        halves[0] = "0"
                end
                if (halves[1] == nil) # cases such as 1::
                        halves[1] = "0"
                end
                upHalf = halves[0].split(":")
                loHalf = halves[1].split(":")
                numWords = upHalf.length + loHalf.length
                if (numWords > 8)
                        raise ValidationError, "#{ip} is too long."
                end
                words = upHalf
                (8-numWords).downto(1) do |i|
                        words.push("0")
                end
                words.concat(loHalf)
        else
                words = ip.split(":")
                if (words.length > 8)
                   raise ValidationError, "#{ip} is too long."
                elsif (words.length < 8)
                        raise ValidationError, "#{ip} is too short."
                end
        end
        ipInt = 0
        i = 8
        words.each do |word|
                i -= 1
                word = word.to_i(16) << (16*i)
                ipInt = ipInt | word
        end
        if ipv4Int # re-add ipv4 portion if present
                ipInt = ipInt | ipv4Int
        end
        return ipInt
end
quick_sort(list) click to toggle source

quick_sort will return a sorted copy of the provided Array. The array must contain only objects which implement a cmp method and which are comparable to each other.

# File lib/util.rb, line 311
def Util.quick_sort(list)
        if (list.length <= 1)
                return [].concat(list)
        end
        
        final_list = []
        lt_list = []
        gt_list = []
        eq_list = []
        pivot = list[list.length-1]
        list.each do |ip|
                cmp = pivot.cmp(ip)
                if (cmp == 1)
                        lt_list.push(ip)
                elsif (cmp == -1)
                        gt_list.push(ip)
                else
                        eq_list.push(ip)
                end
        end
        final_list.concat( quick_sort(lt_list) )
        final_list.concat(eq_list)
        final_list.concat( quick_sort(gt_list) )
        return final_list
end
summ_peers(list) click to toggle source

summ_peers returns a copy of the list with any merge-able subnets summ’d together.

# File lib/util.rb, line 338
def Util.summ_peers(list)
        summd = quick_sort(list)
        while true do
                list_len = summd.length
                last = list_len - 1
                tmp_list = []
                i = 0
                while (i < list_len) do
                        net = summd[i]
                        next_net = i+1
                        if (i != last)
                                # if this net and next_net summarize then discard them & keep summary
                                new_net = net.summ(summd[next_net])
                                if (new_net) # can summ. keep summary
                                        tmp_list.push(new_net)
                                        i += 1 # skip next_net
                                else # cant summ. keep existing
                                        tmp_list.push(net)
                                end
                        else
                                tmp_list.push(net) # keep last
                        end
                        i += 1
                end
                
                # stop when list stops getting shorter
                if (tmp_list.length == list_len)
                        break
                end
                summd = tmp_list
        end
        return summd
end