Class: IPAddress

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/ipaddress.rb,
lib/ipaddress/rle.rb,
lib/ipaddress/ipv4.rb,
lib/ipaddress/ipv6.rb,
lib/ipaddress/prefix.rb,
lib/ipaddress/crunchy.rb,
lib/ipaddress/ip_bits.rb,
lib/ipaddress/prefix32.rb,
lib/ipaddress/prefix128.rb,
lib/ipaddress/ip_version.rb,
lib/ipaddress/ipv6_mapped.rb,
lib/ipaddress/ipv6_unspec.rb,
lib/ipaddress/ipv6_loopback.rb,
lib/ipaddress/result_crunchy_parts.rb

Defined Under Namespace

Modules: IpVersion Classes: Crunchy, IpBits, Ipv4, Ipv6, Ipv6Loopback, Ipv6Mapped, Ipv6Unspec, Last, Prefix, Prefix128, Prefix32, ResultCrunchyParts, Rle

Constant Summary collapse

RE_MAPPED =

Parse the argument string to create a new

IPv4, IPv6 or Mapped IP object

  ip  = IPAddress.parse "172.16.10.1/24"
  ip6 = IPAddress.parse "2001:db8.8:800:200c:417a/64"
  ip_mapped = IPAddress.parse ".ffff:172.16.10.1/128"

All the object created will be instances of the
correct class:

 ip.class
   # => IPAddress.IPv4
 ip6.class
   # => IPAddress.IPv6
 ip_mapped.class
   # => IPAddress.IPv6.Mapped
/:.+\./
RE_IPV4 =
/\./
RE_IPV6 =
/:/
RE_DIGIT =
/^\d+$/
RE_HEX_DIGIT =
/^[0-9a-fA-F]+$/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj) ⇒ IPAddress

Returns a new instance of IPAddress.



23
24
25
26
27
28
29
30
31
# File 'lib/ipaddress.rb', line 23

def initialize(obj)
  @ip_bits = obj[:ip_bits]
  @host_address = obj[:host_address]
  @prefix = obj[:prefix]
  @mapped = obj[:mapped]
  @vt_is_private = obj[:vt_is_private]
  @vt_is_loopback = obj[:vt_is_loopback]
  @vt_to_ipv6 = obj[:vt_to_ipv6]
end

Instance Attribute Details

#host_addressObject

Returns the value of attribute host_address.



19
20
21
# File 'lib/ipaddress.rb', line 19

def host_address
  @host_address
end

#ip_bitsObject (readonly)

Returns the value of attribute ip_bits.



19
20
21
# File 'lib/ipaddress.rb', line 19

def ip_bits
  @ip_bits
end

#mappedObject

Returns the value of attribute mapped.



20
21
22
# File 'lib/ipaddress.rb', line 20

def mapped
  @mapped
end

#prefixObject

Returns the value of attribute prefix.



19
20
21
# File 'lib/ipaddress.rb', line 19

def prefix
  @prefix
end

#vt_is_loopbackObject (readonly)

Returns the value of attribute vt_is_loopback.



20
21
22
# File 'lib/ipaddress.rb', line 20

def vt_is_loopback
  @vt_is_loopback
end

#vt_is_privateObject (readonly)

Returns the value of attribute vt_is_private.



20
21
22
# File 'lib/ipaddress.rb', line 20

def vt_is_private
  @vt_is_private
end

#vt_to_ipv6Object (readonly)

Returns the value of attribute vt_to_ipv6.



20
21
22
# File 'lib/ipaddress.rb', line 20

def vt_to_ipv6
  @vt_to_ipv6
end

Class Method Details

.aggregate(networks) ⇒ Object



390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
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
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/ipaddress.rb', line 390

def self.aggregate(networks)
  if (networks.length == 0)
    return []
  end

  if (networks.length == 1)
    #  console.log("aggregate:", networks[0], networks[0].network())
    return [networks[0].network()]
  end

  stack = networks.map{|i| i.network()}.sort{|a, b| a.cmp(b) }
  #  console.log(IPAddress.to_string_vec(stack))
  #  for i in 0..networks.length {
  #      println!("{}==={}", &networks[i].to_string_uncompressed(),
  #          &stack[i].to_string_uncompressed())
  #  }
  pos = 0
  while true
    if (pos < 0)
      pos = 0
    end

    stack_len = stack.length #  borrow checker
    #  println!("loop:{}:{}", pos, stack_len)
    #  if stack_len == 1 {
    #      println!("exit 1")
    #      break
    #  }
    if (pos >= stack_len)
      #  println!("exit first:{}:{}", stack_len, pos)
      break
    end

    first = IPAddress.pos_to_idx(pos, stack_len)
    pos = pos + 1
    if (pos >= stack_len)
      #  println!("exit second:{}:{}", stack_len, pos)
      break
    end

    second = IPAddress.pos_to_idx(pos, stack_len)
    pos = pos + 1
    # firstUnwrap = first
    if (stack[first].includes(stack[second]))
      pos = pos - 2
      #  println!("remove:1:{}:{}:{}=>{}", first, second, stack_len, pos + 1)
      pidx = IPAddress.pos_to_idx(pos + 1, stack_len)
      stack.delete_at(pidx)
    else
      stack[first].prefix = stack[first].prefix.sub(1)
      #  println!("complex:{}:{}:{}:{}:P1:{}:P2:{}", pos, stack_len,
      #  first, second,
      #  stack[first].to_string(), stack[second].to_string())
      if ((stack[first].prefix.num + 1) == stack[second].prefix.num &&
          stack[first].includes(stack[second]))
        pos = pos - 2
        idx = IPAddress.pos_to_idx(pos, stack_len)
        stack[idx] = stack[first].clone(); #  kaputt
        pidx = IPAddress.pos_to_idx(pos + 1, stack_len)
        stack.delete_at(pidx)
        #  println!("remove-2:{}:{}", pos + 1, stack_len)
        pos = pos - 1; #  backtrack
      else
        stack[first].prefix = stack[first].prefix.add(1); # reset prefix
        #  println!("easy:{}:{}=>{}", pos, stack_len, stack[first].to_string())
        pos = pos - 1; #  do it with second as first
      end
    end
  end

  #  println!("agg={}:{}", pos, stack.length)
  return stack.slice(0, stack.length)
