]>
Commit | Line | Data |
---|---|---|
4e9a2b57 MT |
1 | #!/usr/bin/perl -w |
2 | ############################################################################ | |
3 | # # | |
4 | # This file is part of the IPFire Firewall. # | |
5 | # # | |
6 | # IPFire is free software; you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation; either version 2 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # IPFire is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with IPFire; if not, write to the Free Software # | |
18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # | |
19 | # # | |
20 | # Copyright (C) 2014 IPFire Team <info@ipfire.org>. # | |
21 | # # | |
22 | ############################################################################ | |
23 | ||
24 | package Network; | |
25 | ||
5428eeee MT |
26 | require "/var/ipfire/general-functions.pl"; |
27 | ||
4e9a2b57 MT |
28 | use Socket; |
29 | ||
30 | my %PREFIX2NETMASK = ( | |
31 | 32 => "255.255.255.255", | |
32 | 31 => "255.255.255.254", | |
33 | 30 => "255.255.255.252", | |
34 | 29 => "255.255.255.248", | |
35 | 28 => "255.255.255.240", | |
36 | 27 => "255.255.255.224", | |
37 | 26 => "255.255.255.192", | |
38 | 25 => "255.255.255.128", | |
39 | 24 => "255.255.255.0", | |
40 | 23 => "255.255.254.0", | |
41 | 22 => "255.255.252.0", | |
42 | 21 => "255.255.248.0", | |
43 | 20 => "255.255.240.0", | |
44 | 19 => "255.255.224.0", | |
45 | 18 => "255.255.192.0", | |
46 | 17 => "255.255.128.0", | |
47 | 16 => "255.255.0.0", | |
48 | 15 => "255.254.0.0", | |
49 | 14 => "255.252.0.0", | |
50 | 13 => "255.248.0.0", | |
51 | 12 => "255.240.0.0", | |
52 | 11 => "255.224.0.0", | |
53 | 10 => "255.192.0.0", | |
54 | 9 => "255.128.0.0", | |
55 | 8 => "255.0.0.0", | |
56 | 7 => "254.0.0.0", | |
57 | 6 => "252.0.0.0", | |
58 | 5 => "248.0.0.0", | |
59 | 4 => "240.0.0.0", | |
60 | 3 => "224.0.0.0", | |
61 | 2 => "192.0.0.0", | |
62 | 1 => "128.0.0.0", | |
63 | 0 => "0.0.0.0" | |
64 | ); | |
65 | ||
66 | my %NETMASK2PREFIX = reverse(%PREFIX2NETMASK); | |
67 | ||
68 | # Takes an IP address in dotted decimal notation and | |
69 | # returns a 32 bit integer representing that IP addresss. | |
70 | # Will return undef for invalid inputs. | |
71 | sub ip2bin($) { | |
72 | my $address = shift; | |
73 | ||
74 | # This function returns undef for undefined input. | |
75 | if (!defined $address) { | |
76 | return undef; | |
77 | } | |
78 | ||
79 | my $address_bin = &Socket::inet_pton(AF_INET, $address); | |
80 | if ($address_bin) { | |
81 | $address_bin = unpack('N', $address_bin); | |
82 | } | |
83 | ||
84 | return $address_bin; | |
85 | } | |
86 | ||
87 | # Does the reverse of ip2bin(). | |
88 | # Will return undef for invalid inputs. | |
89 | sub bin2ip($) { | |
90 | my $address_bin = shift; | |
91 | ||
92 | # This function returns undef for undefined input. | |
93 | if (!defined $address_bin) { | |
94 | return undef; | |
95 | } | |
96 | ||
97 | my $address = pack('N', $address_bin); | |
98 | if ($address) { | |
99 | $address = &Socket::inet_ntop(AF_INET, $address); | |
100 | } | |
101 | ||
102 | return $address; | |
103 | } | |
104 | ||
8f23ce8e MT |
105 | # Takes two network addresses, compares them against each other |
106 | # and returns true if equal or false if not | |
107 | sub network_equal { | |
ff6cc711 AM |
108 | my $network1 = shift; |
109 | my $network2 = shift; | |
8f23ce8e | 110 | |
1047805d AM |
111 | my @bin1 = &network2bin($network1); |
112 | my @bin2 = &network2bin($network2); | |
8f23ce8e | 113 | |
3f3974b7 AM |
114 | if (!defined $bin1 || !defined $bin2) { |
115 | return undef; | |
116 | } | |
117 | ||
1047805d | 118 | if ($bin1[0] eq $bin2[0] && $bin1[1] eq $bin2[1]) { |
ff6cc711 AM |
119 | return 1; |
120 | } | |
8f23ce8e | 121 | |
ff6cc711 AM |
122 | return 0; |
123 | } | |
124 | ||
4e9a2b57 MT |
125 | # Takes a network in either a.b.c.d/a.b.c.d or a.b.c.d/e notation |
126 | # and will return an 32 bit integer representing the start | |
127 | # address and an other one representing the network mask. | |
128 | sub network2bin($) { | |
129 | my $network = shift; | |
130 | ||
131 | my ($address, $netmask) = split(/\//, $network, 2); | |
132 | ||
133 | if (&check_prefix($netmask)) { | |
134 | $netmask = &convert_prefix2netmask($netmask); | |
135 | } | |
136 | ||
137 | my $address_bin = &ip2bin($address); | |
138 | my $netmask_bin = &ip2bin($netmask); | |
139 | ||
3f3974b7 AM |
140 | if (!defined $address_bin || !defined $netmask_bin) { |
141 | return undef; | |
142 | } | |
143 | ||
4e9a2b57 MT |
144 | my $network_start = $address_bin & $netmask_bin; |
145 | ||
146 | return ($network_start, $netmask_bin); | |
147 | } | |
148 | ||
f770b728 AM |
149 | # Deletes leading zeros in ip address |
150 | sub ip_remove_zero{ | |
151 | my $address = shift; | |
152 | my @ip = split (/\./, $address); | |
153 | ||
154 | foreach my $octet (@ip) { | |
155 | $octet = int($octet); | |
156 | } | |
157 | ||
158 | $address = join (".", @ip); | |
159 | ||
160 | return $address; | |
161 | } | |
4e9a2b57 MT |
162 | # Returns True for all valid IP addresses |
163 | sub check_ip_address($) { | |
164 | my $address = shift; | |
165 | ||
166 | # Normalise the IP address and compare the result with | |
167 | # the input - which should obviously the same. | |
168 | my $normalised_address = &_normalise_ip_address($address); | |
169 | ||
170 | return ((defined $normalised_address) && ($address eq $normalised_address)); | |
171 | } | |
172 | ||
173 | # Returns True for all valid prefixes. | |
174 | sub check_prefix($) { | |
175 | my $prefix = shift; | |
176 | ||
177 | return (exists $PREFIX2NETMASK{$prefix}); | |
178 | } | |
179 | ||
180 | # Returns True for all valid subnet masks. | |
181 | sub check_netmask($) { | |
182 | my $netmask = shift; | |
183 | ||
184 | return (exists $NETMASK2PREFIX{$netmask}); | |
185 | } | |
186 | ||
187 | # Returns True for all valid inputs like a.b.c.d/a.b.c.d. | |
188 | sub check_ip_address_and_netmask($$) { | |
189 | my $network = shift; | |
190 | ||
191 | my ($address, $netmask) = split(/\//, $network, 2); | |
192 | ||
193 | # Check if the IP address is fine. | |
194 | # | |
195 | my $result = &check_ip_address($address); | |
196 | unless ($result) { | |
197 | return $result; | |
198 | } | |
199 | ||
200 | return &check_netmask($netmask); | |
201 | } | |
202 | ||
883c5453 MT |
203 | # Returns True for all valid subnets like a.b.c.d/e or a.b.c.d/a.b.c.d |
204 | sub check_subnet($) { | |
205 | my $subnet = shift; | |
206 | ||
207 | my ($address, $network) = split(/\//, $subnet, 2); | |
208 | ||
209 | # Check if the IP address is fine. | |
210 | my $result = &check_ip_address($address); | |
211 | unless ($result) { | |
212 | return $result; | |
213 | } | |
214 | ||
215 | return &check_prefix($network) || &check_netmask($network); | |
216 | } | |
217 | ||
4e9a2b57 MT |
218 | # For internal use only. Will take an IP address and |
219 | # return it in a normalised style. Like 8.8.8.010 -> 8.8.8.8. | |
220 | sub _normalise_ip_address($) { | |
221 | my $address = shift; | |
222 | ||
223 | my $address_bin = &ip2bin($address); | |
224 | if (!defined $address_bin) { | |
225 | return undef; | |
226 | } | |
227 | ||
228 | return &bin2ip($address_bin); | |
229 | } | |
230 | ||
231 | # Returns the prefix for the given subnet mask. | |
232 | sub convert_netmask2prefix($) { | |
233 | my $netmask = shift; | |
234 | ||
235 | if (exists $NETMASK2PREFIX{$netmask}) { | |
236 | return $NETMASK2PREFIX{$netmask}; | |
237 | } | |
238 | ||
239 | return undef; | |
240 | } | |
241 | ||
242 | # Returns the subnet mask for the given prefix. | |
243 | sub convert_prefix2netmask($) { | |
244 | my $prefix = shift; | |
245 | ||
246 | if (exists $PREFIX2NETMASK{$prefix}) { | |
247 | return $PREFIX2NETMASK{$prefix}; | |
248 | } | |
249 | ||
250 | return undef; | |
251 | } | |
252 | ||
253 | # Takes an IP address and an offset and | |
254 | # will return the offset'th IP address. | |
255 | sub find_next_ip_address($$) { | |
256 | my $address = shift; | |
257 | my $offset = shift; | |
258 | ||
259 | my $address_bin = &ip2bin($address); | |
260 | $address_bin += $offset; | |
261 | ||
262 | return &bin2ip($address_bin); | |
263 | } | |
264 | ||
265 | # Returns the network address of the given network. | |
266 | sub get_netaddress($) { | |
267 | my $network = shift; | |
268 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
269 | ||
270 | if (defined $network_bin) { | |
271 | return &bin2ip($network_bin); | |
272 | } | |
273 | ||
274 | return undef; | |
275 | } | |
276 | ||
277 | # Returns the broadcast of the given network. | |
278 | sub get_broadcast($) { | |
279 | my $network = shift; | |
280 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
281 | ||
282 | return &bin2ip($network_bin ^ ~$netmask_bin); | |
283 | } | |
284 | ||
285 | # Returns True if $address is in $network. | |
286 | sub ip_address_in_network($$) { | |
287 | my $address = shift; | |
288 | my $network = shift; | |
289 | ||
290 | my $address_bin = &ip2bin($address); | |
291 | return undef unless (defined $address_bin); | |
292 | ||
293 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
294 | ||
295 | # Find end address | |
01d61d15 | 296 | my $broadcast_bin = $network_bin ^ (~$netmask_bin % 2 ** 32); |
4e9a2b57 MT |
297 | |
298 | return (($address_bin ge $network_bin) && ($address_bin le $broadcast_bin)); | |
299 | } | |
300 | ||
5428eeee MT |
301 | sub setup_upstream_proxy() { |
302 | my %proxysettings = (); | |
303 | &General::readhash("${General::swroot}/proxy/settings", \%proxysettings); | |
304 | ||
305 | if ($proxysettings{'UPSTREAM_PROXY'}) { | |
306 | my $credentials = ""; | |
307 | ||
308 | if ($proxysettings{'UPSTREAM_USER'}) { | |
309 | $credentials = $proxysettings{'UPSTREAM_USER'}; | |
310 | ||
311 | if ($proxysettings{'UPSTREAM_PASSWORD'}) { | |
312 | $credentials .= ":" . $proxysettings{'UPSTREAM_PASSWORD'}; | |
313 | } | |
314 | ||
315 | $credentials .= "@"; | |
316 | } | |
317 | ||
318 | my $proxy = "http://" . $credentials . $proxysettings{'UPSTREAM_PROXY'}; | |
319 | ||
320 | $ENV{'http_proxy'} = $proxy; | |
321 | $ENV{'https_proxy'} = $proxy; | |
322 | $ENV{'ftp_proxy'} = $proxy; | |
323 | } | |
324 | } | |
325 | ||
c335b0cd MT |
326 | my %wireless_status = (); |
327 | ||
328 | sub _get_wireless_status($) { | |
329 | my $intf = shift; | |
330 | ||
331 | if (!$wireless_status{$intf}) { | |
332 | $wireless_status{$intf} = `iwconfig $intf`; | |
333 | } | |
334 | ||
335 | return $wireless_status{$intf}; | |
336 | } | |
337 | ||
338 | sub wifi_get_essid($) { | |
339 | my $status = &_get_wireless_status(shift); | |
340 | ||
341 | my ($essid) = $status =~ /ESSID:\"(.*)\"/; | |
342 | ||
343 | return $essid; | |
344 | } | |
345 | ||
346 | sub wifi_get_frequency($) { | |
347 | my $status = &_get_wireless_status(shift); | |
348 | ||
349 | my ($frequency) = $status =~ /Frequency:(\d+\.\d+ GHz)/; | |
350 | ||
351 | return $frequency; | |
352 | } | |
353 | ||
354 | sub wifi_get_access_point($) { | |
355 | my $status = &_get_wireless_status(shift); | |
356 | ||
357 | my ($access_point) = $status =~ /Access Point: ([0-9A-F:]+)/; | |
358 | ||
359 | return $access_point; | |
360 | } | |
361 | ||
362 | sub wifi_get_bit_rate($) { | |
363 | my $status = &_get_wireless_status(shift); | |
364 | ||
365 | my ($bit_rate) = $status =~ /Bit Rate=(\d+ [GM]b\/s)/; | |
366 | ||
367 | return $bit_rate; | |
368 | } | |
369 | ||
370 | sub wifi_get_link_quality($) { | |
371 | my $status = &_get_wireless_status(shift); | |
372 | ||
373 | my ($cur, $max) = $status =~ /Link Quality=(\d+)\/(\d+)/; | |
374 | ||
375 | return $cur * 100 / $max; | |
376 | } | |
377 | ||
378 | sub wifi_get_signal_level($) { | |
379 | my $status = &_get_wireless_status(shift); | |
380 | ||
381 | my ($signal_level) = $status =~ /Signal level=(\-\d+ dBm)/; | |
382 | ||
383 | return $signal_level; | |
384 | } | |
dbfd2622 MT |
385 | |
386 | sub get_hardware_address($) { | |
387 | my $ip_address = shift; | |
388 | my $ret; | |
389 | ||
390 | open(FILE, "/proc/net/arp") or die("Could not read ARP table"); | |
391 | ||
392 | while (<FILE>) { | |
393 | my ($ip_addr, $hwtype, $flags, $hwaddr, $mask, $device) = split(/\s+/, $_); | |
394 | if ($ip_addr eq $ip_address) { | |
395 | $ret = $hwaddr; | |
396 | last; | |
397 | } | |
398 | } | |
399 | ||
400 | close(FILE); | |
401 | ||
402 | return $ret; | |
403 | } | |
404 | ||
1dcf513a FB |
405 | sub get_nic_property { |
406 | my $nicname = shift; | |
407 | my $property = shift; | |
408 | my $result; | |
409 | ||
410 | open(FILE, "/sys/class/net/$nicname/$property") or die("Could not read property"); | |
411 | $result = <FILE>; | |
412 | close(FILE); | |
413 | ||
414 | chomp($result); | |
415 | ||
416 | return $result; | |
417 | } | |
418 | ||
419 | sub valid_mac($) { | |
420 | my $mac = shift; | |
421 | ||
422 | return $mac =~ /^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$/; | |
423 | } | |
424 | ||
425 | sub random_mac { | |
426 | my $address = "02"; | |
427 | ||
428 | for my $i (0 .. 4) { | |
429 | $address = sprintf("$address:%02x", int(rand(255))); | |
430 | } | |
431 | ||
432 | return $address; | |
433 | } | |
434 | ||
435 | sub get_mac_by_name($) { | |
436 | my $mac = shift; | |
437 | ||
438 | if ((!&valid_mac($mac)) && ($mac ne "")) { | |
439 | if (-e "/sys/class/net/$mac/") { | |
440 | $mac = get_nic_property($mac, "address"); | |
441 | } | |
442 | } | |
443 | ||
444 | return $mac; | |
445 | } | |
446 | ||
4e9a2b57 MT |
447 | 1; |
448 | ||
449 | # Remove the next line to enable the testsuite | |
450 | __END__ | |
451 | ||
452 | sub assert($) { | |
453 | my $ret = shift; | |
454 | ||
455 | if ($ret) { | |
456 | return; | |
457 | } | |
458 | ||
459 | print "ASSERTION ERROR"; | |
460 | exit(1); | |
461 | } | |
462 | ||
463 | sub testsuite() { | |
464 | my $result; | |
465 | ||
466 | my $address1 = &ip2bin("8.8.8.8"); | |
467 | assert($address1 == 134744072); | |
468 | ||
469 | my $address2 = &bin2ip($address1); | |
470 | assert($address2 eq "8.8.8.8"); | |
471 | ||
472 | # Check if valid IP addresses are correctly recognised. | |
473 | foreach my $address ("1.2.3.4", "192.168.180.1", "127.0.0.1") { | |
474 | if (!&check_ip_address($address)) { | |
475 | print "$address is not correctly recognised as a valid IP address!\n"; | |
476 | exit 1; | |
477 | }; | |
478 | } | |
479 | ||
480 | # Check if invalid IP addresses are correctly found. | |
481 | foreach my $address ("456.2.3.4", "192.768.180.1", "127.1", "1", "a.b.c.d", "1.2.3.4.5", "1.2.3.4/12") { | |
482 | if (&check_ip_address($address)) { | |
483 | print "$address is recognised as a valid IP address!\n"; | |
484 | exit 1; | |
485 | }; | |
486 | } | |
487 | ||
488 | $result = &check_ip_address_and_netmask("192.168.180.0/255.255.255.0"); | |
489 | assert($result); | |
490 | ||
491 | $result = &convert_netmask2prefix("255.255.254.0"); | |
492 | assert($result == 23); | |
493 | ||
494 | $result = &convert_prefix2netmask(8); | |
495 | assert($result eq "255.0.0.0"); | |
496 | ||
497 | $result = &find_next_ip_address("1.2.3.4", 2); | |
498 | assert($result eq "1.2.3.6"); | |
499 | ||
3713af1e MT |
500 | $result = &network_equal("192.168.0.0/24", "192.168.0.0/255.255.255.0"); |
501 | assert($result); | |
502 | ||
503 | $result = &network_equal("192.168.0.0/24", "192.168.0.0/25"); | |
504 | assert(!$result); | |
505 | ||
506 | $result = &network_equal("192.168.0.0/24", "192.168.0.128/25"); | |
507 | assert(!$result); | |
508 | ||
509 | $result = &network_equal("192.168.0.1/24", "192.168.0.XXX/24"); | |
1047805d | 510 | assert(!$result); |
3713af1e | 511 | |
4e9a2b57 MT |
512 | $result = &ip_address_in_network("10.0.1.4", "10.0.0.0/8"); |
513 | assert($result); | |
514 | ||
01d61d15 AF |
515 | $result = &ip_address_in_network("192.168.30.11", "192.168.30.0/255.255.255.0"); |
516 | assert($result); | |
517 | ||
3713af1e MT |
518 | print "Testsuite completed successfully!\n"; |
519 | ||
4e9a2b57 MT |
520 | return 0; |
521 | } | |
522 | ||
523 | &testsuite(); |