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 = '5.10';
4              
5 13     13   632713 use 5.10.0;
  13         125  
6 13     13   61 use strict;
  13         19  
  13         206  
7 13     13   57 use warnings;
  13         27  
  13         369  
8 13     13   6075 use utf8;
  13         151  
  13         56  
9              
10 13     13   313 use Carp ();
  13         16  
  13         139  
11 13     13   45 use List::Util ();
  13         21  
  13         121  
12 13     13   5040 use Net::IPAM::IP ();
  13         102876  
  13         261  
13 13     13   96 use Net::IPAM::Util ();
  13         22  
  13         189  
14 13     13   5015 use Net::IPAM::Block::Private ();
  13         28  
  13         316  
15              
16 13     13   87 use Exporter 'import';
  13         19  
  13         20414  
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 378     378 1 52682 my $self = bless( {}, $_[0] );
92 378   66     968 my $input = $_[1] // Carp::croak 'missing argument';
93              
94 376 100       578 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 372         521 my $idx = index( $input, '/' );
98 372 100       746 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 145         166 $idx = index( $input, '-' );
103 145 100       305 return Net::IPAM::Block::Private::_fromRange( $self, $input, $idx )
104             if $idx >= 0;
105              
106             # handle address: fe80::1
107 65         124 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 272     272 1 905 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 551 my $self = shift;
140              
141 182 100       275 if ( $self->is_cidr ) {
142             return $self->{base}->to_string . '/'
143 156         370 . Net::IPAM::Block::Private::_common_prefix( $self->{base}->bytes, $self->{last}->bytes );
144             }
145              
146 26         67 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 248     248 1 441 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 20 my $self = shift;
190              
191             # return if block is no CIDR)
192 15 100       23 return unless $self->is_cidr;
193              
194 13         18 my $bits = 32;
195 13 100       19 $bits = 128 if $self->version == 6;
196              
197             # get the number of '1' in mask: 11111111_11111111_1110000_0000 => 19
198 13         45 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       29 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         17 my $next_mask_n = Net::IPAM::Block::Private::_make_mask_n( $n + 1, $bits );
207              
208             # get original base_n from block
209 9         23 my $base_n = $self->{base}->bytes;
210              
211             # make new base and last with new mask
212 9         36 my $base1_n = Net::IPAM::Block::Private::_make_base_n( $base_n, $next_mask_n );
213 9         13 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         20 my $base2_n = Net::IPAM::Util::incr_n($last1_n);
217 9         128 my $last2_n = Net::IPAM::Block::Private::_make_last_n( $base2_n, $next_mask_n );
218              
219             # make new cidr blocks
220 9         18 my $cidr1 = bless( {}, ref $self );
221 9         13 my $cidr2 = bless( {}, ref $self );
222              
223 9         13 $cidr1->{base} = Net::IPAM::IP->new_from_bytes($base1_n);
224 9         94 $cidr1->{last} = Net::IPAM::IP->new_from_bytes($last1_n);
225              
226 9         87 $cidr2->{base} = Net::IPAM::IP->new_from_bytes($base2_n);
227 9         81 $cidr2->{last} = Net::IPAM::IP->new_from_bytes($last2_n);
228              
229 9 100       107 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              
251             # rec-descent call, start with empty buf []
252 9     9 1 428 my $cidrs = Net::IPAM::Block::Private::_to_cidrs_rec( $_[0], [] );
253              
254 9 100       29 return wantarray ? @$cidrs : $cidrs;
255             }
256              
257             =head2 base
258              
259             $ip = $b->base
260              
261             Returns the base IP, as Net::IPAM::IP object.
262              
263             $b = Net::IPAM::Block->new('fe80::ffff/10');
264             say $b->base; # fe80::/10
265              
266             =cut
267              
268             # just return the base slot
269             sub base {
270 2     2 1 9 return $_[0]->{base};
271             }
272              
273             =head2 last
274              
275             $ip = $b->last
276              
277             Returns the last IP, as Net::IPAM::IP object.
278              
279             $b = Net::IPAM::Block->new('10.0.0.0/30')
280             say $b->last; # 10.0.0.3
281              
282             =cut
283              
284             # just return the last slot
285             sub last {
286 2     2 1 481 return $_[0]->{last};
287             }
288              
289             =head2 mask
290              
291             $ip = $b->mask
292              
293             Returns the netmask as Net::IPAM::IP object.
294              
295             $b = Net::IPAM::Block->new('10.0.0.0/24')
296             say $b->mask if defined $b->mask; # 255.255.255.0
297              
298             The mask is only defined for real CIDR blocks.
299              
300             Example:
301              
302             1.2.3.4 => mask is /32 = 255.255.255.255
303             ::1 => mask is /128 = ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
304              
305             10.0.0.0-10.0.0.15 => mask is /28 = 255.255.255.240
306             ::-::f => mask is /124 = ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0
307              
308             10.0.0.0/24 => mask is /24 = 255.255.255.0
309             fe80::/10 => mask is /10 = ffc0:0000:0000:0000:0000:0000:0000:0000
310              
311             10.0.0.0-10.0.0.13 => mask is undef
312             ::-::5 => mask is undef
313              
314             =cut
315              
316             # calc the mask as IP, returns undef if not a CIDR
317             sub mask {
318 2     2 1 482 my $self = shift;
319 2 100       4 return unless $self->is_cidr;
320              
321 1         4 my $mask_n = Net::IPAM::Block::Private::_get_mask_n( $self->{base}, $self->{last} );
322 1         4 return Net::IPAM::IP->new_from_bytes($mask_n);
323             }
324              
325             =head2 hostmask
326              
327             $ip = $b->hostmask
328              
329             Returns the hostmask as Net::IPAM::IP object.
330              
331             $b = Net::IPAM::Block->new('10.0.0.0/24')
332             say $b->mask; # 255.255.255.0
333             say $b->hostmask; # 0.0.0.255
334              
335             $b = Net::IPAM::Block->new('fe80::/10')
336             say $b->mask; # ffc0::
337             say $b->hostmask; # 3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff
338              
339             The hostmask is only defined for real CIDR blocks.
340              
341             =cut
342              
343             sub hostmask {
344 2     2 1 270 my $self = shift;
345 2 100       5 return unless $self->is_cidr;
346              
347 1         3 my $mask_n = Net::IPAM::Block::Private::_get_mask_n( $self->{base}, $self->{last} );
348 1         4 return Net::IPAM::IP->new_from_bytes( ~$mask_n );
349             }
350              
351             =head2 bitlen
352              
353             C<< bitlen >> returns the minimum number of bits to represent a range from base to last
354              
355             $n = $b->bitlen
356              
357             obvious for CIDR blocks:
358              
359             $b = Net::IPAM::Block->new('10.0.0.0/24')
360             say $b->bitlen; # 32 - 24 = 8 bit
361              
362             $b = Net::IPAM::Block->new('::/0');
363             say $b->bitlen; # 128 - 0 = 128 bit
364              
365             not so obvious for ranges:
366              
367             $b = Net::IPAM::Block->new('2001:db8::affe-2001:db8::cafe');
368             say $b->bitlen; # 15 bit (at least)
369              
370             =cut
371              
372             sub bitlen {
373 4     4 1 11 my $self = shift;
374              
375 4         6 my $bits = 32;
376 4 100       5 $bits = 128 if $self->version == 6;
377              
378 4         15 return $bits - Net::IPAM::Block::Private::_common_prefix( $self->{base}->bytes, $self->{last}->bytes );
379             }
380              
381             =head2 iter
382              
383             C<< iter >> returns the next IP in block, starting with base and stopping at last. Returns undef after last.
384              
385             $b = Net::IPAM::Block->new('2001:db8::affe-2001:db8::cafe');
386             while ( my $ip = $b->iter ) {
387             say $ip;
388             }
389              
390             OUTPUT:
391              
392             2001:db8::affe
393             2001:db8::afff
394             2001:db8::b000
395             2001:db8::b001
396             ...
397             2001:db8::cafb
398             2001:db8::cafc
399             2001:db8::cafd
400             2001:db8::cafe
401              
402             =cut
403              
404             sub iter {
405 0     0 1 0 my $self = shift;
406              
407             # init
408 0 0       0 unless ( defined $self->{iter} ) {
409              
410             # initialize state
411 0         0 return $self->{iter} = $self->{base};
412             }
413              
414             #next
415 0 0       0 if ( $self->{iter}->cmp( $self->{last} ) < 0 ) {
416 0         0 return $self->{iter} = $self->{iter}->incr;
417             }
418              
419             # over
420 0         0 return;
421             }
422              
423             =head2 cmp
424              
425             $a->cmp($b)
426              
427             Compares two IP blocks:
428              
429             print $this->cmp($other);
430             @sorted_blocks = sort { $a->cmp($b) } @unsorted_blocks;
431              
432             cmp() returns -1, 0, +1:
433              
434             0 if $a == $b,
435              
436             -1 if $a is v4 and $b is v6
437             +1 if $a is v6 and $b is v4
438              
439             -1 if $a->base < $b->base
440             +1 if $a->base > $b->base
441              
442             -1 if $a->base == $b->base and $a->last > $b->last # $a is super-set of $b
443             +1 if $a->base == $b->base and $a->last < $b->last # $a is sub-set of $b
444              
445             =cut
446              
447             sub cmp {
448             return ( $_[0]->{base}->cmp( $_[1]->{base} ) )
449 547   100 547 1 1111 || ( $_[1]->{last}->cmp( $_[0]->{last} ) );
450             }
451              
452             =head2 is_disjunct_with
453              
454             $a->is_disjunct_with($b)
455              
456             Returns true if the blocks are disjunct
457              
458             a |----------|
459             b |---|
460              
461             a |------|
462             b |---|
463              
464             print "a and b are disjunct" if $a->is_disjunct_with($b)
465              
466             =cut
467              
468             sub is_disjunct_with {
469              
470             # a |----------|
471             # b |---|
472 194 100   194 1 314 return 1 if $_[0]->{base}->cmp( $_[1]->{last} ) == 1;
473              
474             # a |---|
475             # b |----------|
476 188 100       604 return 1 if $_[0]->{last}->cmp( $_[1]->{base} ) == -1;
477              
478 116         399 return;
479             }
480              
481             =head2 overlaps_with
482              
483             $a->overlaps_with($b)
484              
485             Returns true if the blocks overlap.
486              
487             a |-------|
488             b |------|
489            
490             a |------|
491             b |-------|
492            
493             a |----|
494             b |---------|
495            
496             a |---------|
497             b |----|
498              
499             =cut
500              
501             sub overlaps_with {
502              
503             # false if a == b
504 123 100   123 1 171 return if $_[0]->cmp( $_[1] ) == 0;
505              
506             # false if a contains b or vice versa
507 119 100 100     397 return if $_[0]->contains( $_[1] ) || $_[1]->contains( $_[0] );
508              
509             # false if a is_disjunct_with b
510 63 100       267 return if $_[0]->is_disjunct_with( $_[1] );
511              
512 5         17 return 1;
513             }
514              
515             =head2 contains
516              
517             $a->contains($b)
518              
519             Returns true if block a contains block b. a and b may NOT coincide.
520              
521             if ( $a->contains($b) ) {
522             print "block a contains block b\n";
523             }
524              
525             a |-----------------| |-----------------| |-----------------|
526             b |------------| |------------| |------------|
527              
528             The argument may also be a Net::IPAM::IP address object.
529              
530             if ( $a->contains($ip) ) {
531             print "block a contains ip\n";
532             }
533              
534             =cut
535              
536             # polymorphic: arg may be block or ip
537             sub contains {
538 259 100 100 259 1 1321 if ( ref $_[1] && $_[1]->isa('Net::IPAM::IP') ) {
539 8         16 return Net::IPAM::Block::Private::_contains_ip(@_);
540             }
541              
542 251 100 100     618 if ( ref $_[1] && $_[1]->isa('Net::IPAM::Block') ) {
543 249         393 return Net::IPAM::Block::Private::_contains_block(@_);
544             }
545              
546 2         167 Carp::croak 'wrong argument,';
547             }
548              
549             =head2 diff
550              
551             @diff = $outer->diff(@inner)
552              
553             Returns all blocks in outer block, minus the inner blocks.
554              
555             my $outer = Net::IPAM::Block->new("192.168.2.0/24");
556             my @inner = (
557             Net::IPAM::Block->new("192.168.2.0/26"),
558             Net::IPAM::Block->new("192.168.2.240-192.168.2.249"),
559             );
560              
561             my @diff = $outer->diff(@inner);
562              
563             # diff: [192.168.2.64-192.168.2.239, 192.168.2.250-192.168.2.255]
564              
565             =cut
566              
567             sub diff {
568 17     17 1 1008 my $self = shift;
569 17         42 my $outer = Net::IPAM::Block::Private::_clone($self);
570              
571             # no inner blocks, just return outer block
572 17 100       35 unless (@_) {
573 1 50       5 return wantarray ? ($outer) : [$outer];
574             }
575              
576 16         20 my @result;
577              
578             # blocks must be sorted for this algo
579 16         30 LOOP: foreach my $b ( sort_block(@_) ) {
580              
581             SWITCH: {
582             # no-op
583 64 100       178 next LOOP if $outer->is_disjunct_with($b);
  64         94  
584              
585             # masks rest
586 58 100       98 if ( $outer->cmp($b) == 0 ) {
587 4 100       60 return wantarray ? @result : \@result;
588             }
589              
590             # masks rest
591 54 100       289 if ( $b->contains($outer) ) {
592 2 50       26 return wantarray ? @result : \@result;
593             }
594              
595             # move cursor forward
596 52 100       327 if ( $outer->{base}->cmp( $b->{base} ) >= 0 ) {
597 36         115 $outer->{base} = $b->{last}->incr;
598 36         1134 last SWITCH;
599             }
600              
601             # save diff, move cursor forward
602 16 50       51 if ( $outer->{base}->cmp( $b->{base} ) < 0 ) {
603              
604 16         54 my $block = bless {}, ref $outer;
605 16         30 $block->{base} = $outer->{base};
606 16         29 $block->{last} = $b->{base}->decr;
607              
608 16         435 push @result, $block;
609              
610 16         28 $outer->{base} = $b->{last}->incr;
611 16         393 last SWITCH;
612             }
613              
614 0         0 die "logic error: rest=$outer, topic: $b,";
615              
616             } # end of SWITCH
617              
618             # overflow from last incr
619 52 50       90 if ( not defined $outer->{base} ) {
620 0 0       0 return wantarray ? @result : \@result;
621             }
622              
623             # cursor moved behind last
624 52 100       94 if ( $outer->{base}->cmp( $outer->{last} ) > 0 ) {
625 2 50       17 return wantarray ? @result : \@result;
626             }
627              
628             } # end of LOOP
629              
630             # save the rest
631 8         63 push @result, $outer;
632              
633 8 100       40 return wantarray ? @result : \@result;
634             }
635              
636             =head1 FUNCTIONS
637              
638             =head2 sort_block
639              
640             use Net::IPAM::Block 'sort_block';
641              
642             @sorted_blocks = sort_block @unsorted_blocks;
643              
644             Faster sort implemention (Schwartzian transform) as explcit sort function:
645              
646             @sorted_blocks = sort { $a->cmp($b) } @unsorted_blocks;
647              
648             =cut
649              
650             # see also cmp()
651              
652             sub sort_block {
653 212         328 return map { $_->[0] }
654 569 50       1611 sort { $a->[1]->cmp( $b->[1] ) || $b->[2]->cmp( $a->[2] ) }
655 29     29 1 1131 map { [ $_, $_->{base}, $_->{last} ] } @_;
  212         391  
656             }
657              
658             =head2 merge
659              
660              
661             use Net::IPAM::Block 'merge';
662              
663             @merged = merge(@blocks)
664              
665             Returns the minimal number of blocks spanning the range of input blocks.
666              
667             If CIDRs are required, use the following idiom:
668              
669             @cidrs = map { $_->to_cidrs } merge(@blocks);
670              
671             =cut
672              
673             sub merge {
674 13 100   13 1 78 return @_ if @_ <= 1;
675              
676             # sort blocks
677 12         17 my @sorted = sort_block(@_);
678              
679             # start with first [0] block as prev, see below
680 12         29 my @result = ( shift @sorted );
681              
682             # [1, ..
683 12         17 for (@sorted) {
684              
685             # skip rubbish
686 105 50       132 next unless defined $_;
687              
688 105         99 my $prev = $result[-1];
689              
690             # expand prev
691 105 100       124 if ( $prev->overlaps_with($_) ) {
692 3         7 $prev->{last} = $_->{last};
693 3         5 next;
694             }
695              
696             # expand prev
697 102         484 my $next = $prev->{last}->incr;
698 102 100 100     2274 if ( defined $next && $next->cmp( $_->{base} ) == 0 ) {
699 44         141 $prev->{last} = $_->{last};
700 44         72 next;
701             }
702              
703             # append block
704 58 100       151 if ( $prev->is_disjunct_with($_) ) {
705 9         26 push @result, $_;
706 9         16 next;
707             }
708              
709             # skip contains and equals
710             }
711              
712 12 100       38 return wantarray ? @result : [@result];
713             }
714              
715             =head2 aggregate
716              
717             *** DEPRECATED *** use merge in favor of
718              
719             =cut
720              
721             sub aggregate {
722 2     2 1 1228 Carp::carp("DEPRECATED: use merge() in favor of aggregate(),");
723 1         31 return merge(@_);
724             }
725              
726             =head1 OPERATORS
727              
728             L overloads the following operators.
729              
730             =head2 bool
731              
732             my $bool = !!$block;
733              
734             Always true.
735              
736             =head2 stringify
737              
738             my $str = "$block";
739              
740             Alias for L.
741              
742             =cut
743              
744             use overload
745 63     63   3593 '""' => sub { shift->to_string },
746 1     1   4 bool => sub { 1 },
747 13     13   103 fallback => 1;
  13         44  
  13         138  
748              
749             =head1 AUTHOR
750              
751             Karl Gaissmaier, C<< >>
752              
753             =head1 BUGS
754              
755             Please report any bugs or feature requests to C, or through
756             the web interface at L. I will be notified, and then you'll
757             automatically be notified of progress on your bug as I make changes.
758              
759             =head1 SUPPORT
760              
761             You can find documentation for this module with the perldoc command.
762              
763             perldoc Net::IPAM::Block
764              
765              
766             You can also look for information at:
767              
768             =over 4
769              
770             =item * on github
771              
772             TODO
773              
774             =back
775              
776             =head1 SEE ALSO
777              
778             L
779             L
780              
781             =head1 LICENSE AND COPYRIGHT
782              
783             This software is copyright (c) 2020-2021 by Karl Gaissmaier.
784              
785             This is free software; you can redistribute it and/or modify it under
786             the same terms as the Perl 5 programming language system itself.
787              
788             =cut
789              
790             1; # End of Net::IPAM::Block
791              
792             # vim: ts=2 sw=2 sts=2 background=dark