end

.is_valid(addr) ⇒ Object

Checks if the given string is a valid IP address,

either IPv4 or IPv6

Example:

  IPAddress.valid? "2002.1"
    # => true

  IPAddress.valid? "10.0.0.256"
    # => false


206
207
208
# File 'lib/ipaddress.rb', line 206

def self.is_valid(addr)
  return IPAddress.is_valid_ipv4(addr) || IPAddress.is_valid_ipv6(addr)
end

.is_valid_ipv4(addr) ⇒ Object



293
294
295
# File 'lib/ipaddress.rb', line 293

def self.is_valid_ipv4(addr)
  return !IPAddress.split_to_u32(addr).nil?
end

.is_valid_ipv6(addr) ⇒ Object



373
374
375
# File 'lib/ipaddress.rb', line 373

def self.is_valid_ipv6(addr)
  return IPAddress.split_to_num(addr) != nil
end

.is_valid_netmask(addr) ⇒ Object

Checks if the argument is a valid IPv4 netmask

expressed in dotted decimal format.

  IPAddress.valid_ipv4_netmask? "255.255.0.0"
    # => true


758
759
760
# File 'lib/ipaddress.rb', line 758

def self.is_valid_netmask(addr)
  return !IPAddress.parse_netmask_to_prefix(addr).nil?
end

.netmask_to_prefix(nm, bits) ⇒ Object



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
# File 'lib/ipaddress.rb', line 766

def self.netmask_to_prefix(nm, bits)
  prefix = 0
  addr = nm.clone()
  in_host_part = true
  #  two = Crunchy.two()
  _ = 0
  while _ < bits
    bit = addr.mds(2)
    #puts "#{nm.toString(16)} #{bit} #{_} #{in_host_part}"
    #  console.log(">>>", bits, bit, addr, nm)
    if (in_host_part && bit == 0)
      prefix = prefix + 1
    elsif (in_host_part && bit == 1)
      in_host_part = false
    elsif (!in_host_part && bit == 0)
      return nil
    end

    addr = addr.shr(1)
    _ += 1
  end

  return bits - prefix
end

.parse(str) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ipaddress.rb', line 117

def self.parse(str)
  if (RE_MAPPED.match(str))
    #  console.log("mapped:", str)
    return Ipv6Mapped.create(str)
  else
    if (RE_IPV4.match(str))
      # puts("ipv4:", str)
      return Ipv4.create(str)
    elsif (RE_IPV6.match(str))
      #  console.log("ipv6:", str)
      return Ipv6.create(str)
    end
  end

  return nil
end

.parse_dec_str(str) ⇒ Object



215
216
217
218
219
220
221
222
223
224
# File 'lib/ipaddress.rb', line 215

def self.parse_dec_str(str)
  if (!RE_DIGIT.match(str))
    # puts "=1 #{str}"
    #  console.log("parse_dec_str:-1:", str)
    return nil
  end

  part = str.to_i
  return part
end

.parse_hex_str(str) ⇒ Object



227
228
229
230
231
232
233
234
# File 'lib/ipaddress.rb', line 227

def self.parse_hex_str(str)
  if (!RE_HEX_DIGIT.match(str))
    return nil
  end

  part = str.to_i(16)
  return part
end

.parse_ipv4_part(i) ⇒ Object

Checks if the given string is a valid IPv4 address

Example:

  IPAddress.valid_ipv4? "2002.1"
    # => false

  IPAddress.valid_ipv4? "172.16.10.1"
    # => true


246
247
248
249
250
251
252
253
254
# File 'lib/ipaddress.rb', line 246

def self.parse_ipv4_part(i)
  part = IPAddress.parse_dec_str(i)
  # console.log("i=", i, part)
  if (part === nil || part >= 256)
    return nil
  end

  return part
end

.parse_netmask_to_prefix(netmask) ⇒ Object



791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
# File 'lib/ipaddress.rb', line 791

def self.parse_netmask_to_prefix(netmask)
  #  console.log("--1", netmask)
  is_number = IPAddress.parse_dec_str(netmask)
  if (!is_number.nil?)
    #  console.log("--2", netmask, is_number)
    return is_number
  end

  my = IPAddress.parse(netmask)
  #  console.log("--3", netmask, my)
  if (!my)
    #  console.log("--4", netmask, my)
    return nil
  end

  #  console.log("--5", netmask, my)
  my_ip = my
  return IPAddress.netmask_to_prefix(my_ip.host_address, my_ip.ip_bits.bits)
end

.pos_to_idx(pos, len) ⇒ Object

private helper for summarize

assumes that networks is output from reduce_networks
means it should be sorted lowers first and uniq


382
383
384
385
386
387
388
# File 'lib/ipaddress.rb', line 382

def self.pos_to_idx(pos, len)
  ilen = len
  #  ret = pos % ilen
  rem = ((pos % ilen) + ilen) % ilen
  #  println!("pos_to_idx:{}:{}=>{}:{}", pos, len, ret, rem)
  return rem
end

.split_at_slash(str) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/ipaddress.rb', line 134

