From 05fa0053d82ba5fe344903527ef5394ea32bdbc7 Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Mon, 3 Nov 2014 18:15:58 +0100 Subject: [PATCH] Feature #3175: Handle IP/SIZE nets --- .../remotes/security_groups/SecurityGroups.rb | 204 ++++++++++++++++-- 1 file changed, 190 insertions(+), 14 deletions(-) diff --git a/src/vnm_mad/remotes/security_groups/SecurityGroups.rb b/src/vnm_mad/remotes/security_groups/SecurityGroups.rb index eeb484496d..481529341a 100644 --- a/src/vnm_mad/remotes/security_groups/SecurityGroups.rb +++ b/src/vnm_mad/remotes/security_groups/SecurityGroups.rb @@ -14,6 +14,189 @@ # limitations under the License. # #--------------------------------------------------------------------------- # +################################################################################ +# IP and NETMASK Library +################################################################################ + +class IP + include Comparable + attr_accessor :ip + + def initialize(ip) + @ip = ip + end + + def to_s + @ip + end + + def to_i + @ip.split(".").inject(0) {|t,e| (t << 8) + e.to_i } + end + + def to_hex + "0x" + to_i.to_s(16).rjust(8, '0') + end + + def to_bin + "0b" + to_i.to_s(2).rjust(16, '0') + end + + def to_hex_groups(p="") + to_i.to_s(16).rjust(8, '0').scan(/.{2}/).collect{|e| p+e}.join('.') + end + + def to_bin_groups(p="") + to_i.to_s(2).rjust(16, '0').scan(/.{8}/).collect{|e| p+e}.join('.') + end + + def self.from_i(i) + ip = 3.downto(0).collect {|s| (i >> 8*s) & 0xff }.join('.') + self.new(ip) + end + + def &(another_ip) + IP.from_i(self.to_i & another_ip.to_i) + end + + def +(size) + IP.from_i(self.to_i + size) + end + + def -(e) + if e.instance_of? Fixnum + IP.from_i(self.to_i - e) + else + e.to_i - self.to_i + end + end + + def <=>(another_ip) + self.to_i <=> another_ip.to_i + end +end + +class Netmask < IP + def self.from_cidr(cidr) + self.from_i(0xffffffff ^ 2**(32-cidr)-1) + end + + def to_cidr + 32 - Math.log((to_i ^ 0xffffffff) + 1, 2).to_i + end +end + +class Net + attr_accessor :ip, :netmask + + def initialize(ip, netmask = nil) + if netmask + @ip, @netmask = ip, netmask + else + ip, netmask = ip.split('/') + @ip = IP.new(ip) + @netmask = Netmask.from_cidr(netmask.to_i) if netmask + end + + @network_address = network_address + @last_address = last_address + end + + def network_address + IP.from_i(@ip.to_i & @netmask.to_i) + end + + def last_address + IP.from_i(@ip.to_i | (@netmask.to_i ^ 0xffffffff)) + end + + def info + s = "" + s << @network_address.to_s.ljust(15) + s << " /" + s << @netmask.to_cidr.to_s.rjust(2) + s << " " + s << @network_address.to_s.ljust(15) + s << " " + s << last_address.to_s.ljust(15) + + s + end + + def to_s + "#{@network_address}/#{@netmask.to_cidr}" + end + + def next_net + next_ip = IP.from_i(last_address.to_i + 1) + Net.new(next_ip, @netmask) + end + + def between?(ip_start, ip_end) + network_address >= ip_start && last_address <= ip_end + end +end + +class Range + def initialize(ip_start, size) + @ip_start = IP.new(ip_start) + @ip_end = @ip_start + size + end + + def get_nets + self.class.get_nets(@ip_start, @ip_end) + end + + def largest_subnet + self.class.largest_subnet(@ip_start, @ip_end) + end + + def self.get_nets(ip_start, ip_end) + nets = [] + + net_m = largest_subnet(ip_start, ip_end) + + # left scraps + if ip_start < net_m.network_address + nets.concat get_nets(ip_start, net_m.network_address - 1) + end + + nets << net_m + + # right scraps + if net_m.last_address < ip_end + nets.concat get_nets(net_m.last_address + 1, ip_end) + end + + nets + end + + def self.largest_subnet(ip_start, ip_end) + size = ip_start - ip_end + + # start with the largest subnet + if size > 0 + cidr = 32 - Math.log(size, 2).floor + else + cidr = 32 + end + + fits = false + + while !fits + net = Net.new(ip_start, Netmask.from_cidr(cidr)) + net = net.next_net if ip_start > net.network_address + + cidr += 1 + break if cidr > 32 + + fits = net.between?(ip_start, ip_end) + end + + net + end +end + ################################################################################ # SecurityGroups and Rules ################################################################################ @@ -87,10 +270,11 @@ class Rule @rule[:range] || nil end - def net - nets = [@rule[:net]].flatten.compact - nets.empty? ? nil : nets + return nil if @rule[:ip].nil? || @rule[:size].nil? + + r = Range.new(@rule[:ip], @rule[:size].to_i) + r.get_nets.collect{|n| n.to_s} end # Helper @@ -115,7 +299,7 @@ class Rule end if net && !valid_net? - error_message << "Invalid net: #{net}" + error_message << "Invalid net: IP:'#{@rule[:ip]}' SIZE:'#{@rule[:size]}'" valid = false end @@ -123,16 +307,8 @@ class Rule end def valid_net? - valid = true - - net.each do |n| - if !n.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/) - valid = false - break - end - end - - valid + @rule[:ip].match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) && \ + @rule[:size].match(/^\d+$/) end end