File Coverage

blib/lib/Net/IPAM/Block.pm
Criterion Covered Total %
statement 146 155 94.1
branch 71 84 84.5
condition 17 18 94.4
subroutine 32 34 94.1
pod 21 21 100.0
total 287 312 91.9


line stmt bran cond sub pod time code
1             package Net::IPAM::Block;
2              
3             our $VERSION = '6.01';
4              
5 13     13   686477 use 5.10.0;
  13         129  
6 13     13   59 use strict;
  13         19  
  13         217  
7 13     13   60 use warnings;
  13         26  
  13         387  
8 13     13   6588 use utf8;
  13         186  
  13         59  
9              
10 13     13   354 use Carp ();
  13         20  
  13         164  
11 13     13   54 use List::Util ();
  13         22  
  13         133  
12 13     13   5385 use Net::IPAM::IP ();
  13         115110  
  13         273  
13 13     13   72 use Net::IPAM::Util ();
  13         21  
  13         225  
14 13     13   5419 use Net::IPAM::Block::Private ();
  13         41  
  13         354  
15              
16 13     13   76 use Exporter 'import';
  13         25  
  13         22532  
17             our @EXPORT_OK = qw(sort_block merge aggregate);
18              
19             =head1 NAME
20              
21             Net::IPAM::Block - A library for reading, formatting, sorting, ... and much more for IP-blocks.
22              
23             =head1 SYNOPSIS
24              
25             use Net::IPAM::Block;
26              
27             # parse and normalize
28             $cidr = Net::IPAM::Block->new('10.0.0.0/255.0.0.0') // die 'wrong format,';
29             $cidr = Net::IPAM::Block->new('10.0.0.0/8') // die 'wrong format,';
30             $range = Net::IPAM::Block->new('fe80::2-fe80::e') // die 'wrong format,';
31             $host = Net::IPAM::Block->new('2001:db8::1') // die 'wrong format,';
32              
33             =head1 DESCRIPTION
34              
35             A block is an IP-network or IP-range, e.g.
36              
37             192.168.0.1/255.255.255.0 # network, with IP mask
38             192.168.0.1/24 # network, with CIDR mask
39             ::1/128 # network, with CIDR mask
40             10.0.0.3-10.0.17.134 # range
41             2001:db8::1-2001:db8::f6 # range
42              
43             The parsed block is represented as an object with:
44              
45             base
46             last
47              
48             This representation is fast sortable without conversions to/from the different IP versions.
49              
50             =head1 METHODS
51              
52             =head2 new
53              
54             $b = Net::IPAM::Block->new('fe80::/10');
55              
56             new() parses the input as CIDR, range or address (or IP object, see below) and returns the Net::IPAM::Block object.
57              
58             Example for valid input strings:
59              
60             2001:db8:dead::/38
61             10.0.0.0/8
62             10.0.0.0/255.0.0.0
63              
64             2001:db8::1-2001:db8::ff00:35
65             192.168.2.3-192.168.7.255
66              
67             If a begin-end range can be represented as a CIDR, new() calculates the netmask and returns the range as CIDR block with a proper mask.
68              
69             Plain IP addresses as input strings or objects are converted to /32 or /128 CIDRs.
70              
71             0.0.0.0 => 0.0.0.0/32
72             ::ffff:127.0.0.1 => 127.0.0.1/32
73             :: => ::/128
74             Net::IPAM::IP->new('1.2.3.4') => 1.2.3.4/32
75              
76              
77             $range = Net::IPAM::Block->new('10.2.0.17-10.3.67.255') // die 'wrong block format,';
78             $range = Net::IPAM::Block->new('fe80::-fe80::1234') // die 'wrong block format,';
79              
80             $cidr_24 = Net::IPAM::Block->new('10.0.0.0/24') // die 'wrong block format,';
81             $cidr_32 = Net::IPAM::Block->new('192.168.0.1') // die 'wrong block format,';
82             $cidr_128 = Net::IPAM::Block->new('2001:db8::1') // die 'wrong block format,';
83              
84             $cidr_128 = Net::IPAM::Block->new( Net::IPAM::IP->new('2001:db8::1') // die 'wrong IP format,' );
85              
86             Returns undef on illegal input.
87              
88             =cut
89              
90             sub new {
91 380     380 1 57951 my $self = bless( {}, $_[0] );
92 380   66     2268 my $input = $_[1] // Carp::croak 'missing argument';
93              
94 378 100       629 return Net::IPAM::Block::Private::_fromIP( $self, $input ) if ref $input;
95              
96             # handle mask: 2001:db8::/32, 10.0.0.0/8, 10.0.0.0/255.0.0.0
97 374         613 my $idx = index( $input, '/' );
98 374 100       837 return Net::IPAM::Block::Private::_fromMask( $self, $input, $idx )
99             if $idx >= 0;
100              
101             # handle range: 192.168.1.17-192.168.1.35
102 146         195 $idx = index( $input, '-' );
103 146 100       326 return Net::IPAM::Block::Private::_fromRange( $self, $input, $idx )
104             if $idx >= 0;
105              
106             # handle address: fe80::1
107 65         141 return Net::IPAM::Block::Private::_fromAddr( $self, $input );
108             }
109              
110             =head2 version
111              
112             $v = $b->version
113              
114             Returns 4 or 6.
115              
116             =cut
117              
118             # just return the version from the base IP
119             sub version {
120 253     253 1 903 return $_[0]->{base}->version;
121             }
122              
123             =head2 to_string
124              
125             Returns the block in canonical form.
126              
127             say Net::IPAM::Block->new('fe80::aa/10')->to_string; # fe80::/10
128             say Net::IPAM::Block->new('1.2.3.4-1.2.3.36')->to_string; # 1.2.3.4-1.2.3.36
129             say Net::IPAM::Block->new('127.0.0.1')->to_string; # 127.0.0.1/32
130              
131             Stringification is overloaded with L
132              
133             my $b = Net::IPAM::Block->new('fe80::/10');
134             say $b; # fe80::/10
135              
136             =cut
137              
138             sub to_string {
139 182     182 1 608 my $self = shift;
140              
141 182 100       272 if ( $self->is_cidr ) {
142             return $self->{base}->to_string . '/'
143 156         330 . Net::IPAM::Block::Private::_common_prefix( $self->{base}->bytes, $self->{last}->bytes );
144             }
145              
146 26         75 return $self->{base}->to_string . '-' . $self->{last}->to_string;
147             }
148              
149             =head2 TO_JSON
150              
151             helper method for JSON serialization, just calls $block->to_string.
152             See also L.
153              
154             =cut
155              
156             sub TO_JSON {
157 0     0 1 0 $_[0]->to_string;
158             }
159              
160             =head2 is_cidr
161              
162             $b->is_cidr
163              
164             Returns true if the block is a CIDR.
165              
166             Net::IPAM::Block->new('fe80::aa/10')->is_cidr # true
167             Net::IPAM::Block->new('1.2.3.1-1.2.3.2')->is_cidr # false
168              
169             =cut
170              
171             sub is_cidr {
172 226     226 1 420 return Net::IPAM::Block::Private::_is_cidr( $_[0] );
173             }
174              
175             =head2 cidrsplit
176              
177             @cidrs = $b->cidrsplit
178              
179             Returns the next 2 cidrs splitted from block.
180              
181             Net::IPAM::Block->new('0.0.0.0/7')->cidrsplit # 0.0.0.0/8 1.0.0.0/8
182             Net::IPAM::Block->new('fe80::/12')->cidrsplit # fe80::/13 fe88::/13
183              
184             Returns undef if cidr mask is at maximum or if block is no CIDR.
185              
186             =cut
187              
188             sub cidrsplit {
189 15     15 1 23 my $self = shift;
190              
191             # return if block is no CIDR)
192 15 100       27 return unless $self->is_cidr;
193              
194 13         17 my $bits = 32;
195 13 100       23 $bits = 128 if $self->version == 6;
196              
197             # get the number of '1' in mask: 11111111_11111111_1110000_0000 => 19
198 13         55 my $n = Net::IPAM::Block::Private::_common_prefix( $self->{base}->bytes, $self->{last}->bytes );
199              
200             # can't split, n == maxbits (32 or 128)
201 13 100       36 return if $n == $bits;
202              
203             # make next mask, e.g. /19 -> /20
204             # 11111111_11111111_1110000_0000
205             # 11111111_11111111_1111000_0000
206 9         19 my $next_mask_n = Net::IPAM::Block::Private::_make_mask_n( $n + 1, $bits );
207              
208             # get original base_n from block
209 9         33 my $base_n = $self->{base}->bytes;
210              
211             # make new base and last with new mask
212 9         32 my $base1_n = Net::IPAM::Block::Private::_make_base_n( $base_n, $next_mask_n );
213 9         16 my $last1_n = Net::IPAM::Block::Private::_make_last_n( $base_n, $next_mask_n );
214              
215             # make next base by incrementing last
216 9         25 my $base2_n = Net::IPAM::Util::incr_n($last1_n);
217 9         160 my $last2_n = Net::IPAM::Block::Private::_make_last_n( $base2_n, $next_mask_n );
218              
219             # make new cidr blocks
220 9         17 my $cidr1 = bless( {}, ref $self );
221 9         23 my $cidr2 = bless( {}, ref $self );
222              
223 9         21 $cidr1->{base} = Net::IPAM::IP->new_from_bytes($base1_n);
224 9         99 $cidr1->{last} = Net::IPAM::IP->new_from_bytes($last1_n);
225              
226 9         89 $cidr2->{base} = Net::IPAM::IP->new_from_bytes($base2_n);
227 9         116 $cidr2->{last} = Net::IPAM::IP->new_from_bytes($last2_n);
228              
229 9 100       108 return wantarray ? ( $cidr1, $cidr2 ) : [ $cidr1, $cidr2 ];
230             }
231              
232             =head2 to_cidrs
233              
234             @cidrs = $b->to_cidrs
235              
236             Returns a list of Net::IPAM::Block objects as true CIDRs, representing the range.
237              
238             Net::IPAM::Block->new('17.0.0.1-17.0.0.2')->to_cidrs # 17.0.0.1/32 17.0.0.2/32
239             Net::IPAM::Block->new('fe80::aa-fe80::ac')->to_cidrs # fe80::aa/127 fe80::ac/128
240             Net::IPAM::Block->new('1.2.3.0-1.2.3.101')->to_cidrs # 1.2.3.0/26 1.2.3.64/27 1.2.3.96/30 1.2.3.100/31
241              
242             If the range is a CIDR, just returns the CIDR:
243              
244             Net::IPAM::Block->new('10.0.0.0/8')->to_cidrs # 10.0.0.0/8
245             Net::IPAM::Block->new('::1')->to_cidrs # ::1/128
246              
247             =cut
248              
249             sub to_cidrs {
250 10     10 1 336 my $cidrs = Net::IPAM::Block::Private::_to_cidrs_iter( $_[0] );
251 10 100       44 return wantarray ? @$cidrs : $cidrs;
252             }
253              
254             =head2 base
255              
256             $ip = $b->base
257              
258             Returns the base IP, as Net::IPAM::IP object.
259              
260             $b = Net::IPAM::Block->new('fe80::ffff/10');
261             say $b->base; # fe80::/10
262              
263             =cut
264              
265             # just return the base slot
266             sub base {
267 2     2 1 9 return $_[0]->{base};
268             }
269              
270             =head2 last
271              
272             $ip = $b->last
273              
274             Returns the last IP, as Net::IPAM::IP object.
275              
276             $b = Net::IPAM::Block->new('10.0.0.0/30')
277             say $b->last; # 10.0.0.3
278              
279             =cut
280              
281             # just return the last slot
282             sub last {
283 2     2 1 480 return $_[0]->{last};
284             }
285              
286             =head2 mask
287              
288             $ip = $b->mask
289              
290             Returns the netmask as Net::IPAM::IP object.
291              
292             $b = Net::IPAM::Block->new('10.0.0.0/24')
293             say $b->mask if defined $b->mask; # 255.255.255.0
294              
295             The mask is only defined for real CIDR blocks.
296              
297             Example:
298              
299             1.2.3.4 => mask is /32 = 255.255.255.255
300             ::1 => mask is /128 = ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
301              
302             10.0.0.0-10.0.0.15 => mask is /28 = 255.255.255.240
303             ::-::f => mask is /124 = ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0
304              
305             10.0.0.0/24 => mask is /24 = 255.255.255.0
306             fe80::/10 => mask is /10 = ffc0:0000:0000:0000:0000:0000:0000:0000
307              
308             10.0.0.0-10.0.0.13 => mask is undef
309             ::-::5 => mask is undef
310              
311             =cut
312              
313             # calc the mask as IP, returns undef if not a CIDR
314             sub mask {
315 2     2 1 486 my $self = shift;
316 2 100       6 return unless $self->is_cidr;
317              
318 1         4 my $mask_n = Net::IPAM::Block::Private::_get_mask_n( $self->{base}, $self->{last} );
319 1         5 return Net::IPAM::IP->new_from_bytes($mask_n);
320             }
321              
322             =head2 hostmask
323              
324             $ip = $b->hostmask
325              
326             Returns the hostmask as Net::IPAM::IP object.
327              
328             $b = Net::IPAM::Block->new('10.0.0.0/24')
329             say $b->mask; # 255.255.255.0
330             say $b->hostmask; # 0.0.0.255
331              
332             $b = Net::IPAM::Block->new('fe80::/10')
333             say $b->mask; # ffc0::
334             say $b->hostmask; # 3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff
335              
336             The hostmask is only defined for real CIDR blocks.
337              
338             =cut
339              
340             sub hostmask {
341 2     2 1 269 my $self = shift;
342 2 100       5 return unless $self->is_cidr;
343              
344 1         3 my $mask_n = Net::IPAM::Block::Private::_get_mask_n( $self->{base}, $self->{last} );
345 1         4 return Net::IPAM::IP->new_from_bytes( ~$mask_n );
346             }
347              
348             =head2 bitlen
349              
350             C<< bitlen >> returns the minimum number of bits to represent a range from base to last
351              
352             $n = $b->bitlen
353              
354             obvious for CIDR blocks:
355              
356             $b = Net::IPAM::Block->new('10.0.0.0/24')
357             say $b->bitlen; # 32 - 24 = 8 bit
358              
359             $b = Net::IPAM::Block->new('::/0');
360             say $b->bitlen; # 128 - 0 = 128 bit
361              
362             not so obvious for ranges:
363              
364             $b = Net::IPAM::Block->new('2001:db8::affe-2001:db8::cafe');
365             say $b->bitlen; # 15 bit (at least)
366              
367             =cut
368              
369             sub bitlen {
370 4     4 1 14 my $self = shift;
371              
372 4         5 my $bits = 32;
373 4 100       8 $bits = 128 if $self->version == 6;
374              
375 4         17 return $bits - Net::IPAM::Block::Private::_common_prefix( $self->{base}->bytes, $self->{last}->bytes );
376             }
377              
378             =head2 iter
379              
380             C<< iter >> returns the next IP in block, starting with base and stopping at last. Returns undef after last.
381              
382             $b = Net::IPAM::Block->new('2001:db8::affe-2001:db8::cafe');
383             while ( my $ip = $b->iter ) {
384             say $ip;
385             }
386              
387             OUTPUT:
388              
389             2001:db8::affe
390             2001:db8::afff
391             2001:db8::b000
392             2001:db8::b001
393             ...
394             2001:db8::cafb
395             2001:db8::cafc
396             2001:db8::cafd
397             2001:db8::cafe
398              
399             =cut
400              
401             sub iter {
402 0     0 1 0 my $self = shift;
403              
404             # init
405 0 0       0 unless ( defined $self->{iter} ) {
406              
407             # initialize state
408 0         0 return $self->{iter} = $self->{base};
409             }
410              
411             #next
412 0 0       0 if ( $self->{iter}->cmp( $self->{last} ) < 0 ) {
413 0         0 return $self->{iter} = $self->{iter}->incr;
414             }
415              
416             # over
417 0         0 return;
418             }
419              
420             =head2 cmp
421              
422             $a->cmp($b)
423              
424             Compares two IP blocks:
425              
426             print $this->cmp($other);
427             @sorted_blocks = sort { $a->cmp($b) } @unsorted_blocks;
428              
429             cmp() returns -1, 0, +1:
430              
431             0 if $a == $b,
432              
433             -1 if $a is v4 and $b is v6
434             +1 if $a is v6 and $b is v4
435              
436             -1 if $a->base < $b->base
437             +1 if $a->base > $b->base
438              
439             -1 if $a->base == $b->base and $a->last > $b->last # $a is super-set of $b
440             +1 if $a->base == $b->base and $a->last < $b->last # $a is sub-set of $b
441              
442             =cut
443              
444             sub cmp {
445             return ( $_[0]->{base}->cmp( $_[1]->{base} ) )
446 545   100 545 1 1169 || ( $_[1]->{last}->cmp( $_[0]->{last} ) );
447             }
448              
449             =head2 is_disjunct_with
450              
451             $a->is_disjunct_with($b)
452              
453             Returns true if the blocks are disjunct
454              
455             a |----------|
456             b |---|
457              
458             a |------|
459             b |---|
460              
461             print "a and b are disjunct" if $a->is_disjunct_with($b)
462              
463             =cut
464              
465             sub is_disjunct_with {
466              
467             # a |----------|
468             # b |---|
469 194 100   194 1 340 return 1 if $_[0]->{base}->cmp( $_[1]->{last} ) == 1;
470              
471             # a |---|
472             # b |----------|
473 188 100       614 return 1 if $_[0]->{last}->cmp( $_[1]->{base} ) == -1;
474              
475 116         374 return;
476             }
477              
478             =head2 overlaps_with
479              
480             $a->overlaps_with($b)
481              
482             Returns true if the blocks overlap.
483              
484             a |-------|
485             b |------|
486            
487             a |------|
488             b |-------|
489            
490             a |----|
491             b |---------|
492            
493             a |---------|
494             b |----|
495              
496             =cut
497              
498             sub overlaps_with {
499              
500             # false if a == b
501 123 100   123 1 204 return if $_[0]->cmp( $_[1] ) == 0;
502              
503             # false if a contains b or vice versa
504 119 100 100     461 return if $_[0]->contains( $_[1] ) || $_[1]->contains( $_[0] );
505              
506             # false if a is_disjunct_with b
507 63 100       312 return if $_[0]->is_disjunct_with( $_[1] );
508              
509 5         10 return 1;
510             }
511              
512             =head2 contains
513              
514             $a->contains($b)
515              
516             Returns true if block a contains block b. a and b may NOT coincide.
517              
518             if ( $a->contains($b) ) {
519             print "block a contains block b\n";
520             }
521              
522             a |-----------------| |-----------------| |-----------------|
523             b |------------| |------------| |------------|
524              
525             The argument may also be a Net::IPAM::IP address object.
526              
527             if ( $a->contains($ip) ) {
528             print "block a contains ip\n";
529             }
530              
531             =cut
532              
533             # polymorphic: arg may be block or ip
534             sub contains {
535 259 100 100 259 1 1492 if ( ref $_[1] && $_[1]->isa('Net::IPAM::IP') ) {
536 8         18 return Net::IPAM::Block::Private::_contains_ip(@_);
537             }
538              
539 251 100 100     691 if ( ref $_[1] && $_[1]->isa('Net::IPAM::Block') ) {
540 249         439 return Net::IPAM::Block::Private::_contains_block(@_);
541             }
542              
543 2         197 Carp::croak 'wrong argument,';
544             }
545              
546             =head2 diff
547              
548             @diff = $outer->diff(@inner)
549              
550             Returns all blocks in outer block, minus the inner blocks.
551              
552             my $outer = Net::IPAM::Block->new("192.168.2.0/24");
553             my @inner = (
554             Net::IPAM::Block->new("192.168.2.0/26"),
555             Net::IPAM::Block->new("192.168.2.240-192.168.2.249"),
556             );
557              
558             my @diff = $outer->diff(@inner);
559              
560             # diff: [192.168.2.64-192.168.2.239, 192.168.2.250-192.168.2.255]
561              
562             =cut
563              
564             sub diff {
565 17     17 1 770 my $self = shift;
566 17         33 my $outer = Net::IPAM::Block::Private::_clone($self);
567              
568             # no inner blocks, just return outer block
569 17 100       37 unless (@_) {
570 1 50       9 return wantarray ? ($outer) : [$outer];
571             }
572              
573 16         17 my @result;
574              
575             # blocks must be sorted for this algo
576 16         32 LOOP: foreach my $b ( sort_block(@_) ) {
577              
578             SWITCH: {
579             # no-op
580 64 100       170 next LOOP if $outer->is_disjunct_with($b);
  64         91  
581              
582             # masks rest
583 58 100       75 if ( $outer->cmp($b) == 0 ) {
584 4 100       49 return wantarray ? @result : \@result;
585             }
586              
587             # masks rest
588 54 100       278 if ( $b->contains($outer) ) {
589 2 50       26 return wantarray ? @result : \@result;
590             }
591              
592             # move cursor forward
593 52 100       310 if ( $outer->{base}->cmp( $b->{base} ) >= 0 ) {
594 36         108 $outer->{base} = $b->{last}->incr;
595 36         1121 last SWITCH;
596             }
597              
598             # save diff, move cursor forward
599 16 50       52 if ( $outer->{base}->cmp( $b->{base} ) < 0 ) {
600              
601 16         50 my $block = bless {}, ref $outer;
602 16         28 $block->{base} = $outer->{base};
603 16         23 $block->{last} = $b->{base}->decr;
604              
605 16         420 push @result, $block;
606              
607 16         27 $outer->{base} = $b->{last}->incr;
608 16         374 last SWITCH;
609             }
610              
611 0         0 die "logic error: rest=$outer, topic: $b,";
612              
613             } # end of SWITCH
614              
615             # overflow from last incr
616 52 50       104 if ( not defined $outer->{base} ) {
617 0 0       0 return wantarray ? @result : \@result;
618             }
619              
620             # cursor moved behind last
621 52 100       89 if ( $outer->{base}->cmp( $outer->{last} ) > 0 ) {
622 2 50       15 return wantarray ? @result : \@result;
623             }
624              
625             } # end of LOOP
626              
627             # save the rest
628 8         35 push @result, $outer;
629              
630 8 100       35 return wantarray ? @result : \@result;
631             }
632              
633             =head1 FUNCTIONS
634              
635             =head2 sort_block
636              
637             use Net::IPAM::Block 'sort_block';
638              
639             @sorted_blocks = sort_block @unsorted_blocks;
640              
641             Faster sort implemention (Schwartzian transform) as explcit sort function:
642              
643             @sorted_blocks = sort { $a->cmp($b) } @unsorted_blocks;
644              
645             =cut
646              
647             # see also cmp()
648              
649             sub sort_block {
650 212         321 return map { $_->[0] }
651 556 50       1668 sort { $a->[1]->cmp( $b->[1] ) || $b->[2]->cmp( $a->[2] ) }
652 29     29 1 992 map { [ $_, $_->{base}, $_->{last} ] } @_;
  212         381  
653             }
654              
655             =head2 merge
656              
657              
658             use Net::IPAM::Block 'merge';
659              
660             @merged = merge(@blocks)
661              
662             Returns the minimal number of blocks spanning the range of input blocks.
663              
664             If CIDRs are required, use the following idiom:
665              
666             @cidrs = map { $_->to_cidrs } merge(@blocks);
667              
668             =cut
669              
670             sub merge {
671 13 100   13 1 174 return @_ if @_ <= 1;
672              
673             # sort blocks
674 12         23 my @sorted = sort_block(@_);
675              
676             # start with first [0] block as prev, see below
677 12         304 my @result = ( shift @sorted );
678              
679             # [1, ..
680 12         19 for (@sorted) {
681              
682             # skip rubbish
683 105 50       147 next unless defined $_;
684              
685 105         115 my $prev = $result[-1];
686              
687             # expand prev
688 105 100       146 if ( $prev->overlaps_with($_) ) {
689 3         4 $prev->{last} = $_->{last};
690 3         6 next;
691             }
692              
693             # expand prev
694 102         567 my $next = $prev->{last}->incr;
695 102 100 100     2564 if ( defined $next && $next->cmp( $_->{base} ) == 0 ) {
696 44         159 $prev->{last} = $_->{last};
697 44         79 next;
698             }
699              
700             # append block
701 58 100       173 if ( $prev->is_disjunct_with($_) ) {
702 9         32 push @result, $_;
703 9         16 next;
704             }
705              
706             # skip contains and equals
707             }
708              
709 12 100       45 return wantarray ? @result : [@result];
710             }
711              
712             =head2 aggregate
713              
714             *** DEPRECATED *** use merge in favor of
715              
716             =cut
717              
718             sub aggregate {
719 2     2 1 1414 Carp::carp("DEPRECATED: use merge() in favor of aggregate(),");
720 1         37 return merge(@_);
721             }
722              
723             =head1 OPERATORS
724              
725             L overloads the following operators.
726              
727             =head2 bool
728              
729             my $bool = !!$block;
730              
731             Always true.
732              
733             =head2 stringify
734              
735             my $str = "$block";
736              
737             Alias for L.
738              
739             =cut
740              
741             use overload
742 63     63   3729 '""' => sub { shift->to_string },
743 1     1   3 bool => sub { 1 },
744 13     13   94 fallback => 1;
  13         31  
  13         110  
745              
746             =head1 AUTHOR
747              
748             Karl Gaissmaier, C<< >>
749              
750             =head1 BUGS
751              
752             Please report any bugs or feature requests to C, or through
753             the web interface at L. I will be notified, and then you'll
754             automatically be notified of progress on your bug as I make changes.
755              
756             =head1 SUPPORT
757              
758             You can find documentation for this module with the perldoc command.
759              
760             perldoc Net::IPAM::Block
761              
762              
763             You can also look for information at:
764              
765             =over 4
766              
767             =item * on github
768              
769             TODO
770              
771             =back
772              
773             =head1 SEE ALSO
774              
775             L
776             L
777              
778             =head1 LICENSE AND COPYRIGHT
779              
780             This software is copyright (c) 2020-2021 by Karl Gaissmaier.
781              
782             This is free software; you can redistribute it and/or modify it under
783             the same terms as the Perl 5 programming language system itself.
784              
785             =cut
786              
787             1; # End of Net::IPAM::Block
788              
789             # vim: ts=2 sw=2 sts=2 background=dark