def self.split_at_slash(str)
  slash = str.strip().split("/")
  addr = ""
  if (slash[0])
    addr += slash[0].strip()
  end

  if (slash[1])
    return [addr, slash[1].strip()]
  else
    return [addr, nil]
  end
end

.split_on_colon(addr) ⇒ Object

Checks if the given string is a valid IPv6 address

Example:

  IPAddress.valid_ipv6? "2002.1"
    # => true

  IPAddress.valid_ipv6? "2002.DEAD.BEEF"
    # => false


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/ipaddress.rb', line 307

def self.split_on_colon(addr)
  parts = addr.strip().split(":")
  ip = Crunchy.zero()
  if (parts.length == 1 && parts[0].length == 0)
    return ResultCrunchyParts.new(ip, 0)
  end

  parts_len = parts.length
  shift = ((parts_len - 1) * 16)
  parts.each do |i|
    # println!("{}={}", addr, i)
    part = IPAddress.parse_hex_str(i)
    if (part === nil || part >= 65536)
      return nil
    end

    ip = ip.add(Crunchy.from_number(part).shl(shift))
    shift -= 16
  end

  return ResultCrunchyParts.new(ip, parts_len)
end

.split_to_num(addr) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/ipaddress.rb', line 330

def self.split_to_num(addr)
  # ip = 0
  addr = addr.strip()
  pre_post = addr.split("::")
  if pre_post.length == 0 && addr.include?("::")
    pre_post << ""
  end

  if pre_post.length == 1 && addr.include?("::")
    pre_post << ""
  end

  #puts ">>>>split #{addr} #{pre_post}"
  if (pre_post.length > 2)
    return nil
  end

  if (pre_post.length == 2)
    # println!("{}=.={}", pre_post[0], pre_post[1])
    pre = IPAddress.split_on_colon(pre_post[0])
    if (!pre)
      return pre
    end

    post = IPAddress.split_on_colon(pre_post[1])
    if (!post)
      return post
    end

    #  println!("pre:{} post:{}", pre_parts, post_parts)
    return ResultCrunchyParts.new(
      pre.crunchy.shl(128 - (pre.parts * 16)).add(post.crunchy), 128 / 16)
  end

  # println!("split_to_num:no double:{}", addr)
  ret = IPAddress.split_on_colon(addr)
  if (ret == nil || ret.parts != 128 / 16)
    return nil
  end

  return ret
end

.split_to_u32(addr) ⇒ Object



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
285
286
287
288
289
290
291
# File 'lib/ipaddress.rb', line 256

def self.split_to_u32(addr)
  ip = Crunchy.zero()
  shift = 24
  split_addr = addr.split(".")
  if (split_addr.length > 4)
    # puts "+1"
    return nil
  end

  split_addr_len = split_addr.length
  if (split_addr_len < 4)
    part = IPAddress.parse_ipv4_part(split_addr[split_addr_len - 1])
    if (part === nil)
      # puts "+2"
      return nil
    end

    ip = Crunchy.from_number(part)
    split_addr = split_addr.slice(0, split_addr_len - 1)
  end

  split_addr.each do |i|
    part = IPAddress.parse_ipv4_part(i)
    #  console.log("u32-", addr, i, part)
    if (part === nil)
      # puts "+3"
      return nil
    end

    # println!("{}-{}", part_num, shift)
    ip = ip.add(Crunchy.from_number(part).shl(shift))
    shift -= 8
  end

  return ip
end

.summarize(*networks) ⇒ Object

Summarization (or aggregation) is the process when two or more

networks are taken together to check if a supernet, including all
and only these networks, exists. If it exists then @supernet
is called the summarized (or aggregated) network.

It is very important to understand that summarization can only
occur if there are no holes in the aggregated network, or, in other
words, if the given networks fill completely the address space
of the supernet. So the two rules are:

1) The aggregate network must contain +all+ the IP addresses of the
   original networks
2) The aggregate network must contain +only+ the IP addresses of the
   original networks

A few examples will help clarify the above. Let's consider for
instance the following two networks:

  ip1 = IPAddress("172.16.10.0/24")
  ip2 = IPAddress("172.16.11.0/24")

These two networks can be expressed using only one IP address
network if we change the prefix. Let Ruby do the work:

  IPAddress.IPv4.summarize(ip1,ip2).to_s
    # => "172.16.10.0/23"

We note how the network "172.16.10.0/23" includes all the addresses
specified in the above networks, and (more important) includes
ONLY those addresses.

If we summarized +ip1+ and +ip2+ with the following network:

  "172.16.0.0/16"

we would have satisfied rule # 1 above, but not rule # 2. So "172.16.0.0/16"
is not an aggregate network for +ip1+ and +ip2+.

If it's not possible to compute a single aggregated network for all the
original networks, the method returns an array with all the aggregate
networks found. For example, the following four networks can be
aggregated in a single /22:

  ip1 = IPAddress("10.0.0.1/24")
  ip2 = IPAddress("10.0.1.1/24")
  ip3 = IPAddress("10.0.2.1/24")
  ip4 = IPAddress("10.0.3.1/24")

  IPAddress.IPv4.summarize(ip1,ip2,ip3,ip4).to_string
    # => "10.0.0.0/22",

