Module: IPAddrExtensions
- Defined in:
- lib/ipaddr_extensions.rb
Defined Under Namespace
Modules: ClassMethods
Constant Summary collapse
- MSCOPES =
{ 1 => "INTERFACE LOCAL MULTICAST", 2 => "LINK LOCAL MULTICAST", 4 => "ADMIN LOCAL MULTICAST", 5 => "SITE LOCAL MULTICAST", 8 => "ORGANISATION LOCAL MULTICAST", 0xe => "GLOBAL MULTICAST" }
- MDESTS =
{ 1 => "ALL NODES", 2 => "ALL ROUTERS", 3 => "ALL DHCP SERVERS", 4 => "DVMRP ROUTERS", 5 => "OSPFIGP", 6 => "OSPFIGP DESIGNATED ROUTERS", 7 => "ST ROUTERS", 8 => "ST HOSTS", 9 => "RIP ROUTERS", 0xa => "EIGRP ROUTERS", 0xb => "MOBILE-AGENTS", 0xc => "SSDP", 0xd => "ALL PIM ROUTERS", 0xe => "RSVP ENCAPSULATION", 0xf => "UPNP", 0x16 => "ALL MLDV2 CAPABLE ROUTERS", 0x6a => "ALL SNOOPERS", 0x6b => "PTP-PDELAY", 0x6c => "SARATOGA", 0x6d => "LL MANET ROUTERS", 0xfb => "MDNSV6", 0x100 => "VMTP MANAGERS GROUP", 0x101 => "NTP", 0x102 => "SGI-DOGFIGHT", 0x103 => "RWHOD", 0x104 => "VNP", 0x105 => "ARTIFICIAL HORIZONS", 0x106 => "NSS", 0x107 => "AUDIONEWS", 0x108 => "SUN NIS+", 0x109 => "MTP", 0x10a => "IETF-1-LOW-AUDIO", 0x10b => "IETF-1-AUDIO", 0x10c => "IETF-1-VIDEO", 0x10d => "IETF-2-LOW-AUDIO", 0x10e => "IETF-2-AUDIO", 0x10f => "IETF-2-VIDEO", 0x110 => "MUSIC-SERVICE", 0x111 => "SEANET-TELEMETRY", 0x112 => "SEANET-IMAGE", 0x113 => "MLOADD", 0x114 => "ANY PRIVATE EXPERIMENT", 0x115 => "DVMRP on MOSPF", 0x116 => "SVRLOC", 0x117 => "XINGTV", 0x118 => "MICROSOFT-DS", 0x119 => "NBC-PRO", 0x11a => "NBC-PFN", 0x10001 => "LINK NAME", 0x10002 => "ALL DHCP AGENTS", 0x10003 => "LINK LOCAL MULTICAST NAME", 0x10004 => "DTCP ANNOUNCEMENT", }
Class Method Summary collapse
Instance Method Summary collapse
- #/(by) ⇒ Object
- #documentation? ⇒ Boolean
-
#eui_64(mac) ⇒ Object
Return an EUI-64 host address for the current prefix (must be a 64 bit long IPv6 prefix).
- #eui_64? ⇒ Boolean
-
#first ⇒ Object
Retrieve the first address in this prefix (called a network address in IPv4 land).
- #from_6to4 ⇒ Object
- #from_teredo ⇒ Object
- #global? ⇒ Boolean
-
#host? ⇒ Boolean
Extra quick tests.
- #is_6to4? ⇒ Boolean
- #is_teredo? ⇒ Boolean
-
#last ⇒ Object
Retrieve the last address in this prefix (called a broadcast address in IPv4 land).
-
#length ⇒ Object
(also: #bitmask)
Return the bit length of the prefix ie: IPAddr.new(“2001:db8::/32”).length => 32 IPAddr.new(“192.0.2.0/255.255.255.0”).length => 24.
-
#length=(length) ⇒ Object
Modify the bit length of the prefix.
- #link? ⇒ Boolean
-
#local? ⇒ Boolean
Some scope tests.
- #loopback? ⇒ Boolean
- #mac ⇒ Object
-
#mask_with_a_care!(mask) ⇒ Object
Call the original mask! method but don’t allow it to change the internally stored address, since we might actually need that.
- #multicast? ⇒ Boolean
- #multicast_from_prefix? ⇒ Boolean
- #prefix? ⇒ Boolean
-
#prefix_from_multicast ⇒ Object
Returns the original prefix a Multicast address was generated from see RFC3306.
- #private? ⇒ Boolean
-
#reverses ⇒ Object
Return likely reverse zones for the Address or prefix (differs from reverse() because it will return the correct number of zones to adequately delegate the prefix).
-
#scope ⇒ Object
Returns a string describing the scope of the address.
-
#space ⇒ Object
Return the space available inside this prefix.
-
#subnet_mask ⇒ Object
Return an old-style subnet mask ie: IPAddr.new(“2001:db8::/32”).subnet_mask => #<IPAddr: IPv6:ffff:ffff:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff> IPAddr.new(“192.0.2.0/255.255.255.0”).subnet_mask => #<IPAddr: IPv4:255.255.255.0/255.255.255.255>.
-
#to_6to4 ⇒ Object
Convert an IPv4 address into an IPv6 6to4 address.
- #to_string_including_length ⇒ Object
- #unicast? ⇒ Boolean
-
#usable ⇒ Object
Return usable address space inside this prefix.
-
#wildcard_mask ⇒ Object
Return a “cisco style” subnet mask for use in ACLs:.
Class Method Details
.included(base) ⇒ Object
7 8 9 10 11 12 13 |
# File 'lib/ipaddr_extensions.rb', line 7 def self.included(base) base.extend(ClassMethods) base.class_eval do alias_method :mask_without_a_care!, :mask! alias_method :mask!, :mask_with_a_care! end end |
Instance Method Details
#/(by) ⇒ Object
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/ipaddr_extensions.rb', line 409 def /(by) if self.ipv4? space = 1 << 32 - length if space % by == 0 newmask = (((1<<32)-1) ^ (space/by-1)).to_s(2).count("1") (0..by-1).collect do |i| ip = (self.to_i + ((1 << 32 - newmask)*i)).to_ip(Socket::AF_INET) ip.length = newmask ip end else raise ArgumentError.new "Cannot evenly devide by #{by}" end elsif self.ipv6? space = 1 << 128 - length if space % by == 0 newmask = (((1<<128)-1) ^ (space/by-1)).to_s(2).count("1") (0..by-1).collect do |i| ip = (self.to_i + ((1 << 128 - newmask)*i)).to_ip(Socket::AF_INET6) ip.length = newmask ip end else raise ArgumentError.new "Cannot evenly devide by #{by}" end end end |
#documentation? ⇒ Boolean
299 300 301 |
# File 'lib/ipaddr_extensions.rb', line 299 def documentation? self.scope.split(' ').member? 'DOCUMENTATION' end |
#eui_64(mac) ⇒ Object
Return an EUI-64 host address for the current prefix (must be a 64 bit long IPv6 prefix).
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ipaddr_extensions.rb', line 79 def eui_64(mac) if @family != Socket::AF_INET6 raise Exception, "EUI-64 only makes sense on IPv6 prefixes." elsif self.length != 64 raise Exception, "EUI-64 only makes sense on 64 bit IPv6 prefixes." end if mac.is_a? Integer mac = "%:012x" % mac end if mac.is_a? String mac.gsub!(/[^0-9a-fA-F]/, "") if mac.match(/^[0-9a-f]{12}/).nil? raise ArgumentError, "Second argument must be a valid MAC address." end e64 = (mac[0..5] + "fffe" + mac[6..11]).to_i(16) ^ 0x0200000000000000 IPAddr.new(self.first.to_i + e64, Socket::AF_INET6) end end |
#eui_64? ⇒ Boolean
98 99 100 101 102 103 104 105 |
# File 'lib/ipaddr_extensions.rb', line 98 def eui_64? if @family != Socket::AF_INET6 raise Exception, "EUI-64 only makes sense on IPv6 prefixes." #elsif self.length != 64 # raise Exception, "EUI-64 only makes sense on 64 bit IPv6 prefixes." end (self.to_i & 0x20000fffe000000) == 0x20000fffe000000 end |
#first ⇒ Object
Retrieve the first address in this prefix (called a network address in IPv4 land)
61 62 63 |
# File 'lib/ipaddr_extensions.rb', line 61 def first IPAddr.new(@addr & @mask_addr, @family) end |
#from_6to4 ⇒ Object
448 449 450 451 |
# File 'lib/ipaddr_extensions.rb', line 448 def from_6to4 x = self.to_string.scanf("%*4x:%4x:%4x:%s") IPAddr.new((x[0]<<16)+x[1], Socket::AF_INET) end |
#from_teredo ⇒ Object
441 442 443 |
# File 'lib/ipaddr_extensions.rb', line 441 def from_teredo is_teredo? && { :server => IPAddr.new((@addr >> 64) & ((1<<32)-1), Socket::AF_INET), :client => IPAddr.new((@addr & ((1<<32)-1)) ^ ((1<<32)-1), Socket::AF_INET), :port => ((@addr >> 32) & ((1<<16)-1)) } end |
#global? ⇒ Boolean
305 306 307 |
# File 'lib/ipaddr_extensions.rb', line 305 def global? self.scope.split(' ').member? 'GLOBAL' end |
#host? ⇒ Boolean
Extra quick tests
390 391 392 393 |
# File 'lib/ipaddr_extensions.rb', line 390 def host? (@family == Socket::AF_INET && self.length == 32) || (@family == Socket::AF_INET6 && self.length == 128) end |
#is_6to4? ⇒ Boolean
445 446 447 |
# File 'lib/ipaddr_extensions.rb', line 445 def is_6to4? IPAddr.new("2002::/16").include? self end |
#is_teredo? ⇒ Boolean
437 438 439 |
# File 'lib/ipaddr_extensions.rb', line 437 def is_teredo? IPAddr.new("2001::/32").include? self end |
#last ⇒ Object
Retrieve the last address in this prefix (called a broadcast address in IPv4 land)
67 68 69 70 71 72 73 74 75 |
# File 'lib/ipaddr_extensions.rb', line 67 def last if @family == Socket::AF_INET IPAddr.new(first.to_i | (@mask_addr ^ IPAddr::IN4MASK), @family) elsif @family == Socket::AF_INET6 IPAddr.new(first.to_i | (@mask_addr ^ IPAddr::IN6MASK), @family) else raise "unsupported address family." end end |
#length ⇒ Object Also known as: bitmask
Return the bit length of the prefix ie:
IPAddr.new("2001:db8::/32").length
=> 32
IPAddr.new("192.0.2.0/255.255.255.0").length
=> 24
21 22 23 24 |
# File 'lib/ipaddr_extensions.rb', line 21 def length # nasty hack, but works well enough. @mask_addr.to_s(2).count("1") end |
#length=(length) ⇒ Object
Modify the bit length of the prefix
27 28 29 30 31 32 33 |
# File 'lib/ipaddr_extensions.rb', line 27 def length=(length) if self.ipv4? @mask_addr=((1<<32)-1) - ((1<<32-length)-1) elsif self.ipv6? @mask_addr=((1<<128)-1) - ((1<<128-length)-1) end end |
#link? ⇒ Boolean
296 297 298 |
# File 'lib/ipaddr_extensions.rb', line 296 def link? self.scope.split(' ').member? 'LINK' end |
#local? ⇒ Boolean
Some scope tests
287 288 289 |
# File 'lib/ipaddr_extensions.rb', line 287 def local? self.scope.split(' ').member? 'LOCAL' end |
#loopback? ⇒ Boolean
302 303 304 |
# File 'lib/ipaddr_extensions.rb', line 302 def loopback? self.scope.split(' ').member? 'LOOPBACK' end |
#mac ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ipaddr_extensions.rb', line 107 def mac if eui_64? network_bits = self.to_i & 0xffffffffffffffff top_chunk = network_bits >> 40 bottom_chunk = network_bits & 0xffffff mac = ((top_chunk << 24) + bottom_chunk) ^ 0x20000000000 result = [] 5.downto(0).each do |i| result << sprintf("%02x", (mac >> i * 8) & 0xff) end result * ':' end end |
#mask_with_a_care!(mask) ⇒ Object
Call the original mask! method but don’t allow it to change the internally stored address, since we might actually need that.
124 125 126 127 128 129 |
# File 'lib/ipaddr_extensions.rb', line 124 def mask_with_a_care!(mask) original_addr = @addr mask_without_a_care!(mask) @addr = original_addr unless self.class.mask_by_default return self end |
#multicast? ⇒ Boolean
293 294 295 |
# File 'lib/ipaddr_extensions.rb', line 293 def multicast? self.scope.split(' ').member? 'MULTICAST' end |
#multicast_from_prefix? ⇒ Boolean
311 312 313 |
# File 'lib/ipaddr_extensions.rb', line 311 def multicast_from_prefix? ipv6? && ('ff00::/8'.to_ip.include? self) && ((self.to_i >> 116) & 0x03 == 3) end |
#prefix? ⇒ Boolean
395 396 397 |
# File 'lib/ipaddr_extensions.rb', line 395 def prefix? !self.host? end |
#prefix_from_multicast ⇒ Object
Returns the original prefix a Multicast address was generated from see RFC3306
317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/ipaddr_extensions.rb', line 317 def prefix_from_multicast if ipv6? && multicast_from_prefix? prefix_length = (to_i >> 92) & 0xff if (prefix_length == 0xff) && (((to_i >> 112) & 0xf) >= 2) # Link local prefix #(((to_i >> 32) & 0xffffffffffffffff) + (0xfe80 << 112)).to_ip(Socket::AF_INET6).tap { |p| p.length = 64 } return nil # See http://redmine.ruby-lang.org/issues/5468 else # Global unicast prefix (((to_i >> 32) & 0xffffffffffffffff) << 64).to_ip(Socket::AF_INET6).tap { |p| p.length = prefix_length } end end end |
#private? ⇒ Boolean
308 309 310 |
# File 'lib/ipaddr_extensions.rb', line 308 def private? self.scope.split(' ').member? 'PRIVATE' end |
#reverses ⇒ Object
Return likely reverse zones for the Address or prefix (differs from reverse() because it will return the correct
number of zones to adequately delegate the prefix).
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/ipaddr_extensions.rb', line 356 def reverses if @family == Socket::AF_INET if self.length == 32 [ self.reverse ] else boundary = self.length % 8 == 0 && self.length != 0 ? self.length / 8 - 1 : self.length / 8 divisor = (boundary + 1) * 8 count = (self.last.to_i - self.first.to_i) / (1 << 32 - divisor) res = [] (0..count).each do |i| octets = IPAddr.new(first.to_i + ((1<<32-divisor)*i), Socket::AF_INET).to_s.split('.')[0..boundary] res << "#{octets.reverse * '.'}.in-addr.arpa" end res end elsif @family == Socket::AF_INET6 if self.length == 128 [ self.reverse ] else boundary = self.length % 16 == 0 && self.length != 0 ? self.length / 4 - 1 : self.length / 4 divisor = (boundary + 1) * 4 count = (self.last.to_i - self.first.to_i) / (1 << 128-divisor) res = [] (0..count).each do |i| baseaddr = self.first.to_i + (1<<128-divisor)*i octets = ("%032x" % baseaddr).split('')[0..boundary] res << octets.reverse * '.' + '.ip6.arpa' end res end end end |
#scope ⇒ Object
Returns a string describing the scope of the address.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/ipaddr_extensions.rb', line 197 def scope if @family == Socket::AF_INET if IPAddr.new("0.0.0.0/8").include? self "CURRENT NETWORK" elsif IPAddr.new("10.0.0.0/8").include? self "RFC1918 PRIVATE" elsif IPAddr.new("127.0.0.0/8").include? self "LOOPBACK" elsif IPAddr.new("168.254.0.0/16").include? self "AUTOCONF PRIVATE" elsif IPAddr.new("172.16.0.0/12").include? self "RFC1918 PRIVATE" elsif IPAddr.new("192.0.0.0/24").include? self "RESERVED (IANA)" elsif IPAddr.new("192.0.2.0/24").include? self "DOCUMENTATION" elsif IPAddr.new("192.88.99.0/24").include? self "6to4 ANYCAST" elsif IPAddr.new("192.168.0.0/16").include? self "RFC1918 PRIVATE" elsif IPAddr.new("198.18.0.0/15").include? self "NETWORK BENCHMARK TESTS" elsif IPAddr.new("198.51.100.0/24").include? self "DOCUMENTATION" elsif IPAddr.new("203.0.113.0/24").include? self "DOCUMENTATION" elsif IPAddr.new("224.0.0.0/4").include? self if IPAddr.new("239.0.0.0/8").include? self "LOCAL MULTICAST" else "GLOBAL MULTICAST" end elsif IPAddr.new("240.0.0.0/4").include? self "RESERVED" elsif IPAddr.new("255.255.255.255") == self "GLOBAL BROADCAST" else "GLOBAL UNICAST" end elsif @family == Socket::AF_INET6 if IPAddr.new("2000::/3").include? self require 'scanf' if is_6to4? "GLOBAL UNICAST (6to4: #{from_6to4})" elsif is_teredo? "GLOBAL UNICAST (Teredo #{from_teredo[:client].to_s}:#{from_teredo[:port].to_s} -> #{from_teredo[:server].to_s}:#{from_teredo[:port].to_s})" elsif IPAddr.new("2001:10::/28").include? self "ORCHID" elsif IPAddr.new("2001:db8::/32").include? self "DOCUMENTATION" else "GLOBAL UNICAST" end elsif IPAddr.new("::/128") == self "UNSPECIFIED ADDRESS" elsif IPAddr.new("::1/128") == self "LINK LOCAL LOOPBACK" elsif IPAddr.new("::ffff:0:0/96").include? self a,b,c,d = self.to_string.scanf("%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%4x:%4x:%4x:%4x") "IPv4 MAPPED (#{a.to_s}.#{b.to_s}.#{c.to_s}.#{d.to_s})" elsif IPAddr.new("::/96").include? self a,b,c,d = self.to_string.scanf("%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%*4x:%4x:%4x:%4x:%4x") "IPv4 TRANSITION (#{a.to_s}.#{b.to_s}.#{c.to_s}.#{d.to_s}, deprecated)" elsif IPAddr.new("fc00::/7").include? self "UNIQUE LOCAL UNICAST" elsif IPAddr.new("fec0::/10").include? self "SITE LOCAL (deprecated)" elsif IPAddr.new("fe80::/10").include? self "LINK LOCAL UNICAST" elsif IPAddr.new("ff00::/8").include? self mscope,mdesta,mdestb = self.to_string.scanf("%*1x%*1x%*1x%1x:%*4x:%*4x:%*4x:%*4x:%*4x:%4x:%4x") mdest = (mdesta << 16) + mdestb s = "MULTICAST" if MSCOPES[mscope] s += " #{MSCOPES[mscope]}" end if MDESTS[mdest] s += " #{MDESTS[mdest]}" end if multicast_from_prefix? s += " (prefix = #{prefix_from_multicast.to_string_including_length})" end s else "RESERVED" end end end |
#space ⇒ Object
Return the space available inside this prefix
340 341 342 |
# File 'lib/ipaddr_extensions.rb', line 340 def space self.last.to_i - self.first.to_i + 1 end |
#subnet_mask ⇒ Object
Return an old-style subnet mask ie:
IPAddr.new("2001:db8::/32").subnet_mask
=> #<IPAddr: IPv6:ffff:ffff:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
IPAddr.new("192.0.2.0/255.255.255.0").subnet_mask
=> #<IPAddr: IPv4:255.255.255.0/255.255.255.255>
41 42 43 |
# File 'lib/ipaddr_extensions.rb', line 41 def subnet_mask @mask_addr.to_ip end |
#to_6to4 ⇒ Object
Convert an IPv4 address into an IPv6 6to4 address.
333 334 335 336 337 |
# File 'lib/ipaddr_extensions.rb', line 333 def to_6to4 if @family == Socket::AF_INET IPAddr.new((0x2002 << 112) + (@addr << 80), Socket::AF_INET6).tap { |p| p.length = 48 } end end |
#to_string_including_length ⇒ Object
399 400 401 402 403 404 405 |
# File 'lib/ipaddr_extensions.rb', line 399 def to_string_including_length if host? to_s else "#{to_s}/#{length.to_s}" end end |
#unicast? ⇒ Boolean
290 291 292 |
# File 'lib/ipaddr_extensions.rb', line 290 def unicast? !self.scope.split(' ').any? { |scope| ['BROADCAST', 'MULTICAST'].member? scope } end |
#usable ⇒ Object
Return usable address space inside this prefix
345 346 347 348 349 350 351 |
# File 'lib/ipaddr_extensions.rb', line 345 def usable if ipv6? space else space - 2 end end |
#wildcard_mask ⇒ Object
Return a “cisco style” subnet mask for use in ACLs:
IPAddr.new("2001:db8::/32").wildcard_mask
=> #<IPAddr: IPv6:0000:0000:ffff:ffff:ffff:ffff:ffff:ffff/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
IPAddr.new("192.0.2.0/255.255.255.0").wildcard_mask
=> #<IPAddr: IPv4:0.0.0.255/255.255.255.255>
51 52 53 54 55 56 57 |
# File 'lib/ipaddr_extensions.rb', line 51 def wildcard_mask if self.ipv4? (@mask_addr ^ IPAddr::IN4MASK).to_ip else (@mask_addr ^ IPAddr::IN6MASK).to_ip end end |