diff --git a/lib/puppet/util/network_device/ipcalc.rb b/lib/puppet/util/network_device/ipcalc.rb new file mode 100644 index 000000000..2b4f360b7 --- /dev/null +++ b/lib/puppet/util/network_device/ipcalc.rb @@ -0,0 +1,68 @@ + +require 'puppet/util/network_device' +module Puppet::Util::NetworkDevice::IPCalc + + # This is a rip-off of authstore + Octet = '(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])' + IPv4 = "#{Octet}\.#{Octet}\.#{Octet}\.#{Octet}" + IPv6_full = "_:_:_:_:_:_:_:_|_:_:_:_:_:_::_?|_:_:_:_:_::((_:)?_)?|_:_:_:_::((_:){0,2}_)?|_:_:_::((_:){0,3}_)?|_:_::((_:){0,4}_)?|_::((_:){0,5}_)?|::((_:){0,6}_)?" + IPv6_partial = "_:_:_:_:_:_:|_:_:_:_::(_:)?|_:_::(_:){0,2}|_::(_:){0,3}" + IP = "#{IPv4}|#{IPv6_full}".gsub(/_/,'([0-9a-fA-F]{1,4})').gsub(/\(/,'(?:') + + def parse(value) + case value + when /^(#{IP})\/(\d+)$/ # 12.34.56.78/24, a001:b002::efff/120, c444:1000:2000::9:192.168.0.1/112 + [$2.to_i,IPAddr.new($1)] + when /^(#{IP})$/ # 10.20.30.40, + value = IPAddr.new(value) + [bits(value.family),value] + end + end + + def bits(family) + family == Socket::AF_INET6 ? 128 : 32 + end + + def fullmask(family) + (1 << bits(family)) - 1 + end + + def mask(family, length) + (1 << (bits(family) - length)) - 1 + end + + # returns ip address netmask from prefix length + def netmask(family, length) + IPAddr.new(fullmask(family) & ~mask(family, length) , family) + end + + # returns an IOS wildmask + def wildmask(family, length) + IPAddr.new(mask(family, length) , family) + end + + # returns ip address prefix length from netmask + def prefix_length(netmask) + mask_addr = netmask.to_i + return 0 if mask_addr == 0 + length=32 + if (netmask.ipv6?) + length=128 + end + + mask = mask_addr < 2**length ? length : 128 + + mask.times do + if ((mask_addr & 1) == 1) + break + end + mask_addr = mask_addr >> 1 + mask = mask - 1 + end + mask + end + + def linklocal?(ip) + end + +end \ No newline at end of file diff --git a/spec/unit/util/network_device/ipcalc_spec.rb b/spec/unit/util/network_device/ipcalc_spec.rb new file mode 100644 index 000000000..6f55a66e4 --- /dev/null +++ b/spec/unit/util/network_device/ipcalc_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/network_device/ipcalc' + +describe Puppet::Util::NetworkDevice::IPCalc do + class TestIPCalc + include Puppet::Util::NetworkDevice::IPCalc + end + + before(:each) do + @ipcalc = TestIPCalc.new + end + + describe "when parsing ip/prefix" do + it "should parse ipv4 without prefixes" do + @ipcalc.parse('127.0.0.1').should == [32,IPAddr.new('127.0.0.1')] + end + + it "should parse ipv4 with prefixes" do + @ipcalc.parse('127.0.1.2/8').should == [8,IPAddr.new('127.0.1.2')] + end + + it "should parse ipv6 without prefixes" do + @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0').should == [128,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] + end + + it "should parse ipv6 with prefixes" do + @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0/56').should == [56,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] + end + end + + describe "when building netmask" do + it "should produce the correct ipv4 netmask from prefix length" do + @ipcalc.netmask(Socket::AF_INET, 27).should == IPAddr.new('255.255.255.224') + end + + it "should produce the correct ipv6 netmask from prefix length" do + @ipcalc.netmask(Socket::AF_INET6, 56).should == IPAddr.new('ffff:ffff:ffff:ff00::0') + end + end + + describe "when building wildmask" do + it "should produce the correct ipv4 wildmask from prefix length" do + @ipcalc.wildmask(Socket::AF_INET, 27).should == IPAddr.new('0.0.0.31') + end + + it "should produce the correct ipv6 wildmask from prefix length" do + @ipcalc.wildmask(Socket::AF_INET6, 126).should == IPAddr.new('::3') + end + end + + describe "when computing prefix length from netmask" do + it "should produce the correct ipv4 prefix length" do + @ipcalc.prefix_length(IPAddr.new('255.255.255.224')).should == 27 + end + + it "should produce the correct ipv6 prefix length" do + @ipcalc.prefix_length(IPAddr.new('fffe::0')).should == 15 + end + end +end \ No newline at end of file