But the following networks can't be summarized in a single network:

  ip1 = IPAddress("10.0.1.1/24")
  ip2 = IPAddress("10.0.2.1/24")
  ip3 = IPAddress("10.0.3.1/24")
  ip4 = IPAddress("10.0.4.1/24")

  IPAddress.IPv4.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
    # => ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]

 Summarization (or aggregation) is the process when two or more
 networks are taken together to check if a supernet, including all
 and only these networks, exists. If it exists then @supernet
 is called the summarized (or aggregated) network.

 It is very important to understand that summarization can only
 occur if there are no holes in the aggregated network, or, in other
 words, if the given networks fill completely the address space
 of the supernet. So the two rules are:

 1) The aggregate network must contain +all+ the IP addresses of the
    original networks
 2) The aggregate network must contain +only+ the IP addresses of the
    original networks

 A few examples will help clarify the above. Let's consider for
 instance the following two networks:

   ip1 = IPAddress("2000:0.4/32")
   ip2 = IPAddress("2000:1.6/32")

 These two networks can be expressed using only one IP address
 network if we change the prefix. Let Ruby do the work:

   IPAddress.IPv6.summarize(ip1,ip2).to_s
     #  => "2000:0./31"

 We note how the network "2000:0./31" includes all the addresses
 specified in the above networks, and (more important) includes
 ONLY those addresses.

 If we summarized +ip1+ and +ip2+ with the following network:

   "2000./16"

 we would have satisfied rule #  1 above, but not rule #  2. So "2000./16"
 is not an aggregate network for +ip1+ and +ip2+.

 If it's not possible to compute a single aggregated network for all the
 original networks, the method returns an array with all the aggregate
 networks found. For example, the following four networks can be
 aggregated in a single /22:

   ip1 = IPAddress("2000:0./32")
   ip2 = IPAddress("2000:1./32")
   ip3 = IPAddress("2000:2./32")
   ip4 = IPAddress("2000:3./32")

   IPAddress.IPv6.summarize(ip1,ip2,ip3,ip4).to_string
     #  => ""2000:3./30",

 But the following networks can't be summarized in a single network:

   ip1 = IPAddress("2000:1./32")
   ip2 = IPAddress("2000:2./32")
   ip3 = IPAddress("2000:3./32")
   ip4 = IPAddress("2000:4./32")

   IPAddress.IPv4.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
     #  => ["2000:1./32","2000:2./31","2000:4./32"]


680
681
682
# File 'lib/ipaddress.rb', line 680

def self.summarize(*networks)
  return IPAddress.aggregate(networks.flatten)
end

.summarize_str(*netstr) ⇒ Object



684
685
686
687
688
689
690
691
692
# File 'lib/ipaddress.rb', line 684

def self.summarize_str(*netstr)
  vec = IPAddress.to_ipaddress_vec(netstr.flatten)
  #  console.log(netstr, vec)
  if (!vec)
    return vec
  end

  return IPAddress.aggregate(vec)
end

.to_ipaddress_vec(vec) ⇒ Object



1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
# File 'lib/ipaddress.rb', line 1003

def self.to_ipaddress_vec(vec)
  ret = []
  vec.each do |ipstr|
    ipa = IPAddress.parse(ipstr)
    if (!ipa)
      # puts "#{ipstr} failed"
      return nil
    end

    ret.push(ipa)
  end

  return ret
end

.to_network(adr, host_prefix) ⇒ Object



969
970
971
# File 'lib/ipaddress.rb', line 969

def self.to_network(adr, host_prefix)
  return adr.shr(host_prefix).shl(host_prefix)
end

.to_s_vec(vec) ⇒ Object



985
986
987
988
989
990
991
992
# File 'lib/ipaddress.rb', line 985

def self.to_s_vec(vec)
  ret = []
  vec.each do |i|
    ret.push(i.to_s())
  end

  return ret
end

.to_string_vec(vec) ⇒ Object



994
995
996
997
998
999
1000
1001
# File 'lib/ipaddress.rb', line 994

def self.to_string_vec(vec)
  ret = []
  vec.each do |i|
    ret.push(i.to_string())
  end

  return ret
end

.valid?(addr) ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
# File 'lib/ipaddress.rb', line 210

def self.valid?(addr)
  is_valid(addr)
end

.valid_netmask?(addr) ⇒ Boolean

Returns:

  • (Boolean)


762
763
764
# File 'lib/ipaddress.rb', line 762

def self.valid_netmask?(addr)
  is_valid_netmask(addr)
end

Instance Method Details

#<=>(oth) ⇒ Object



66
67
68
# File 'lib/ipaddress.rb', line 66

def <=>(oth)
  cmp(oth)
end

#add(other) ⇒ Object



981
982
983
# File 'lib/ipaddress.rb', line 981

def add(other)
  return IPAddress.aggregate([self, other])
end

#bitsObject

Returns the address portion of an IP in binary format,

as a string containing a sequence of 0 and 1

  ip = IPAddress("127.0.0.1")

  ip.bits
    #  => "01111111000000000000000000000001"


903
904
905
906
907
908
909
910
911
912
913
914
# File 'lib/ipaddress.rb', line 903

def bits()
  num = @host_address.toString(2)
  ret = ""
  _ = num.length
  while  _ < @ip_bits.bits
    ret += "0"
    _ += 1
  end

  ret += num
  return ret
end

#broadcastObject

Returns the broadcast address for the given IP.

ip = IPAddress("172.16.10.64/24")

ip.broadcast.to_s
  #  => "172.16.10.255"


931
932
933
934
# File 'lib/ipaddress.rb', line 931

def broadcast()
  return from(network().host_address.add(size().sub(Crunchy.one())), @prefix)
  #  IPv4.parse_u32(@broadcast_u32, @prefix)
end

#change_netmask(str) ⇒ Object



837
838
839
840
841
842
843
844
# File 'lib/ipaddress.rb', line 837

def change_netmask(str)
  nm = IPAddress.parse_netmask_to_prefix(str)
  if (!nm)
    return nil
  end

  return change_prefix(nm)
end

#change_prefix(num) ⇒ Object

Set a new prefix number for the object

