captive: Get MAC address of a device without calling arp
[people/pmueller/ipfire-2.x.git] / config / cfgroot / network-functions.pl
CommitLineData
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
24package Network;
25
5428eeee
MT
26require "/var/ipfire/general-functions.pl";
27
4e9a2b57
MT
28use Socket;
29
30my %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
66my %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.
71sub 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.
89sub 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
107sub network_equal {
ff6cc711
AM
108 my $network1 = shift;
109 my $network2 = shift;
8f23ce8e 110
ff6cc711
AM
111 my $bin1 = &network2bin($network1);
112 my $bin2 = &network2bin($network2);
8f23ce8e
MT
113
114 if ($bin1 eq $bin2) {
ff6cc711
AM
115 return 1;
116 }
8f23ce8e 117
ff6cc711
AM
118 return 0;
119}
120
4e9a2b57
MT
121# Takes a network in either a.b.c.d/a.b.c.d or a.b.c.d/e notation
122# and will return an 32 bit integer representing the start
123# address and an other one representing the network mask.
124sub network2bin($) {
125 my $network = shift;
126
127 my ($address, $netmask) = split(/\//, $network, 2);
128
129 if (&check_prefix($netmask)) {
130 $netmask = &convert_prefix2netmask($netmask);
131 }
132
133 my $address_bin = &ip2bin($address);
134 my $netmask_bin = &ip2bin($netmask);
135
136 my $network_start = $address_bin & $netmask_bin;
137
138 return ($network_start, $netmask_bin);
139}
140
f770b728
AM
141# Deletes leading zeros in ip address
142sub ip_remove_zero{
143 my $address = shift;
144 my @ip = split (/\./, $address);
145
146 foreach my $octet (@ip) {
147 $octet = int($octet);
148 }
149
150 $address = join (".", @ip);
151
152 return $address;
153}
4e9a2b57
MT
154# Returns True for all valid IP addresses
155sub check_ip_address($) {
156 my $address = shift;
157
158 # Normalise the IP address and compare the result with
159 # the input - which should obviously the same.
160 my $normalised_address = &_normalise_ip_address($address);
161
162 return ((defined $normalised_address) && ($address eq $normalised_address));
163}
164
165# Returns True for all valid prefixes.
166sub check_prefix($) {
167 my $prefix = shift;
168
169 return (exists $PREFIX2NETMASK{$prefix});
170}
171
172# Returns True for all valid subnet masks.
173sub check_netmask($) {
174 my $netmask = shift;
175
176 return (exists $NETMASK2PREFIX{$netmask});
177}
178
179# Returns True for all valid inputs like a.b.c.d/a.b.c.d.
180sub check_ip_address_and_netmask($$) {
181 my $network = shift;
182
183 my ($address, $netmask) = split(/\//, $network, 2);
184
185 # Check if the IP address is fine.
186 #
187 my $result = &check_ip_address($address);
188 unless ($result) {
189 return $result;
190 }
191
192 return &check_netmask($netmask);
193}
194
883c5453
MT
195# Returns True for all valid subnets like a.b.c.d/e or a.b.c.d/a.b.c.d
196sub check_subnet($) {
197 my $subnet = shift;
198
199 my ($address, $network) = split(/\//, $subnet, 2);
200
201 # Check if the IP address is fine.
202 my $result = &check_ip_address($address);
203 unless ($result) {
204 return $result;
205 }
206
207 return &check_prefix($network) || &check_netmask($network);
208}
209
4e9a2b57
MT
210# For internal use only. Will take an IP address and
211# return it in a normalised style. Like 8.8.8.010 -> 8.8.8.8.
212sub _normalise_ip_address($) {
213 my $address = shift;
214
215 my $address_bin = &ip2bin($address);
216 if (!defined $address_bin) {
217 return undef;
218 }
219
220 return &bin2ip($address_bin);
221}
222
223# Returns the prefix for the given subnet mask.
224sub convert_netmask2prefix($) {
225 my $netmask = shift;
226
227 if (exists $NETMASK2PREFIX{$netmask}) {
228 return $NETMASK2PREFIX{$netmask};
229 }
230
231 return undef;
232}
233
234# Returns the subnet mask for the given prefix.
235sub convert_prefix2netmask($) {
236 my $prefix = shift;
237
238 if (exists $PREFIX2NETMASK{$prefix}) {
239 return $PREFIX2NETMASK{$prefix};
240 }
241
242 return undef;
243}
244
245# Takes an IP address and an offset and
246# will return the offset'th IP address.
247sub find_next_ip_address($$) {
248 my $address = shift;
249 my $offset = shift;
250
251 my $address_bin = &ip2bin($address);
252 $address_bin += $offset;
253
254 return &bin2ip($address_bin);
255}
256
257# Returns the network address of the given network.
258sub get_netaddress($) {
259 my $network = shift;
260 my ($network_bin, $netmask_bin) = &network2bin($network);
261
262 if (defined $network_bin) {
263 return &bin2ip($network_bin);
264 }
265
266 return undef;
267}
268
269# Returns the broadcast of the given network.
270sub get_broadcast($) {
271 my $network = shift;
272 my ($network_bin, $netmask_bin) = &network2bin($network);
273
274 return &bin2ip($network_bin ^ ~$netmask_bin);
275}
276
277# Returns True if $address is in $network.
278sub ip_address_in_network($$) {
279 my $address = shift;
280 my $network = shift;
281
282 my $address_bin = &ip2bin($address);
283 return undef unless (defined $address_bin);
284
285 my ($network_bin, $netmask_bin) = &network2bin($network);
286
287 # Find end address
01d61d15 288 my $broadcast_bin = $network_bin ^ (~$netmask_bin % 2 ** 32);
4e9a2b57
MT
289
290 return (($address_bin ge $network_bin) && ($address_bin le $broadcast_bin));
291}
292
5428eeee
MT
293sub setup_upstream_proxy() {
294 my %proxysettings = ();
295 &General::readhash("${General::swroot}/proxy/settings", \%proxysettings);
296
297 if ($proxysettings{'UPSTREAM_PROXY'}) {
298 my $credentials = "";
299
300 if ($proxysettings{'UPSTREAM_USER'}) {
301 $credentials = $proxysettings{'UPSTREAM_USER'};
302
303 if ($proxysettings{'UPSTREAM_PASSWORD'}) {
304 $credentials .= ":" . $proxysettings{'UPSTREAM_PASSWORD'};
305 }
306
307 $credentials .= "@";
308 }
309
310 my $proxy = "http://" . $credentials . $proxysettings{'UPSTREAM_PROXY'};
311
312 $ENV{'http_proxy'} = $proxy;
313 $ENV{'https_proxy'} = $proxy;
314 $ENV{'ftp_proxy'} = $proxy;
315 }
316}
317
c335b0cd
MT
318my %wireless_status = ();
319
320sub _get_wireless_status($) {
321 my $intf = shift;
322
323 if (!$wireless_status{$intf}) {
324 $wireless_status{$intf} = `iwconfig $intf`;
325 }
326
327 return $wireless_status{$intf};
328}
329
330sub wifi_get_essid($) {
331 my $status = &_get_wireless_status(shift);
332
333 my ($essid) = $status =~ /ESSID:\"(.*)\"/;
334
335 return $essid;
336}
337
338sub wifi_get_frequency($) {
339 my $status = &_get_wireless_status(shift);
340
341 my ($frequency) = $status =~ /Frequency:(\d+\.\d+ GHz)/;
342
343 return $frequency;
344}
345
346sub wifi_get_access_point($) {
347 my $status = &_get_wireless_status(shift);
348
349 my ($access_point) = $status =~ /Access Point: ([0-9A-F:]+)/;
350
351 return $access_point;
352}
353
354sub wifi_get_bit_rate($) {
355 my $status = &_get_wireless_status(shift);
356
357 my ($bit_rate) = $status =~ /Bit Rate=(\d+ [GM]b\/s)/;
358
359 return $bit_rate;
360}
361
362sub wifi_get_link_quality($) {
363 my $status = &_get_wireless_status(shift);
364
365 my ($cur, $max) = $status =~ /Link Quality=(\d+)\/(\d+)/;
366
367 return $cur * 100 / $max;
368}
369
370sub wifi_get_signal_level($) {
371 my $status = &_get_wireless_status(shift);
372
373 my ($signal_level) = $status =~ /Signal level=(\-\d+ dBm)/;
374
375 return $signal_level;
376}
dbfd2622
MT
377
378sub get_hardware_address($) {
379 my $ip_address = shift;
380 my $ret;
381
382 open(FILE, "/proc/net/arp") or die("Could not read ARP table");
383
384 while (<FILE>) {
385 my ($ip_addr, $hwtype, $flags, $hwaddr, $mask, $device) = split(/\s+/, $_);
386 if ($ip_addr eq $ip_address) {
387 $ret = $hwaddr;
388 last;
389 }
390 }
391
392 close(FILE);
393
394 return $ret;
395}
396
4e9a2b57
MT
3971;
398
399# Remove the next line to enable the testsuite
400__END__
401
402sub assert($) {
403 my $ret = shift;
404
405 if ($ret) {
406 return;
407 }
408
409 print "ASSERTION ERROR";
410 exit(1);
411}
412
413sub testsuite() {
414 my $result;
415
416 my $address1 = &ip2bin("8.8.8.8");
417 assert($address1 == 134744072);
418
419 my $address2 = &bin2ip($address1);
420 assert($address2 eq "8.8.8.8");
421
422 # Check if valid IP addresses are correctly recognised.
423 foreach my $address ("1.2.3.4", "192.168.180.1", "127.0.0.1") {
424 if (!&check_ip_address($address)) {
425 print "$address is not correctly recognised as a valid IP address!\n";
426 exit 1;
427 };
428 }
429
430 # Check if invalid IP addresses are correctly found.
431 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") {
432 if (&check_ip_address($address)) {
433 print "$address is recognised as a valid IP address!\n";
434 exit 1;
435 };
436 }
437
438 $result = &check_ip_address_and_netmask("192.168.180.0/255.255.255.0");
439 assert($result);
440
441 $result = &convert_netmask2prefix("255.255.254.0");
442 assert($result == 23);
443
444 $result = &convert_prefix2netmask(8);
445 assert($result eq "255.0.0.0");
446
447 $result = &find_next_ip_address("1.2.3.4", 2);
448 assert($result eq "1.2.3.6");
449
3713af1e
MT
450 $result = &network_equal("192.168.0.0/24", "192.168.0.0/255.255.255.0");
451 assert($result);
452
453 $result = &network_equal("192.168.0.0/24", "192.168.0.0/25");
454 assert(!$result);
455
456 $result = &network_equal("192.168.0.0/24", "192.168.0.128/25");
457 assert(!$result);
458
459 $result = &network_equal("192.168.0.1/24", "192.168.0.XXX/24");
460 assert($result);
461
4e9a2b57
MT
462 $result = &ip_address_in_network("10.0.1.4", "10.0.0.0/8");
463 assert($result);
464
01d61d15
AF
465 $result = &ip_address_in_network("192.168.30.11", "192.168.30.0/255.255.255.0");
466 assert($result);
467
3713af1e
MT
468 print "Testsuite completed successfully!\n";
469
4e9a2b57
MT
470 return 0;
471}
472
473&testsuite();