File Coverage

blib/lib/Metabrik/Address/Generate.pm
Criterion Covered Total %
statement 9 123 7.3
branch 0 56 0.0
condition 0 57 0.0
subroutine 3 11 27.2
pod 2 8 25.0
total 14 255 5.4


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # address::generate Brik
5             #
6             package Metabrik::Address::Generate;
7 1     1   545 use strict;
  1         2  
  1         29  
8 1     1   5 use warnings;
  1         2  
  1         27  
9              
10 1     1   5 use base qw(Metabrik);
  1         2  
  1         649  
11              
12             sub brik_properties {
13             return {
14 0     0 1   revision => '$Revision$',
15             tags => [ qw(unstable ipv4 ipv6 public routable reserved) ],
16             author => 'GomoR ',
17             license => 'http://opensource.org/licenses/BSD-3-Clause',
18             attributes => {
19             datadir => [ qw(directory) ],
20             file_count => [ qw(integer) ],
21             count => [ qw(integer) ],
22             },
23             attributes_default => {
24             file_count => 1000,
25             count => 0,
26             },
27             commands => {
28             ipv4_reserved_ranges => [ ],
29             ipv4_private_ranges => [ ],
30             ipv4_public_ranges => [ ],
31             ipv4_generate_space => [ qw(count|OPTIONAL file_count|OPTIONAL) ],
32             ipv4_generate_space_from_subnet => [ qw(
33             subnet count|OPTIONAL file_count|OPTIONAL
34             ) ],
35             random_ipv4_addresses => [ qw(count|OPTIONAL) ],
36             },
37             require_modules => {
38             'BSD::Resource' => [ qw(getrlimit setrlimit) ],
39             'List::Util' => [ qw(shuffle) ],
40             'Metabrik::Network::Address' => [ ],
41             },
42             };
43             }
44              
45             sub brik_init {
46 0     0 1   my $self = shift;
47              
48 0           my $limit = 200_000;
49 0           my $r = BSD::Resource::setrlimit(
50             BSD::Resource::RLIMIT_OPEN_MAX(), $limit, $limit);
51 0 0         if (! defined($r)) {
52 0           return $self->log->error("brik_init: failed to set open file ".
53             "limit to [$limit]");
54             }
55              
56 0           return $self->SUPER::brik_init(@_);
57             }
58              
59             #
60             # From zmap blacklist.conf:
61             #
62             # From IANA IPv4 Special-Purpose Address Registry
63             # http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
64             # Updated 2013-05-22
65             #
66             # 0.0.0.0/8 # RFC1122: "This host on this network"
67             # 10.0.0.0/8 # RFC1918: Private-Use
68             # 100.64.0.0/10 # RFC6598: Shared Address Space
69             # 127.0.0.0/8 # RFC1122: Loopback
70             # 169.254.0.0/16 # RFC3927: Link Local
71             # 172.16.0.0/12 # RFC1918: Private-Use
72             # 192.0.0.0/24 # RFC6890: IETF Protocol Assignments
73             # 192.0.2.0/24 # RFC5737: Documentation (TEST-NET-1)
74             # 192.88.99.0/24 # RFC3068: 6to4 Relay Anycast
75             # 192.168.0.0/16 # RFC1918: Private-Use
76             # 198.18.0.0/15 # RFC2544: Benchmarking
77             # 198.51.100.0/24 # RFC5737: Documentation (TEST-NET-2)
78             # 203.0.113.0/24 # RFC5737: Documentation (TEST-NET-3)
79             # 240.0.0.0/4 # RFC1112: Reserved
80             # 255.255.255.255/32 # RFC0919: Limited Broadcast
81             #
82             # From IANA Multicast Address Space Registry
83             # http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
84             # Updated 2013-06-25
85             #
86             # 224.0.0.0/4 # RFC5771: Multicast/Reserved
87             #
88             sub ipv4_reserved_ranges {
89 0     0 0   my $self = shift;
90              
91 0           my @reserved = qw(
92             0.0.0.0/8
93             10.0.0.0/8
94             100.64.0.0/10
95             127.0.0.0/8
96             169.254.0.0/16
97             172.16.0.0/12
98             192.0.2.0/24
99             192.88.99.0/24
100             192.168.0.0/16
101             198.18.0.0/15
102             198.51.100.0/24
103             203.0.113.0/24
104             224.0.0.0/4
105             240.0.0.0/4
106             255.255.255.255/32
107             );
108              
109 0           return \@reserved;
110             }
111              
112             sub ipv4_private_ranges {
113 0     0 0   my $self = shift;
114              
115 0           my @private = qw(
116             10.0.0.0/8
117             127.0.0.0/8
118             169.254.0.0/16
119             172.16.0.0/12
120             192.0.2.0/24
121             192.168.0.0/16
122             );
123              
124 0           return \@private;
125             }
126              
127             sub ipv4_public_ranges {
128 0     0 0   my $self = shift;
129              
130             # XXX: perform subnet diff from full address space and ipv4_reserved_ranges
131 0           my $reserved = $self->ipv4_reserved_ranges;
132              
133 0           return 1;
134             }
135              
136             sub ipv4_generate_space {
137 0     0 0   my $self = shift;
138 0           my ($count, $file_count) = @_;
139              
140 0   0       $count ||= $self->count;
141 0   0       $file_count ||= $self->file_count;
142 0 0         if ($file_count <= 0) {
143 0           return $self->log->error("ipv4_generate_space: cannot generate [$file_count] file");
144             }
145              
146 0           my $datadir = $self->datadir;
147 0           my $n = $file_count - 1;
148              
149 0           my @chunks = ();
150 0 0         if ($n > 0) {
151 0           my $size = length($n);
152 0           for (0..$n) {
153 0           my $file = sprintf("ip4-space-%0${size}d.txt", $_);
154 0 0         open(my $fd, '>', "$datadir/$file")
155             or return $self->log->error("ipv4_generate_space: open: file [$datadir/$file]: $!");
156 0           push @chunks, $fd;
157             }
158             }
159             else {
160 0           my $file = "ip4-space.txt";
161 0 0         open(my $fd, '>', "$datadir/$file")
162             or return $self->log->error("ipv4_generate_space: open: file [$datadir/$file]: $!");
163 0           push @chunks, $fd;
164             }
165              
166 0           my $current = 0;
167             # Note: this algorithm is best suited to generate the full IPv4 address space
168 0           for my $b4 (List::Util::shuffle(0..255)) {
169 0           for my $b3 (List::Util::shuffle(0..255)) {
170 0           for my $b2 (List::Util::shuffle(0..255)) {
171 0           for my $b1 (List::Util::shuffle(1..9,11..126,128..223)) {
172             # Skip:
173             # 0.0.0.0/8
174             # 10.0.0.0/8
175             # 127.0.0.0/8
176             # 224.0.0.0/4
177             # 240.0.0.0/4
178              
179 0 0 0       next if ($b1 == 169 && $b2 == 254); # Skip 169.254.0.0/16
180 0 0 0       next if ($b1 == 172 && ($b2 >= 16 && $b2 <= 31)); # Skip 172.16.0.0/12
      0        
181 0 0 0       next if ($b1 == 192 && $b2 == 168); # Skip 192.168.0.0/16
182 0 0 0       next if ($b1 == 192 && $b2 == 0 && $b3 == 2); # Skip 192.0.2.0/24
      0        
183              
184             # Write randomly to one of the previously open files
185 0           my $i;
186 0 0         ($n > 0) ? ($i = int(rand($n + 1))) : ($i = 0);
187              
188 0           my $out = $chunks[$i];
189 0           print $out "$b1.$b2.$b3.$b4\n";
190 0           $current++;
191              
192             # Stop if we have the number we wanted
193 0 0 0       if ($count && $current == $count) {
194 0           $self->log->info("ipv4_generate_space: generated $current IP addresses");
195 0           return 1;
196             }
197             }
198             }
199             }
200             }
201              
202 0           $self->log->info("ipv4_generate_space: generated $current IP addresses");
203              
204 0           return 1;
205             }
206              
207             sub ipv4_generate_space_from_subnet {
208 0     0 0   my $self = shift;
209 0           my ($subnet, $count, $file_count) = @_;
210              
211 0 0         $self->brik_help_run_undef_arg('ipv4_generate_space_from_subnet',
212             $subnet) or return;
213              
214 0   0       $count ||= $self->count;
215 0   0       $file_count ||= $self->file_count;
216 0 0         if ($file_count <= 0) {
217 0           return $self->log->error("ipv4_generate_space_from_subnet: cannot ".
218             "generate [$file_count] file");
219             }
220              
221 0           my $datadir = $self->datadir;
222 0           my $n = $file_count - 1;
223              
224 0 0         my $na = Metabrik::Network::Address->new_from_brik_init($self) or return;
225              
226             # Open all file descriptors where we will write ip addresses.
227 0           my @chunks = ();
228 0 0         if ($n > 0) {
229 0           my $size = length($n);
230 0           for (0..$n) {
231 0           my $file = sprintf("ip4-space-%0${size}d.txt", $_);
232 0 0         open(my $fd, '>', "$datadir/$file")
233             or return $self->log->error("ipv4_generate_space_from_subnet: ".
234             "open: file [$datadir/$file]: $!");
235 0           push @chunks, $fd;
236             }
237             }
238             else {
239 0           my $file = "ip4-space.txt";
240 0 0         open(my $fd, '>', "$datadir/$file")
241             or return $self->log->error("ipv4_generate_space_from_subnet: ".
242             "open: file [$datadir/$file]: $!");
243 0           push @chunks, $fd;
244             }
245              
246             # Generate ip addresses from given subnet and write to open files
247             # in a random order.
248 0 0         my $first = $na->ipv4_first_address($subnet) or return;
249 0 0         my $last = $na->ipv4_last_address($subnet) or return;
250              
251 0           my @bytes_first = split(/\./, $first);
252 0           my @bytes_last = split(/\./, $last);
253              
254 0           my $current = 0;
255             # Note: this algorithm is best suited to generate the full IPv4
256             # address space
257 0           for my $b4 (List::Util::shuffle($bytes_first[3]..$bytes_last[3])) {
258 0           for my $b3 (List::Util::shuffle($bytes_first[2]..$bytes_last[2])) {
259 0           for my $b2 (List::Util::shuffle($bytes_first[1]..$bytes_last[1])) {
260 0           for my $b1 (List::Util::shuffle($bytes_first[0]..$bytes_last[0])) {
261             # Write randomly to one of the previously open files
262 0           my $i;
263 0 0         ($n > 0) ? ($i = int(rand($n + 1))) : ($i = 0);
264              
265 0           my $out = $chunks[$i];
266 0           print $out "$b1.$b2.$b3.$b4\n";
267 0           $current++;
268              
269             # Stop if we have the number we wanted
270 0 0 0       if ($count && $current == $count) {
271 0           $self->log->info("ipv4_generate_space_from_subnet: ".
272             "generated $current IP addresses");
273 0           return 1;
274             }
275             }
276             }
277             }
278             }
279              
280 0           $self->log->info("ipv4_generate_space_from_subnet: generated ".
281             "$current IP addresses");
282              
283 0           return 1;
284              
285             }
286              
287             sub random_ipv4_addresses {
288 0     0 0   my $self = shift;
289 0           my ($count) = @_;
290              
291 0   0       $count ||= $self->count;
292 0 0         if ($count <= 0) {
293 0           return $self->log->error("random_ipv4_addresses: cannot generate [$count] address");
294             }
295              
296 0           my $current = 0;
297 0           my %random = ();
298 0           while (1) {
299 0           my $b1 = List::Util::shuffle(1..9,11..126,128..223); # Skip 0.0.0.0/8, 224.0.0.0/4,
300             # 240.0.0.0/4, 10.0.0.0/8,
301             # 127.0.0.0/8
302 0           my $b2 = List::Util::shuffle(0..255);
303 0 0 0       next if ($b1 == 169 && $b2 == 254); # Skip 169.254.0.0/16
304 0 0 0       next if ($b1 == 172 && ($b2 >= 16 && $b2 <= 31)); # Skip 172.16.0.0/12
      0        
305 0 0 0       next if ($b1 == 192 && $b2 == 168); # Skip 192.168.0.0/16
306              
307 0           my $b3 = List::Util::shuffle(0..255);
308 0 0 0       next if ($b1 == 192 && $b2 == 0 && $b3 == 2); # Skip 192.0.2.0/24
      0        
309              
310 0           my $b4 = List::Util::shuffle(0..255);
311 0           my $ip = "$b1.$b2.$b3.$b4";
312 0 0         if (! exists($random{$ip})) {
313 0           $random{$ip}++;
314 0           $current++;
315             }
316              
317 0 0         last if $current == $count;
318             }
319              
320 0           return [ keys %random ];
321             }
322              
323             1;
324              
325             __END__