This is useful if you want to change the prefix
to an object created with IPv4.parse_u32 or
if the object was created using the classful
mask.

  ip = IPAddress("172.16.100.4")

  puts ip
    #  => 172.16.100.4/16

  ip.prefix = 22

  puts ip
    #  => 172.16.100.4/22


828
829
830
831
832
833
834
835
# File 'lib/ipaddress.rb', line 828

def change_prefix(num)
  prefix = @prefix.from(num)
  if (!prefix)
    return nil
  end

  return from(@host_address, prefix)
end

#cloneObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ipaddress.rb', line 33

def clone()
  mapped = nil
  if (@mapped)
    mapped = @mapped.clone()
  end

  return IPAddress.new({
    ip_bits: @ip_bits.clone(),
    host_address: @host_address.clone(),
    prefix: @prefix.clone(),
    mapped: mapped,
    vt_is_private: @vt_is_private,
    vt_is_loopback: @vt_is_loopback,
    vt_to_ipv6: @vt_to_ipv6
  })
end

#cmp(oth) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ipaddress.rb', line 70

def cmp(oth)
  if (@ip_bits.version != oth.ip_bits.version)
    if (@ip_bits.version == IpVersion::V6)
      return 1
    end

    return -1
  end

  hostCmp = @host_address.compare(oth.host_address)
  if (hostCmp != 0)
    return hostCmp
  end

  return @prefix.cmp(oth.prefix)
end

#dataObject



1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
# File 'lib/ipaddress.rb', line 1436

def data
  vec = []
  my = host_address
  part_mod = Crunchy.one().shl(8)
  i = 0
  while i < self.ip_bits.bits
    vec.push(my.mod(part_mod).num)
    my = my.shr(8)
    i = i + 8
  end

  return vec.reverse().pack("c*")
end

#decObject



1099
1100
1101
1102
1103
1104
1105
1106
1107
# File 'lib/ipaddress.rb', line 1099

def dec()
  ret = clone()
  ret.host_address = ret.host_address.sub(Crunchy.one())
  if (ret.gte(first()))
    return ret
  end

  return nil
end

#dns_networksObject



527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
# File 'lib/ipaddress.rb', line 527

def dns_networks()
  #  +@ip_bits.dns_bits-1
  next_bit_mask = @ip_bits.bits -
    ((~~((@prefix.host_prefix()) / @ip_bits.dns_bits)) * @ip_bits.dns_bits)
  #  console.log("dns_networks-1", @to_string(), @prefix.host_prefix();j
  #  @ip_bits.dns_bits, next_bit_mask);
  if (next_bit_mask <= 0)
    return [network()]
  end

  #   println!("dns_networks:{}:{}", @to_string(), next_bit_mask)
  #  dns_bits
  step_bit_net = Crunchy.one().shl(@ip_bits.bits - next_bit_mask)
  if (step_bit_net.eq(Crunchy.zero()))
    #  console.log("dns_networks-2", @to_string());
    return [network()]
  end

  ret = []
  step = network().host_address
  prefix = @prefix.from(next_bit_mask)
  while (step.lte(broadcast().host_address))
    #  console.log("dns_networks-3", @to_string(), step.toString(), next_bit_mask, step_bit_net.toString());
    ret.push(from(step, prefix))
    step = step.add(step_bit_net)
  end

  return ret
end

#dns_partsObject



514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/ipaddress.rb', line 514

def dns_parts()
  ret = []
  num = @host_address.clone()
  mask = Crunchy.one().shl(@ip_bits.dns_bits)
  (@ip_bits.bits / @ip_bits.dns_bits).times do
    part = num.clone().mod(mask).num
    num = num.shr(@ip_bits.dns_bits)
    ret.push(part)
  end

  return ret
end

#dns_rev_domainsObject

Returns the IP address in in-addr.arpa format

for DNS Domain definition entries like SOA Records

  ip = IPAddress("172.17.100.50/15")

  ip.dns_rev_domains
    #  => ["16.172.in-addr.arpa","17.172.in-addr.arpa"]


486
487
488
489
490
491
492
493
494
# File 'lib/ipaddress.rb', line 486

def dns_rev_domains()
  ret = []
  dns_networks().each do |net|
    #  console.log("dns_rev_domains:", @to_string(), net.to_string())
    ret.push(net.dns_reverse())
  end

  return ret
end

#dns_reverseObject



496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/ipaddress.rb', line 496

def dns_reverse()
  ret = ""
  dot = ""
  dns_parts = dns_parts()
  i = ((@prefix.host_prefix() + (@ip_bits.dns_bits - 1)) / @ip_bits.dns_bits)
  while  i < dns_parts().length
    #  console.log("dns_r", i);
    ret += dot
    ret += @ip_bits.dns_part_format(dns_parts[i])
    dot = "."
    i += 1
  end

  ret += dot
  ret += @ip_bits.rev_domain
  return ret
end

#each(&func) ⇒ Object

# => “10.0.0.0”

#  => "10.0.0.1"
#  => "10.0.0.2"
#  => "10.0.0.3"
#  => "10.0.0.4"
#  => "10.0.0.5"
#  => "10.0.0.6"
#  => "10.0.0.7"


1130
1131
1132
1133
1134
1135
1136
# File 'lib/ipaddress.rb', line 1130

def each(&func)
  i = network().host_address
  while (i.num <= broadcast().host_address.num)
    func.call(from(i, @prefix))
    i = i.add(Crunchy.one())
  end
end

#each_host(&func) ⇒ Object

# => “10.0.0.1”

#  => "10.0.0.2"
#  => "10.0.0.3"
#  => "10.0.0.4"
#  => "10.0.0.5"
#  => "10.0.0.6"


