Class: NetAddr::Tree

Inherits:
Object
  • Object
show all
Defined in:
lib/tree.rb

Overview

Tree

A class & series of methods for creating and manipulating IP-based heirarchical trees. Both IPv4 and IPv6 are supported.

A sample tree would look like: 192.168.1.0/24

192.168.1.0/26
   192.168.1.0/27
      192.168.1.0/28
      192.168.1.16/29
         192.168.1.16/30
      192.168.1.24/30
         192.168.1.25/32
      192.168.1.28/30
   192.168.1.32/27
192.168.1.64/26
   192.168.1.64/27
192.168.1.128/26
192.168.1.192/26

Instance Method Summary collapse

Constructor Details

#initializeTree

Synopsis

Create a new Tree object.

Example: NetAddr::Tree.new()

Arguments:

  • none



45
46
47
48
49
# File 'lib/tree.rb', line 45

def initialize()
    # root of our ordered IP tree
    @v4_root = NetAddr::CIDRv4.new(0,0,{:Subnets => []})
    @v6_root = NetAddr::CIDRv6.new(0,0,{:Subnets => []})
end

Instance Method Details

#add!(new) ⇒ Object

Synopsis

Add a CIDR address or NetAddr::CIDR object to the tree. Example: tree.add!(‘192.168.1.0/24’) cidr = NetAddr::CIDR.create(‘192.168.1.0/24’, :Tag => => ‘test net’ tree.add!(cidr)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • nil



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

def add!(new)
    # validate object
    if ( !new.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(new)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    else
        cidr = new.dup
    end

    cidr.tag[:Subnets] = []
    add_to_tree(cidr)

    return(nil)
end

#ancestors(cidr) ⇒ Object

Synopsis

Returns all the ancestors of the provided CIDR addresses.

Example: tree.ancestors(‘192.168.1.0/27’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • Array of NetAddr::CIDR objects



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/tree.rb', line 103

def ancestors(cidr)
    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    list = []
    parent = find_parent(cidr)
    until (!parent.tag[:Parent])
        list.push( NetAddr.cidr_build(parent.version, parent.to_i(:network), parent.to_i(:netmask)) )
        parent = parent.tag[:Parent]
    end

    return(list)
end

#children(cidr) ⇒ Object

Synopsis

Returns all the immediate children of the provided CIDR addresses.

Example: tree.children(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • Array of NetAddr::CIDR objects



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/tree.rb', line 140

def children(cidr)
    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    list = []
    me = find_me(cidr)
    if (me)
        me.tag[:Subnets].each do |child|
            list.push( NetAddr.cidr_build(child.version, child.to_i(:network), child.to_i(:netmask)) )
        end
    end

    return(list)
end

#delete!(cidr) ⇒ Object

Synopsis

Remove the provided CIDR address from the tree.

Example: tree.remove!(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true on success or false on fail



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
# File 'lib/tree.rb', line 218

def delete!(cidr)
    removed = false

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    # find matching
    me = find_me(cidr)

    # remove
    if (me)
        parent = me.tag[:Parent]
        children = me.tag[:Subnets]
        parent.tag[:Subnets].delete(me)
        children.each {|x| add_to_parent(x,parent)}
        removed = true
    end

    return(removed)
end

#descendants(cidr) ⇒ Object

Synopsis

Return all descendants of the provided CIDR address.

Example: tree.descendants(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • Array of NetAddr::CIDR objects



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/tree.rb', line 178

def descendants(cidr)
    list = []

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)
    if (me)
        dump_children(me).each do |x|
            child = x[:CIDR]
            list.push( NetAddr.cidr_build(child.version, child.to_i(:network), child.to_i(:netmask)) )
        end
    end

    return(list)
end

#dumpObject

Synopsis

Dump the contents of this tree.

Example: tree.dump()

Arguments:

  • none

Returns:

  • ordered array of hashes with the following fields:

    :CIDR => NetAddr::CIDR object
    :Depth => (depth level in tree)
    


264
265
266
267
268
269
# File 'lib/tree.rb', line 264

def dump()
    list = dump_children(@v4_root)
    list.concat( dump_children(@v6_root) )
    list.each {|x| x[:CIDR] =  NetAddr.cidr_build(x[:CIDR].version, x[:CIDR].to_i(:network), x[:CIDR].to_i(:netmask)) }
    return(list)
end

#exists?(cidr) ⇒ Boolean

Synopsis

Has a CIDR address already been added to the tree?

Example: tree.exists?(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true or false

Returns:

  • (Boolean)


287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/tree.rb', line 287

def exists?(cidr)
    found = false

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    found = true if (find_me(cidr))
    return(found)
end

#fill_in!(cidr) ⇒ Object

Synopsis

Fill in the missing subnets of a particular CIDR.

Example: tree.fill_in!(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true or false



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/tree.rb', line 320

def fill_in!(cidr)
    filled = false

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)
    if (me && me.tag[:Subnets].length != 0)
        me.tag[:Subnets] = NetAddr.cidr_fill_in(me, me.tag[:Subnets])
        me.tag[:Subnets].each do |subnet|
            subnet.tag[:Subnets] = [] if (!subnet.tag.has_key?(:Subnets))
        end
        filled = true
    end
    return(filled)
end

#find(cidr) ⇒ Object

Synopsis

Find and return a CIDR from within the tree.

Example: tree.find(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • NetAddr::CIDR object, or nil



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/tree.rb', line 360

def find(cidr)
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)
    if (me)
        me =  NetAddr.cidr_build(me.version, me.to_i(:network), me.to_i(:netmask)) 
    end

    return(me)
end

#find_space(options) ⇒ Object

Synopsis

Find subnets that are of at least size X. Only subnets that are not themselves subnetted will be returned. :Subnet takes precedence over :IPCount

Example: tree.find_space(:IPCount => 16)

Arguments:

  • Minimum subnet size in bits, or a Hash with the following keys:

    :Subnet - minimum subnet size in bits for returned subnets
    :IPCount - minimum IP count per subnet required for returned subnets
    :Version - restrict results to IPvX
    

Returns:

  • Array of NetAddr::CIDR objects



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
# File 'lib/tree.rb', line 398

def find_space(options)
    known_args = [:Subnet, :IPCount, :Version]
    version = nil
    if (options.kind_of? Integer)
        bits4 = options
        bits6 = options
    elsif (options.kind_of? Hash) 
        NetAddr.validate_args(options.keys,known_args)
        if (options.has_key?(:Version))
            version = options[:Version]
            raise "IP version should be 4 or 6, but was #{version}." if (version != 4 && version !=6)
        end

        if (options.has_key?(:Subnet))
            bits4 = options[:Subnet]
            bits6 = options[:Subnet]
        elsif(options.has_key?(:IPCount))
            bits4 = NetAddr.minimum_size(options[:IPCount], :Version => 4)
            bits6 = NetAddr.minimum_size(options[:IPCount], :Version => 6)
        else
            raise "Missing arguments: :Subnet/:IPCount"
        end
    else
        raise "Integer or Hash expected, but #{options.class} provided."
    end

    list = []
    if (!version || version == 4)
        dump_children(@v4_root).each do |entry|
            cidr = entry[:CIDR]
            if ( (cidr.tag[:Subnets].length == 0) && (cidr.bits <= bits4) )
                list.push(cidr)
            end
        end
    end

    if (!version || version == 6)
        dump_children(@v6_root).each do |entry|
            cidr = entry[:CIDR]
            if ( (cidr.tag[:Subnets].length == 0) && (cidr.bits <= bits6) )
                list.push(cidr)
            end
        end
    end

    new_list = []
    list.each {|x| new_list.push( NetAddr.cidr_build(x.version, x.to_i(:network), x.to_i(:netmask)) )}

    return(new_list)
end

#longest_match(cidr) ⇒ Object

Synopsis

Find the longest matching branch of our tree to which a CIDR address belongs. Useful for performing ‘routing table’ style lookups.

Example: tree.longest_match(‘192.168.1.1’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • NetAddr::CIDR object



466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/tree.rb', line 466

def longest_match(cidr)
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin                
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    found = find_me(cidr)
    found = find_parent(cidr) if !found

    return( NetAddr.cidr_build(found.version, found.to_i(:network), found.to_i(:netmask)) )
end

#prune!(cidr) ⇒ Object

Synopsis

Remove all subnets of the provided CIDR address.

Example: tree.prune!(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true on success or false on fail



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/tree.rb', line 498

def prune!(cidr)
    pruned = false

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)

    if (me)
        me.tag[:Subnets].clear
        pruned = true
    end

    return(pruned)
end

#remove!(cidr) ⇒ Object

Synopsis

Remove the provided CIDR address, and all of its subnets from the tree.

Example: tree.remove!(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true on success or false on fail



537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/tree.rb', line 537

def remove!(cidr)
    removed = false
    found = nil

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)

    if (me)
        parent = me.tag[:Parent]
        parent.tag[:Subnets].delete(me)
        removed = true
    end

    return(removed)
end

#resize!(cidr, bits) ⇒ Object

Synopsis

Resize the provided CIDR address.

Example: tree.resize!(‘192.168.1.0/24’, 23)

Arguments:

  • CIDR address as a String or an NetAddr::CIDR object

  • Integer representing the bits of the new netmask

Returns:

  • true on success or false on fail



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
# File 'lib/tree.rb', line 579

def resize!(cidr,bits)
    resized = false

    # validate cidr
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)

    if (me)
        new = me.resize(bits)
        delete!(me)
        add!(new)
        resized = true
    end

    return(resized)
end

#root(cidr) ⇒ Object

Synopsis

Returns the root of the provided CIDR address.

Example: tree.root(‘192.168.1.32/27’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • NetAddr::CIDR object



620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/tree.rb', line 620

def root(cidr)
    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    parent = find_parent(cidr)
    if (parent.tag.has_key?(:Parent)) # if parent is not 0/0
        while(1)
            grandparent = parent.tag[:Parent]
            break if (!grandparent.tag.has_key?(:Parent)) # if grandparent is 0/0
            parent = grandparent
        end
    end

    return( NetAddr.cidr_build(parent.version, parent.to_i(:network), parent.to_i(:netmask)) )
end

#showObject

Synopsis

Print the tree as a formatted string.

Example: tree.show()

Arguments:

  • none

Returns:

  • String



659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
# File 'lib/tree.rb', line 659

def show()
    printed = "IPv4 Tree\n---------\n"
    list4 = dump_children(@v4_root)
    list6 = dump_children(@v6_root)

    list4.each do |entry|
        cidr = entry[:CIDR]
        depth = entry[:Depth]

        if (depth == 0)
            indent = ""
        else
            indent = " " * (depth*3)
        end

        printed << "#{indent}#{cidr.desc}\n"
    end

    printed << "\n\nIPv6 Tree\n---------\n" if (list6.length != 0)

   list6.each do |entry|
        cidr = entry[:CIDR]
        depth = entry[:Depth]

        if (depth == 0)
            indent = ""
        else
            indent = " " * (depth*3)
        end

        printed << "#{indent}#{cidr.desc(:Short => true)}\n"
    end

    return(printed)
end

#siblings(cidr) ⇒ Object

Synopsis

Return list of the sibling CIDRs of the provided CIDR address.

Example: tree.siblings(‘192.168.1.0/27’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • Array of NetAddr::CIDR objects



711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
# File 'lib/tree.rb', line 711

def siblings(cidr)
    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    list = []
    find_parent(cidr).tag[:Subnets].each do |entry|
        if (!cidr.cmp(entry))
            list.push( NetAddr.cidr_build(entry.version, entry.to_i(:network), entry.to_i(:netmask)) )
        end
    end

    return(list)
end

#summarize_subnets!(cidr) ⇒ Object Also known as: merge_subnets!

Synopsis

Summarize all subnets of the provided CIDR address. The subnets will be placed under the new summary address within the tree.

Example: tree.summarize_subnets!(‘192.168.1.0/24’)

Arguments:

  • String or NetAddr::CIDR object

Returns:

  • true on success or false on fail



749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
# File 'lib/tree.rb', line 749

def summarize_subnets!(cidr)
    merged = false

    # validate object
    if ( !cidr.kind_of?(NetAddr::CIDR) )
        begin
            cidr = NetAddr::CIDR.create(cidr)
        rescue Exception => error
            raise ArgumentError, "Provided argument raised the following " +
                                 "errors: #{error}"
        end
    end

    me = find_me(cidr)

    if (me)
        merged = NetAddr.cidr_summarize(me.tag[:Subnets])
        me.tag[:Subnets] = merged
        merged = true
    end

    return(merged)
end

#supernetsObject

Synopsis

Return list of the top-level supernets of this tree.

Example: tree.supernets()

Arguments:

  • none

Returns:

  • Array of NetAddr::CIDR objects



790
791
792
793
794
795
# File 'lib/tree.rb', line 790

def supernets()
   supernets = []
   @v4_root.tag[:Subnets].each {|x| supernets.push( NetAddr.cidr_build(x.version, x.to_i(:network), x.to_i(:netmask)) )}
   @v6_root.tag[:Subnets].each {|x| supernets.push( NetAddr.cidr_build(x.version, x.to_i(:network), x.to_i(:netmask)) )}
   return (supernets)
end