1081
1082
1083
1084
1085
1086
1087
# File 'lib/ipaddress.rb', line 1081

def each_host(&func)
  i = first().host_address
  while (i.lte(last().host_address))
    func.call(from(i, @prefix))
    i = i.add(Crunchy.one())
  end
end

#eq(other) ⇒ Object



87
88
89
90
91
# File 'lib/ipaddress.rb', line 87

def eq(other)
  return @ip_bits.version == other.ip_bits.version &&
    @prefix.eq(other.prefix) &&
    @host_address.eq(other.host_address)
end

#firstObject

Returns a new IPv4 object with the

first host IP address in the range.

Example: given the 192.168.100.0/24 network, the first
host IP address is 192.168.100.1.

  ip = IPAddress("192.168.100.0/24")

  ip.first.to_s
    #  => "192.168.100.1"

The object IP doesn't need to be a network: the method
automatically gets the network number from it

  ip = IPAddress("192.168.100.50/24")

  ip.first.to_s
    #  => "192.168.100.1"


1037
1038
1039
# File 'lib/ipaddress.rb', line 1037

def first()
  return from(network().host_address.add(@ip_bits.host_ofs), @prefix)
end

#from(addr, prefix) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/ipaddress.rb', line 148

def from(addr, prefix)
  mapped = nil
  if (@mapped)
    mapped = @mapped.clone()
  end

  return IPAddress.new({
    ip_bits: @ip_bits,
    host_address: addr.clone(),
    prefix: prefix.clone(),
    mapped: mapped,
    vt_is_private: @vt_is_private,
    vt_is_loopback: @vt_is_loopback,
    vt_to_ipv6: @vt_to_ipv6
  })
end

#gt(oth) ⇒ Object



58
59
60
# File 'lib/ipaddress.rb', line 58

def gt(oth)
  return cmp(oth) == 1
end

#gte(oth) ⇒ Object



62
63
64
# File 'lib/ipaddress.rb', line 62

def gte(oth)
  return cmp(oth) >= 0
end

#incObject



1089
1090
1091
1092
1093
1094
1095
1096
1097
# File 'lib/ipaddress.rb', line 1089

def inc()
  ret = clone()
  ret.host_address = ret.host_address.add(Crunchy.one())
  if (ret.lte(last()))
    return ret
  end

  return nil
end

#include?(oth) ⇒ Boolean

Checks whether a subnet includes the given IP address.

Accepts an IPAddress.IPv4 object.

  ip = IPAddress("192.168.10.100/24")

  addr = IPAddress("192.168.10.102/24")

  ip.include? addr
    #  => true

  ip.include? IPAddress("172.16.0.48/16")
    #  => false

Returns:

  • (Boolean)


1202
1203
1204
# File 'lib/ipaddress.rb', line 1202

def include?(oth)
  includes(oth)
end

#include_all?(*oths) ⇒ Boolean

Checks whether a subnet includes all the

given IPv4 objects.

  ip = IPAddress("192.168.10.100/24")

  addr1 = IPAddress("192.168.10.102/24")
  addr2 = IPAddress("192.168.10.103/24")

  ip.include_all?(addr1,addr2)
    #  => true

Returns:

  • (Boolean)


1225
1226
1227
# File 'lib/ipaddress.rb', line 1225

def include_all?(*oths)
  includes_all(oths)
end

#includes(oth) ⇒ Object



1206
1207
1208
1209
1210
1211
1212
# File 'lib/ipaddress.rb', line 1206

def includes(oth)
  ret = is_same_kind(oth) &&
    @prefix.num <= oth.prefix.num &&
    network().host_address.eq(IPAddress.to_network(oth.host_address, @prefix.host_prefix()))
  #  println!("includes:{}=={}=>{}", @to_string(), oth.to_string(), ret)
  return ret
end

#includes_all(*oths) ⇒ Object



1229
1230
1231
1232
1233
1234
1235
1236
1237
# File 'lib/ipaddress.rb', line 1229

def includes_all(*oths)
  oths.flatten.each do |oth|
    if (!includes(oth))
      return false
    end
  end

  return true
end

#ip_same_kind(oth) ⇒ Object



694
695
696
# File 'lib/ipaddress.rb', line 694

def ip_same_kind(oth)
  return @ip_bits.version == oth.ip_bits.version
end

#ipv4?Boolean

True if the object is an IPv4 address

ip = IPAddress("192.168.10.100/24")

ip.ipv4?
  # -> true

Returns:

  • (Boolean)


172
173
174
# File 'lib/ipaddress.rb', line 172

def ipv4?
  is_ipv4
end

#ipv6?Boolean

True if the object is an IPv6 address

ip = IPAddress("192.168.10.100/24")

ip.ipv6?
  # -> false

Returns:

  • (Boolean)


187
188
189
# File 'lib/ipaddress.rb', line 187

def ipv6?
  is_ipv6
end

#is_ipv4Object



176
177
178
# File 'lib/ipaddress.rb', line 176

def is_ipv4()
  return @ip_bits.version == IpVersion::V4
end

#is_ipv6Object



191
192
193
# File 'lib/ipaddress.rb', line 191

def is_ipv6()
  return @ip_bits.version == IpVersion::V6
end

#is_loopbackObject



718
719
720
# File 'lib/ipaddress.rb', line 718

def is_loopback()
  return (@vt_is_loopback).call(self)
end

#is_mappedObject



730
731
732
733
734
# File 'lib/ipaddress.rb', line 730

def is_mapped()
  ret = !@mapped.nil? &&
    @host_address.shr(32).eq(Crunchy.one().shl(16).sub(Crunchy.one()))
  return ret
end

#is_networkObject



952
953
954
955
# File 'lib/ipaddress.rb', line 952

def is_network()
  return @prefix.num != @ip_bits.bits &&
    @host_address.eq(network().host_address)
end

#is_privateObject



1252
1253
1254
# File 'lib/ipaddress.rb', line 1252

def is_private()
  return @vt_is_private.call(self)
end

#is_same_kind(oth) ⇒ Object



1183
1184
1185
1186
# File 'lib/ipaddress.rb', line 1183

def is_same_kind(oth)
  return is_ipv4() == oth.is_ipv4() &&
    is_ipv6() == oth.is_ipv6()
end

#is_unspecifiedObject



706
707
708
# File 'lib/ipaddress.rb', line 706

def is_unspecified()
  return @host_address.eq(Crunchy.zero())
end

#lastObject

Like its sibling method IPv4# first, @method

returns a new IPv4 object with the
last host IP address in the range.

Example: given the 192.168.100.0/24 network, the last
host IP address is 192.168.100.254

  ip = IPAddress("192.168.100.0/24")

  ip.last.to_s
    #  => "192.168.100.254"

The object IP doesn't need to be a network: the method
automatically gets the network number from it

  ip = IPAddress("192.168.100.50/24")

  ip.last.to_s
    #  => "192.168.100.254"


1061
1062
1063
# File 'lib/ipaddress.rb', line 1061

def last()
  return from(broadcast().host_address.sub(@ip_bits.host_ofs), @prefix)
end

#loopback?Boolean

Returns true if the address is a loopback address

See IPAddress.IPv6.Loopback for more information

Returns:

  • (Boolean)


714
715
716
# File 'lib/ipaddress.rb', line 714

def loopback?
  is_loopback
end

#lt(oth) ⇒ Object



50
51
52
# File 'lib/ipaddress.rb', line 50

def lt(oth)
  return cmp(oth) == -1
end

#lte(oth) ⇒ Object



54
55
56
# File 'lib/ipaddress.rb', line 54

def lte(oth)
  return cmp(oth) <= 0
end

#mapped?Boolean

Returns true if the address is a mapped address

See IPAddress.IPv6.Mapped for more information

Returns:

  • (Boolean)


726
727
728
# File 'lib/ipaddress.rb', line 726

def mapped?
  is_mapped
end

#ne(other) ⇒ Object



93
94
95
# File 'lib/ipaddress.rb', line 93

def ne(other)
  return !eq(other)
end

#netmaskObject



920
921
922
# File 'lib/ipaddress.rb', line 920

def netmask()
  return from(@prefix.netmask(), @prefix)
end

#networkObject

Returns a new IPv4 object with the network number

for the given IP.

  ip = IPAddress("172.16.10.64/24")

  ip.network.to_s
    #  => "172.16.10.0"


965
966
967
# File 'lib/ipaddress.rb', line 965

def network()
  return from(IPAddress.to_network(@host_address, @prefix.host_prefix()), @prefix)
end

#network?Boolean

Checks if the IP address is actually a network

ip = IPAddress("172.16.10.64/24")

ip.network?
  #  => false

ip = IPAddress("172.16.10.64/26")

ip.network?
  #  => true

Returns:

  • (Boolean)


948
949
950
# File 'lib/ipaddress.rb', line 948

def network?
  is_network
end

#newprefix(num) ⇒ Object



1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
# File 'lib/ipaddress.rb', line 1422

def newprefix(num)
  i = num
  while i < @ip_bits.bits
    a = Math.log2(i).to_i
    if (a == Math.log2(i))
      return @prefix.add(a)
    end

    i += 1
  end

  return nil
end

#partsObject



464
465
466
# File 'lib/ipaddress.rb', line 464

def parts()
  return @ip_bits.parts(@host_address)
end

#parts_hex_strObject



468
469
470
471
472
473
474
475
476
# File 'lib/ipaddress.rb', line 468

def parts_hex_str()
  ret = []
  leading = 1 << @ip_bits.part_bits
  self.parts().each do |i|
    ret.push((leading + i).toString(16).slice(1))
  end

  return ret
end

#private?Boolean

Checks if an IPv4 address objects belongs

to a private network RFC1918

Example:

  ip = IPAddress "10.1.1.1/24"
  ip.private?
    #  => true

Returns:

  • (Boolean)


1248
1249
1250
# File 'lib/ipaddress.rb', line 1248

def private?
  is_private
end

#sizeObject

Returns the number of IP addresses included

in the network. It also counts the network
address and the broadcast address.

  ip = IPAddress("10.0.0.1/29")

  ip.size
    #  => 8


1179
1180
1181
# File 'lib/ipaddress.rb', line 1179

def size()
  return Crunchy.one().shl(@prefix.host_prefix())
end

#split(subnets) ⇒ Object



1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
# File 'lib/ipaddress.rb', line 1310

def split(subnets)
  if (subnets == 0 || (1 << @prefix.host_prefix()) <= subnets)
    return nil
  end

  networks = subnet(newprefix(subnets).num)
  if (!networks)
    return networks
  end

  net = networks
  while (net.length != subnets)
    net = sum_first_found(net)
  end

  return net
end

#sub(other) ⇒ Object



973
974
975
976
977
978
979
# File 'lib/ipaddress.rb', line 973

def sub(other)
  if (@host_address.gt(other.host_address))
    return @host_address.clone().sub(other.host_address)
  end

  return other.host_address.clone().sub(@host_address)
end

#subnet(subprefix) ⇒ Object

This method implements the subnetting function

similar to the one described in RFC3531.

By specifying a new prefix, the method calculates
the network number for the given IPv4 object
and calculates the subnets associated to the new
prefix.

For example, given the following network:

  ip = IPAddress "172.16.10.0/24"

we can calculate the subnets with a /26 prefix

  ip.subnets(26).map(:to_string)
    #  => ["172.16.10.0/26", "172.16.10.64/26",
         "172.16.10.128/26", "172.16.10.192/26"]

The resulting number of subnets will of course always be
a power of two.


1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
# File 'lib/ipaddress.rb', line 1390

def subnet(subprefix)
  if (subprefix < @prefix.num || @ip_bits.bits < subprefix)
    return nil
  end

  ret = []
  net = network()
  net.prefix = net.prefix.from(subprefix)
  (1 << (subprefix - @prefix.num)).times do
    ret.push(net.clone())
    net = net.from(net.host_address, net.prefix)
    size = net.size()
    net.host_address = net.host_address.add(size)
  end

  return ret
end

#sum_first_found(arr) ⇒ Object

Splits a network into different subnets

If the IP Address is a network, it can be divided into
multiple networks. If +self+ is not a network, this
method will calculate the network from the IP and then
subnet it.

If +subnets+ is an power of two number, the resulting
networks will be divided evenly from the supernet.

  network = IPAddress("172.16.10.0/24")

  network / 4   #   implies map{|i| i.to_string}
    #  => ["172.16.10.0/26",
         "172.16.10.64/26",
         "172.16.10.128/26",
         "172.16.10.192/26"]

If +num+ is any other number, the supernet will be
divided into some networks with a even number of hosts and
other networks with the remaining addresses.

  network = IPAddress("172.16.10.0/24")

  network / 3   #   implies map{|i| i.to_string}
    #  => ["172.16.10.0/26",
         "172.16.10.64/26",
         "172.16.10.128/25"]

Returns an array of IPv4 objects


1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
# File 'lib/ipaddress.rb', line 1287

def sum_first_found(arr)
  dup = arr.clone()
  if (dup.length < 2)
    return dup
  end

  i = dup.length - 2
  while i >= 0
    #  console.log("sum_first_found:", dup[i], dup[i + 1])
    a = IPAddress.summarize([dup[i], dup[i + 1]])
    #  println!("dup:{}:{}:{}", dup.length, i, a.length)
    if (a.length == 1)
      dup[i] = a[0]
      dup.delete_at(i + 1)
      break
    end

    i -= 1
  end

  return dup
end

#supernet(new_prefix) ⇒ Object

Returns a new IPv4 object from the supernetting

of the instance network.

Supernetting is similar to subnetting, except
that you getting as a result a network with a
smaller prefix (bigger host space). For example,
given the network

  ip = IPAddress("172.16.10.0/24")

you can supernet it with a new /23 prefix

  ip.supernet(23).to_string
    #  => "172.16.10.0/23"

However if you supernet it with a /22 prefix, the
network address will change:

  ip.supernet(22).to_string
    #  => "172.16.8.0/22"

If +new_prefix+ is less than 1, returns 0.0.0.0/0


1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
# File 'lib/ipaddress.rb', line 1353

def supernet(new_prefix)
  if (new_prefix >= @prefix.num)
    return nil
  end

  if new_prefix < 0
    new_prefix = 0
  end

  #  new_ip = @host_address.clone()
  #  for _ in [email protected] {
  #      new_ip = new_ip << 1
  #  }
  return from(@host_address, @prefix.from(new_prefix)).network()
end

#to_hexObject



916
917
918
# File 'lib/ipaddress.rb', line 916

def to_hex()
  return @host_address.toString(16)
end

#to_ipv6Object

Return the ip address in a format compatible

with the IPv6 Mapped IPv4 addresses

Example:

  ip = IPAddress("172.16.10.1/24")

  ip.to_ipv6
    #  => "ac10:0a01"


1418
1419
1420
# File 'lib/ipaddress.rb', line 1418

def to_ipv6()
  return @vt_to_ipv6.call(self)
end

#to_sObject



862
863
864
# File 'lib/ipaddress.rb', line 862

def to_s()
  return @ip_bits.as_compressed_string(@host_address)
end

#to_s_mappedObject



878
879
880
881
882
883
884
# File 'lib/ipaddress.rb', line 878

def to_s_mapped()
  if (is_mapped())
    return "::ffff:#{@mapped.to_s()}"
  end

  return to_s()
end

#to_s_uncompressedObject



874
875
876
# File 'lib/ipaddress.rb', line 874

def to_s_uncompressed()
  return @ip_bits.as_uncompressed_string(@host_address)
end

#to_stringObject

Returns a string with the IP address in canonical

form.

  ip = IPAddress("172.16.100.4/22")

  ip.to_string
    #  => "172.16.100.4/22"


854
855
856
857
858
859
860
# File 'lib/ipaddress.rb', line 854

def to_string()
  ret = ""
  ret += to_s()
  ret += "/"
  ret += @prefix.to_s()
  return ret
end

#to_string_mappedObject



886
887
888
889
890
891
892
893
# File 'lib/ipaddress.rb', line 886

def to_string_mapped()
  if (is_mapped())
    mapped = @mapped.clone()
    return "#{to_s_mapped()}/#{mapped.prefix.num}"
  end

  return to_string()
end

#to_string_uncompressedObject



866
867
868
869
870
871
872
# File 'lib/ipaddress.rb', line 866

def to_string_uncompressed()
  ret = ""
  ret += to_s_uncompressed()
  ret += "/"
  ret += @prefix.to_s()
  return ret
end

#unspecified?Boolean

Returns true if the address is an unspecified address

See IPAddress.IPv6.Unspecified for more information

Returns:

  • (Boolean)


702
703
704
# File 'lib/ipaddress.rb', line 702

def unspecified?
  is_unspecified
end