]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blame - config/cfgroot/general-functions.pl
core81: set need reboot flag and restart apache.
[people/teissler/ipfire-2.x.git] / config / cfgroot / general-functions.pl
CommitLineData
ac1cfefa
MT
1# SmoothWall CGIs
2#
3# This code is distributed under the terms of the GPL
4#
5# (c) The SmoothWall Team
6# Copyright (C) 2002 Alex Hudson - getcgihash() rewrite
7# Copyright (C) 2002 Bob Grant <bob@cache.ucr.edu> - validmac()
8# Copyright (c) 2002/04/13 Steve Bootes - add alias section, helper functions
9# Copyright (c) 2002/08/23 Mark Wormgoor <mark@wormgoor.com> validfqdn()
10# Copyright (c) 2003/09/11 Darren Critchley <darrenc@telus.net> srtarray()
11#
12# $Id: general-functions.pl,v 1.1.2.26 2006/01/04 16:33:55 franck78 Exp $
13#
14
15package General;
16
17use strict;
18use Socket;
19use IO::Socket;
c545beb1 20use Net::SSLeay;
8c877a82 21use Net::IPv4Addr qw(:all);
ac1cfefa
MT
22$|=1; # line buffering
23
776a1761
MT
24$General::version = 'VERSION';
25$General::swroot = 'CONFIG_ROOT';
ac1cfefa 26$General::noipprefix = 'noipg-';
c545beb1 27$General::adminmanualurl = 'http://wiki.ipfire.org';
ac1cfefa 28
c545beb1
MT
29#
30# log ("message") use default 'ipcop' tag
31# log ("tag","message") use your tag
32#
ac1cfefa
MT
33sub log
34{
c545beb1
MT
35 my $tag='ipfire';
36 $tag = shift if (@_>1);
ac1cfefa
MT
37 my $logmessage = $_[0];
38 $logmessage =~ /([\w\W]*)/;
39 $logmessage = $1;
77007ce5 40 system('logger', '-t', $tag, $logmessage);
ac1cfefa 41}
111c99dd
MT
42sub setup_default_networks
43{
44 my %netsettings=();
45 my $defaultNetworks = shift;
46
47 &readhash("/var/ipfire/ethernet/settings", \%netsettings);
48
49 # Get current defined networks (Red, Green, Blue, Orange)
50 $defaultNetworks->{$Lang::tr{'fwhost any'}}{'IPT'} = "0.0.0.0/0.0.0.0";
51 $defaultNetworks->{$Lang::tr{'fwhost any'}}{'NAME'} = "ALL";
52
53 $defaultNetworks->{$Lang::tr{'green'}}{'IPT'} = "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
68f6312a 54 $defaultNetworks->{$Lang::tr{'green'}}{'NET'} = "$netsettings{'GREEN_ADDRESS'}";
111c99dd
MT
55 $defaultNetworks->{$Lang::tr{'green'}}{'NAME'} = "GREEN";
56
b9648e58 57 if ($netsettings{'RED_DEV'} ne ''){
223d3b1d 58 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'IPT'} = "$netsettings{'RED_NETADDRESS'}/$netsettings{'RED_NETMASK'}";
68f6312a 59 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'NET'} = "$netsettings{'RED_ADDRESS'}";
223d3b1d 60 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'NAME'} = "RED";
b9648e58 61 }
111c99dd
MT
62 if ($netsettings{'ORANGE_DEV'} ne ''){
63 $defaultNetworks->{$Lang::tr{'orange'}}{'IPT'} = "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
68f6312a 64 $defaultNetworks->{$Lang::tr{'orange'}}{'NET'} = "$netsettings{'ORANGE_ADDRESS'}";
111c99dd
MT
65 $defaultNetworks->{$Lang::tr{'orange'}}{'NAME'} = "ORANGE";
66 }
67
68 if ($netsettings{'BLUE_DEV'} ne ''){
69 $defaultNetworks->{$Lang::tr{'blue'}}{'IPT'} = "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
68f6312a 70 $defaultNetworks->{$Lang::tr{'blue'}}{'NET'} = "$netsettings{'BLUE_ADDRESS'}";
111c99dd
MT
71 $defaultNetworks->{$Lang::tr{'blue'}}{'NAME'} = "BLUE";
72 }
c7043621
AM
73
74 #IPFire himself
75 $defaultNetworks->{'IPFire'}{'NAME'} = "IPFire";
111c99dd
MT
76
77 # OpenVPN
78 if(-e "${General::swroot}/ovpn/settings")
79 {
80 my %ovpnSettings = ();
81 &readhash("${General::swroot}/ovpn/settings", \%ovpnSettings);
82
83 # OpenVPN on Red?
84 if(defined($ovpnSettings{'DOVPN_SUBNET'}))
85 {
86 my ($ip,$sub) = split(/\//,$ovpnSettings{'DOVPN_SUBNET'});
87 $sub=&General::iporsubtocidr($sub);
88 my @tempovpnsubnet = split("\/", $ovpnSettings{'DOVPN_SUBNET'});
1a8fde0e
AM
89 $defaultNetworks->{'OpenVPN ' ."($ip/$sub)"}{'ADR'} = $tempovpnsubnet[0];
90 $defaultNetworks->{'OpenVPN ' ."($ip/$sub)"}{'NAME'} = "OpenVPN-Dyn";
111c99dd
MT
91 }
92 } # end OpenVPN
93 # IPsec RW NET
94 if(-e "${General::swroot}/vpn/settings")
95 {
96 my %ipsecsettings = ();
97 &readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
98 if($ipsecsettings{'RW_NET'} ne '')
99 {
100 my ($ip,$sub) = split(/\//,$ipsecsettings{'RW_NET'});
101 $sub=&General::iporsubtocidr($sub);
102 my @tempipsecsubnet = split("\/", $ipsecsettings{'RW_NET'});
6ee90535
AM
103 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'ADR'} = $tempipsecsubnet[0];
104 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'NAME'} = "IPsec RW";
105 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'NET'} = &getnextip($ip);
111c99dd
MT
106 }
107 }
111c99dd
MT
108}
109sub get_aliases
110{
111
112 my $defaultNetworks = shift;
113 open(FILE, "${General::swroot}/ethernet/aliases") or die 'Unable to open aliases file.';
114 my @current = <FILE>;
115 close(FILE);
116 my $ctr = 0;
117 foreach my $line (@current)
118 {
119 if ($line ne ''){
120 chomp($line);
121 my @temp = split(/\,/,$line);
122 if ($temp[2] eq '') {
123 $temp[2] = "Alias $ctr : $temp[0]";
124 }
125 $defaultNetworks->{$temp[2]}{'IPT'} = "$temp[0]";
68f6312a 126 $defaultNetworks->{$temp[2]}{'NET'} = "$temp[0]";
111c99dd
MT
127
128 $ctr++;
129 }
130 }
131}
ac1cfefa
MT
132
133sub readhash
134{
135 my $filename = $_[0];
136 my $hash = $_[1];
137 my ($var, $val);
138
139
140 # Some ipcop code expects that readhash 'complete' the hash if new entries
141 # are presents. Not clear it !!!
142 #%$hash = ();
143
144 open(FILE, $filename) or die "Unable to read file $filename";
145
146 while (<FILE>)
147 {
148 chop;
149 ($var, $val) = split /=/, $_, 2;
150 if ($var)
151 {
152 $val =~ s/^\'//g;
153 $val =~ s/\'$//g;
154
155 # Untaint variables read from hash
77007ce5
MT
156 # trim space from begin and end
157 $var =~ s/^\s+//;
158 $var =~ s/\s+$//;
159 $var =~ /([A-Za-z0-9_-]*)/;
160 $var = $1;
161 $val =~ /([\w\W]*)/;
162 $val = $1;
ac1cfefa
MT
163 $hash->{$var} = $val;
164 }
165 }
166 close FILE;
167}
168
169
170sub writehash
171{
172 my $filename = $_[0];
173 my $hash = $_[1];
174 my ($var, $val);
175
176 # write cgi vars to the file.
177 open(FILE, ">${filename}") or die "Unable to write file $filename";
178 flock FILE, 2;
179 foreach $var (keys %$hash)
180 {
ad60e3ea 181 if ( $var eq "__CGI__"){next;}
ac1cfefa
MT
182 $val = $hash->{$var};
183 # Darren Critchley Jan 17, 2003 added the following because when submitting with a graphic, the x and y
184 # location of the mouse are submitted as well, this was being written to the settings file causing
185 # some serious grief! This skips the variable.x and variable.y
186 if (!($var =~ /(.x|.y)$/)) {
187 if ($val =~ / /) {
188 $val = "\'$val\'"; }
189 if (!($var =~ /^ACTION/)) {
190 print FILE "${var}=${val}\n"; }
191 }
192 }
193 close FILE;
194}
195
90c2e164
CS
196sub writehashpart
197{
198 # This function replaces the given hash in the original hash by keeping the old
199 # content and just replacing the new content
200
201 my $filename = $_[0];
202 my $newhash = $_[1];
203 my %oldhash;
204 my ($var, $val);
205
206 readhash("${filename}", \%oldhash);
207
208 foreach $var (keys %$newhash){
209 $oldhash{$var}=$newhash->{$var};
210 }
211
212 # write cgi vars to the file.
213 open(FILE, ">${filename}") or die "Unable to write file $filename";
214 flock FILE, 2;
215 foreach $var (keys %oldhash)
216 {
217 if ( $var eq "__CGI__"){next;}
218 $val = $oldhash{$var};
219 # Darren Critchley Jan 17, 2003 added the following because when submitting with a graphic, the x and y
220 # location of the mouse are submitted as well, this was being written to the settings file causing
221 # some serious grief! This skips the variable.x and variable.y
222 if (!($var =~ /(.x|.y)$/)) {
223 if ($val =~ / /) {
224 $val = "\'$val\'"; }
225 if (!($var =~ /^ACTION/)) {
226 print FILE "${var}=${val}\n"; }
227 }
228 }
229 close FILE;
230}
ac1cfefa 231
1dc44471 232sub age {
ac1cfefa 233 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
1dc44471
MT
234 $atime, $mtime, $ctime, $blksize, $blocks) = stat $_[0];
235 my $totalsecs = time() - $mtime;
236 my @s = ();
ac1cfefa 237
ac1cfefa 238 my $secs = $totalsecs % 60;
1dc44471
MT
239 $totalsecs /= 60;
240 if ($secs > 0) {
241 push(@s, "${secs}s");
3687a2e2
JIW
242 }
243
1dc44471
MT
244 my $min = $totalsecs % 60;
245 $totalsecs /= 60;
246 if ($min > 0) {
247 push(@s, "${min}m");
3687a2e2
JIW
248 }
249
1dc44471
MT
250 my $hrs = $totalsecs % 24;
251 $totalsecs /= 24;
252 if ($hrs > 0) {
253 push(@s, "${hrs}h");
3687a2e2
JIW
254 }
255
1dc44471
MT
256 my $days = int($totalsecs);
257 if ($days > 0) {
258 push(@s, "${days}d");
3687a2e2 259 }
3687a2e2 260
1dc44471 261 return join(" ", reverse(@s));
ac1cfefa
MT
262}
263
264sub validip
265{
266 my $ip = $_[0];
267
268 if (!($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)) {
269 return 0; }
270 else
271 {
272 my @octets = ($1, $2, $3, $4);
273 foreach $_ (@octets)
274 {
275 if (/^0./) {
276 return 0; }
277 if ($_ < 0 || $_ > 255) {
278 return 0; }
279 }
280 return 1;
281 }
282}
283
284sub validmask
285{
286 my $mask = $_[0];
287
288 # secord part an ip?
289 if (&validip($mask)) {
290 return 1; }
291 # second part a number?
292 if (/^0/) {
293 return 0; }
294 if (!($mask =~ /^\d+$/)) {
295 return 0; }
296 if ($mask >= 0 && $mask <= 32) {
297 return 1; }
298 return 0;
299}
300
301sub validipormask
302{
303 my $ipormask = $_[0];
304
305 # see if it is a IP only.
306 if (&validip($ipormask)) {
307 return 1; }
308 # split it into number and mask.
309 if (!($ipormask =~ /^(.*?)\/(.*?)$/)) {
310 return 0; }
311 my $ip = $1;
312 my $mask = $2;
313 # first part not a ip?
314 if (!(&validip($ip))) {
315 return 0; }
316 return &validmask($mask);
317}
318
8c877a82 319sub subtocidr
ac1cfefa 320{
8c877a82
AM
321 #gets: Subnet in decimal (255.255.255.0)
322 #Gives: 24 (The cidr of network)
323 my ($byte1, $byte2, $byte3, $byte4) = split(/\./, $_[0].".0.0.0.0");
324 my $num = ($byte1 * 16777216) + ($byte2 * 65536) + ($byte3 * 256) + $byte4;
325 my $bin = unpack("B*", pack("N", $num));
326 my $count = ($bin =~ tr/1/1/);
327 return $count;
328}
ac1cfefa 329
8c877a82
AM
330sub cidrtosub
331{
332 #gets: Cidr of network (20-30 for ccd)
333 #Konverts 30 to 255.255.255.252 e.g
334 my $cidr=$_[0];
335 my $netmask = &Net::IPv4Addr::ipv4_cidr2msk($cidr);
336 return "$netmask";
337}
338
339sub iporsubtodec
340{
341 #Gets: Ip address or subnetmask in decimal oder CIDR
342 #Gives: What it gets only in CIDR format
343 my $subnet=$_[0];
344 my $net;
345 my $mask;
346 my $full=0;
347 if ($subnet =~ /^(.*?)\/(.*?)$/) {
348 ($net,$mask) = split (/\//,$subnet);
349 $full=1;
350 return "$subnet";
351 }else{
352 $mask=$subnet;
353 }
354 #Subnet already in decimal and valid?
355 if ($mask=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
7490b22e 356 for (my $i=0;$i<=32;$i++){
8c877a82
AM
357 if (&General::cidrtosub($i) eq $mask){
358 if ($full == 0){return $mask;}else{
359 return $net."/".$mask;
360 }
361 }
362 }
363 }
364 #Subnet in binary format?
7490b22e 365 if ($mask=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
8c877a82
AM
366 if($full == 0){ return &General::cidrtosub($mask);}else{
367 return $net."/".&General::cidrtosub($mask);
368 }
369 }else{
370 return 3;
371 }
372 return 3;
373}
374
375
376sub iporsubtocidr
377{
378 #gets: Ip Address or subnetmask in decimal oder CIDR
379 #Gives: What it gets only in CIDR format
380 my $subnet=$_[0];
381 my $net;
382 my $mask;
383 my $full=0;
384 if ($subnet =~ /^(.*?)\/(.*?)$/) {
385 ($net,$mask) = split (/\//,$subnet);
386 $full=1;
387 }else{
388 $mask=$subnet;
389 }
390 #Subnet in decimal and valid?
391 if ($mask=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
7490b22e 392 for (my $i=0;$i<=32;$i++){
8c877a82
AM
393 if (&General::cidrtosub($i) eq $mask){
394 if ($full == 0){return &General::subtocidr($mask);}else{
395 return $net."/".&General::subtocidr($mask);
396 }
397 }
398 }
399 }
400 #Subnet already in binary format?
7490b22e 401 if ($mask=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
8c877a82
AM
402 if($full == 0){ return $mask;}else{
403 return $net."/".$mask;
404 }
405 }else{
406 return 3;
407 }
408 return 3;
409}
410
411sub getnetworkip
412{
413 #Gets: IP, CIDR (10.10.10.0-255, 24)
414 #Gives: 10.10.10.0
415 my ($ccdip,$ccdsubnet) = @_;
8df091d9
MT
416 my $ip_address_binary = inet_aton( $ccdip );
417 my $netmask_binary = ~pack("N", (2**(32-$ccdsubnet))-1);
418 my $network_address = inet_ntoa( $ip_address_binary & $netmask_binary );
8c877a82
AM
419 return $network_address;
420}
421
422sub getccdbc
423{
424 #Gets: IP in Form ("192.168.0.0/24")
425 #Gives: Broadcastaddress of network
426 my $ccdnet=$_;
427 my ($ccdip,$ccdsubnet) = split "/",$ccdnet;
428 my $ip_address_binary = inet_aton( $ccdip );
429 my $netmask_binary = ~pack("N", (2**(32-$ccdsubnet))-1);
430 my $broadcast_address = inet_ntoa( $ip_address_binary | ~$netmask_binary );
431 return $broadcast_address;
432}
e81be1e1
AM
433
434sub ip2dec
435{
436 my $ip_num;
437 my $ip=$_[0];
438 if ( $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ ) {
439 $ip_num = (($1*256**3) + ($2*256**2) + ($3*256) + $4);
440 } else {
441 $ip_num = -1;
442 }
443 $ip_num = (($1*256**3) + ($2*256**2) + ($3*256) + $4);
444 return($ip_num);
445}
446
447sub dec2ip
448{
449 my $ip;
450 my $ip_num=$_[0];
451 my $o1=$ip_num%256;
452 $ip_num=int($ip_num/256);
453 my $o2=$ip_num%256;
454 $ip_num=int($ip_num/256);
455 my $o3=$ip_num%256;
456 $ip_num=int($ip_num/256);
457 my $o4=$ip_num%256;
458 $ip="$o4.$o3.$o2.$o1";
459 return ($ip);
460}
461
8c877a82
AM
462sub getnextip
463{
e81be1e1
AM
464 my $decip=&ip2dec($_[0]);
465 $decip=$decip+4;
466 return &dec2ip($decip);
8c877a82 467}
e81be1e1 468
8c877a82
AM
469sub getlastip
470{
e81be1e1
AM
471 my $decip=&ip2dec($_[0]);
472 $decip--;
473 return &dec2ip($decip);
8c877a82
AM
474}
475
476sub validipandmask
477{
478 #Gets: Ip address in 192.168.0.0/24 or 192.168.0.0/255.255.255.0 and checks if subnet valid
479 #Gives: True bzw 0 if success or false
480 my $ccdnet=$_[0];
481 my $subcidr;
482
483 if (!($ccdnet =~ /^(.*?)\/(.*?)$/)) {
484 return 0;
485 }
486 my ($ccdip,$ccdsubnet)=split (/\//, $ccdnet);
487 #IP valid?
488 if ($ccdip=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1>0 && $1<=255 && $2>=0 && $2<=255 && $3>=0 && $3<=255 && $4<=255 ))) {
489 #Subnet in decimal and valid?
490 if ($ccdsubnet=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
7490b22e 491 for (my $i=0;$i<=32;$i++){
8c877a82
AM
492 if (&General::cidrtosub($i) eq $ccdsubnet){
493 return 1;
494 }
7490b22e 495 }
8c877a82 496 #Subnet already in binary format?
7490b22e 497 }elsif ($ccdsubnet=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
8c877a82
AM
498 return 1;
499 }else{
500 return 0;
501 }
502
503 }
504 return 0;
ac1cfefa
MT
505}
506
e2429e8d
AM
507sub checksubnets
508{
4d81e0f3
AM
509 my %ccdconfhash=();
510 my %ovpnconfhash=();
511 my %vpnconf=();
512 my %ipsecconf=();
513 my %ownnet=();
514 my %ovpnconf=();
515 my @ccdconf=();
516 my $ccdname=$_[0];
517 my $ccdnet=$_[1];
518 my $ownnet=$_[2];
e2429e8d
AM
519 my $errormessage;
520 my ($ip,$cidr)=split(/\//,$ccdnet);
521 $cidr=&iporsubtocidr($cidr);
4d81e0f3 522
e2429e8d 523 #get OVPN-Subnet (dynamic range)
e2429e8d
AM
524 &readhash("${General::swroot}/ovpn/settings", \%ovpnconf);
525 my ($ovpnip,$ovpncidr)= split (/\//,$ovpnconf{'DOVPN_SUBNET'});
526 $ovpncidr=&iporsubtocidr($ovpncidr);
4d81e0f3 527
e2429e8d
AM
528 #check if we try to use same network as ovpn server
529 if ("$ip/$cidr" eq "$ovpnip/$ovpncidr") {
530 $errormessage=$errormessage.$Lang::tr{'ccd err isovpnnet'}."<br>";
531 return $errormessage;
532 }
4d81e0f3
AM
533
534 #check if we try to use same network as another ovpn N2N
535 if($ownnet ne 'ovpn'){
536 &readhasharray("${General::swroot}/ovpn/ovpnconfig", \%ovpnconfhash);
537 foreach my $key (keys %ovpnconfhash) {
538 if ($ovpnconfhash{$key}[3] eq 'net'){
539 my @ovpnnet=split (/\//,$ovpnconfhash{$key}[11]);
540 if (&IpInSubnet($ip,$ovpnnet[0],&iporsubtodec($ovpnnet[1]))){
541 $errormessage=$errormessage.$Lang::tr{'ccd err isovpnn2n'}." $ovpnconfhash{$key}[1] <br>";
542 return $errormessage;
543 }
544 }
545 }
546 }
547
548 #check if we use a network-name/subnet (static-ovpn) that already exists
e2429e8d
AM
549 &readhasharray("${General::swroot}/ovpn/ccd.conf", \%ccdconfhash);
550 foreach my $key (keys %ccdconfhash) {
551 @ccdconf=split(/\//,$ccdconfhash{$key}[1]);
552 if ($ccdname eq $ccdconfhash{$key}[0])
553 {
554 $errormessage=$errormessage.$Lang::tr{'ccd err nameexist'}."<br>";
555 return $errormessage;
556 }
557 my ($newip,$newsub) = split(/\//,$ccdnet);
558 if (&IpInSubnet($newip,$ccdconf[0],&iporsubtodec($ccdconf[1])))
559 {
4d81e0f3 560 $errormessage=$errormessage.$Lang::tr{'ccd err issubnet'}." $ccdconfhash{$key}[0]<br>";
e2429e8d
AM
561 return $errormessage;
562 }
e2429e8d 563 }
4d81e0f3 564
e2429e8d 565 #check if we use a ipsec right network which is already defined
4d81e0f3
AM
566 if($ownnet ne 'ipsec'){
567 &General::readhasharray("${General::swroot}/vpn/config", \%ipsecconf);
568 foreach my $key (keys %ipsecconf){
569 if ($ipsecconf{$key}[11] ne ''){
570 my ($ipsecip,$ipsecsub) = split (/\//, $ipsecconf{$key}[11]);
571 $ipsecsub=&iporsubtodec($ipsecsub);
572 if($ipsecconf{$key}[1] ne $ccdname){
573 if ( &IpInSubnet ($ip,$ipsecip,$ipsecsub) ){
574 $errormessage=$Lang::tr{'ccd err isipsecnet'}." Name: $ipsecconf{$key}[1]";
575 return $errormessage;
576 }
f7e3d208 577 }
e2429e8d
AM
578 }
579 }
580 }
4d81e0f3
AM
581
582 #check if we use the ipsec RW Network (if defined)
583 &readhash("${General::swroot}/vpn/settings", \%vpnconf);
584 if ($vpnconf{'RW_NET'} ne ''){
585 my ($ipsecrwnet,$ipsecrwsub)=split (/\//, $vpnconf{'RW_NET'});
586 if (&IpInSubnet($ip,$ipsecrwnet,&iporsubtodec($ipsecrwsub)))
587 {
588 $errormessage=$errormessage.$Lang::tr{'ccd err isipsecrw'}."<br>";
589 return $errormessage;
590 }
591 }
592
e2429e8d 593 #check if we use one of ipfire's networks (green,orange,blue)
e2429e8d 594 &readhash("${General::swroot}/ethernet/settings", \%ownnet);
4d81e0f3
AM
595 if (($ownnet{'GREEN_NETADDRESS'} ne '' && $ownnet{'GREEN_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'GREEN_NETADDRESS'},&iporsubtodec($ownnet{'GREEN_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err green'};return $errormessage;}
596 if (($ownnet{'ORANGE_NETADDRESS'} ne '' && $ownnet{'ORANGE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'ORANGE_NETADDRESS'},&iporsubtodec($ownnet{'ORANGE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err orange'};return $errormessage;}
597 if (($ownnet{'BLUE_NETADDRESS'} ne '' && $ownnet{'BLUE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'BLUE_NETADDRESS'},&iporsubtodec($ownnet{'BLUE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err blue'};return $errormessage;}
598 if (($ownnet{'RED_NETADDRESS'} ne '' && $ownnet{'RED_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'RED_NETADDRESS'},&iporsubtodec($ownnet{'RED_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err red'};return $errormessage;}
e2429e8d
AM
599}
600
29f238b2
AM
601sub check_net_internal{
602 my $network=shift;
603 my ($ip,$cidr)=split(/\//,$network);
604 my %ownnet=();
605 my $errormessage;
606 $cidr=&iporsubtocidr($cidr);
607 #check if we use one of ipfire's networks (green,orange,blue)
608 &readhash("${General::swroot}/ethernet/settings", \%ownnet);
609 if (($ownnet{'GREEN_NETADDRESS'} ne '' && $ownnet{'GREEN_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'GREEN_NETADDRESS'},&iporsubtodec($ownnet{'GREEN_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err green'};return $errormessage;}
610 if (($ownnet{'ORANGE_NETADDRESS'} ne '' && $ownnet{'ORANGE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'ORANGE_NETADDRESS'},&iporsubtodec($ownnet{'ORANGE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err orange'};return $errormessage;}
611 if (($ownnet{'BLUE_NETADDRESS'} ne '' && $ownnet{'BLUE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'BLUE_NETADDRESS'},&iporsubtodec($ownnet{'BLUE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err blue'};return $errormessage;}
612 if (($ownnet{'RED_NETADDRESS'} ne '' && $ownnet{'RED_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'RED_NETADDRESS'},&iporsubtodec($ownnet{'RED_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err red'};return $errormessage;}
613}
e2429e8d 614
ac1cfefa
MT
615sub validport
616{
617 $_ = $_[0];
618
619 if (!/^\d+$/) {
620 return 0; }
621 if (/^0./) {
622 return 0; }
623 if ($_ >= 1 && $_ <= 65535) {
624 return 1; }
625 return 0;
626}
627
d30ea451
CS
628sub validproxyport
629{
630 $_ = $_[0];
631
632 if (!/^\d+$/) {
633 return 0; }
634 if (/^0./) {
635 return 0; }
636 if ($_ == 53 || $_ == 222 || $_ == 444 || $_ == 81 ) {
637 return 0; }
638 elsif ($_ >= 1 && $_ <= 65535) {
639 return 1; }
640 return 0;
641}
642
ac1cfefa
MT
643sub validmac
644{
645 my $checkmac = $_[0];
646 my $ot = '[0-9a-f]{2}'; # 2 Hex digits (one octet)
647 if ($checkmac !~ /^$ot:$ot:$ot:$ot:$ot:$ot$/i)
648 {
649 return 0;
650 }
651 return 1;
652}
653
654sub validhostname
655{
656 # Checks a hostname against RFC1035
657 my $hostname = $_[0];
658
659 # Each part should be at least two characters in length
660 # but no more than 63 characters
661 if (length ($hostname) < 1 || length ($hostname) > 63) {
662 return 0;}
663 # Only valid characters are a-z, A-Z, 0-9 and -
8c877a82 664 if ($hostname !~ /^[a-zA-Z0-9-\s]*$/) {
ac1cfefa
MT
665 return 0;}
666 # First character can only be a letter or a digit
667 if (substr ($hostname, 0, 1) !~ /^[a-zA-Z0-9]*$/) {
668 return 0;}
669 # Last character can only be a letter or a digit
670 if (substr ($hostname, -1, 1) !~ /^[a-zA-Z0-9]*$/) {
671 return 0;}
672 return 1;
673}
674
675sub validdomainname
676{
677 my $part;
678
679 # Checks a domain name against RFC1035
680 my $domainname = $_[0];
681 my @parts = split (/\./, $domainname); # Split hostname at the '.'
682
683 foreach $part (@parts) {
684 # Each part should be at least two characters in length
685 # but no more than 63 characters
686 if (length ($part) < 2 || length ($part) > 63) {
687 return 0;}
688 # Only valid characters are a-z, A-Z, 0-9 and -
689 if ($part !~ /^[a-zA-Z0-9-]*$/) {
690 return 0;}
691 # First character can only be a letter or a digit
692 if (substr ($part, 0, 1) !~ /^[a-zA-Z0-9]*$/) {
693 return 0;}
694 # Last character can only be a letter or a digit
695 if (substr ($part, -1, 1) !~ /^[a-zA-Z0-9]*$/) {
696 return 0;}
697 }
698 return 1;
699}
700
701sub validfqdn
702{
703 my $part;
704
705 # Checks a fully qualified domain name against RFC1035
706 my $fqdn = $_[0];
707 my @parts = split (/\./, $fqdn); # Split hostname at the '.'
708 if (scalar(@parts) < 2) { # At least two parts should
709 return 0;} # exist in a FQDN
710 # (i.e. hostname.domain)
711 foreach $part (@parts) {
712 # Each part should be at least one character in length
713 # but no more than 63 characters
714 if (length ($part) < 1 || length ($part) > 63) {
715 return 0;}
716 # Only valid characters are a-z, A-Z, 0-9 and -
717 if ($part !~ /^[a-zA-Z0-9-]*$/) {
718 return 0;}
719 # First character can only be a letter or a digit
720 if (substr ($part, 0, 1) !~ /^[a-zA-Z0-9]*$/) {
721 return 0;}
722 # Last character can only be a letter or a digit
723 if (substr ($part, -1, 1) !~ /^[a-zA-Z0-9]*$/) {
724 return 0;}
725 }
726 return 1;
727}
728
729sub validportrange # used to check a port range
730{
731 my $port = $_[0]; # port values
732 $port =~ tr/-/:/; # replace all - with colons just in case someone used -
733 my $srcdst = $_[1]; # is it a source or destination port
734
735 if (!($port =~ /^(\d+)\:(\d+)$/)) {
736
737 if (!(&validport($port))) {
738 if ($srcdst eq 'src'){
739 return $Lang::tr{'source port numbers'};
740 } else {
741 return $Lang::tr{'destination port numbers'};
742 }
743 }
744 }
745 else
746 {
747 my @ports = ($1, $2);
748 if ($1 >= $2){
749 if ($srcdst eq 'src'){
750 return $Lang::tr{'bad source range'};
751 } else {
752 return $Lang::tr{'bad destination range'};
753 }
754 }
755 foreach $_ (@ports)
756 {
757 if (!(&validport($_))) {
758 if ($srcdst eq 'src'){
759 return $Lang::tr{'source port numbers'};
760 } else {
761 return $Lang::tr{'destination port numbers'};
762 }
763 }
764 }
765 return;
766 }
767}
768
769# Test if IP is within a subnet
770# Call: IpInSubnet (Addr, Subnet, Subnet Mask)
771# Subnet can be an IP of the subnet: 10.0.0.0 or 10.0.0.1
772# Everything in dottted notation
773# Return: TRUE/FALSE
774sub IpInSubnet
775{
cea4fc3a
MT
776 my $ip = unpack('N', &Socket::inet_aton(shift));
777 my $start = unpack('N', &Socket::inet_aton(shift));
778 my $mask = unpack('N', &Socket::inet_aton(shift));
779 $start &= $mask; # base of subnet...
780 my $end = $start + ~$mask;
781 return (($ip >= $start) && ($ip <= $end));
ac1cfefa
MT
782}
783
c545beb1
MT
784#
785# Return the following IP (IP+1) in dotted notation.
786# Call: NextIP ('1.1.1.1');
787# Return: '1.1.1.2'
788#
789sub NextIP
790{
791 return &Socket::inet_ntoa( pack("N", 1 + unpack('N', &Socket::inet_aton(shift))
792 )
793 );
794}
8c877a82
AM
795sub NextIP2
796{
797 return &Socket::inet_ntoa( pack("N", 4 + unpack('N', &Socket::inet_aton(shift))
798 )
799 );
800}
45762fc6
AF
801sub ipcidr
802{
803 my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift);
804 return "$ip\/$cidr";
805}
806
54fd0535
MT
807sub ipcidr2msk
808{
809 my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift);
810 my $netmask = &Net::IPv4Addr::ipv4_cidr2msk($cidr);
811 return "$ip\/$netmask";
812}
813
814
ac1cfefa
MT
815sub validemail {
816 my $mail = shift;
817 return 0 if ( $mail !~ /^[0-9a-zA-Z\.\-\_]+\@[0-9a-zA-Z\.\-]+$/ );
818 return 0 if ( $mail =~ /^[^0-9a-zA-Z]|[^0-9a-zA-Z]$/);
819 return 0 if ( $mail !~ /([0-9a-zA-Z]{1})\@./ );
820 return 0 if ( $mail !~ /.\@([0-9a-zA-Z]{1})/ );
821 return 0 if ( $mail =~ /.\.\-.|.\-\..|.\.\..|.\-\-./g );
822 return 0 if ( $mail =~ /.\.\_.|.\-\_.|.\_\..|.\_\-.|.\_\_./g );
823 return 0 if ( $mail !~ /\.([a-zA-Z]{2,4})$/ );
824 return 1;
825}
826
c545beb1
MT
827#
828# Currently only vpnmain use this three procs (readhasharray, writehasharray, findhasharray)
829# The 'key' used is numeric but is perfectly unneeded! This will to be removed so don't use
830# this code. Vpnmain will be splitted in parts: x509/pki, connection ipsec, connection other,... .
831#
ac1cfefa
MT
832sub readhasharray {
833 my ($filename, $hash) = @_;
834 %$hash = ();
835
836 open(FILE, $filename) or die "Unable to read file $filename";
837
838 while (<FILE>) {
839 my ($key, $rest, @temp);
840 chomp;
841 ($key, $rest) = split (/,/, $_, 2);
c545beb1 842 if ($key =~ /^[0-9]+$/) {
ac1cfefa
MT
843 @temp = split (/,/, $rest);
844 $hash->{$key} = \@temp;
845 }
846 }
847 close FILE;
848 return;
849}
850
851sub writehasharray {
852 my ($filename, $hash) = @_;
853 my ($key, @temp, $i);
854
855 open(FILE, ">$filename") or die "Unable to write to file $filename";
856
857 foreach $key (keys %$hash) {
8c877a82
AM
858 if ($key =~ /^[0-9]+$/) {
859 print FILE "$key";
860 foreach $i (0 .. $#{$hash->{$key}}) {
861 print FILE ",$hash->{$key}[$i]";
862 }
863 print FILE "\n";
864 }
ac1cfefa
MT
865 }
866 close FILE;
867 return;
868}
869
870sub findhasharraykey {
871 foreach my $i (1 .. 1000000) {
872 if ( ! exists $_[0]{$i}) {
873 return $i;
874 }
875 }
876}
877
878sub srtarray
879# Darren Critchley - darrenc@telus.net - (c) 2003
880# &srtarray(SortOrder, AlphaNumeric, SortDirection, ArrayToBeSorted)
881# This subroutine will take the following parameters:
882# ColumnNumber = the column which you want to sort on, starts at 1
883# AlphaNumberic = a or n (lowercase) defines whether the sort should be alpha or numberic
884# SortDirection = asc or dsc (lowercase) Ascending or Descending sort
885# ArrayToBeSorted = the array that wants sorting
886#
887# Returns an array that is sorted to your specs
888#
889# If SortOrder is greater than the elements in array, then it defaults to the first element
890#
891{
892 my ($colno, $alpnum, $srtdir, @tobesorted) = @_;
893 my @tmparray;
894 my @srtedarray;
895 my $line;
896 my $newline;
897 my $ctr;
898 my $ttlitems = scalar @tobesorted; # want to know the number of rows in the passed array
899 if ($ttlitems < 1){ # if no items, don't waste our time lets leave
900 return (@tobesorted);
901 }
902 my @tmp = split(/\,/,$tobesorted[0]);
903 $ttlitems = scalar @tmp; # this should be the number of elements in each row of the passed in array
904
905 # Darren Critchley - validate parameters
906 if ($colno > $ttlitems){$colno = '1';}
907 $colno--; # remove one from colno to deal with arrays starting at 0
908 if($colno < 0){$colno = '0';}
909 if ($alpnum ne '') { $alpnum = lc($alpnum); } else { $alpnum = 'a'; }
910 if ($srtdir ne '') { $srtdir = lc($srtdir); } else { $srtdir = 'src'; }
911
912 foreach $line (@tobesorted)
913 {
914 chomp($line);
915 if ($line ne '') {
916 my @temp = split(/\,/,$line);
917 # Darren Critchley - juggle the fields so that the one we want to sort on is first
918 my $tmpholder = $temp[0];
919 $temp[0] = $temp[$colno];
920 $temp[$colno] = $tmpholder;
921 $newline = "";
922 for ($ctr=0; $ctr < $ttlitems ; $ctr++) {
923 $newline=$newline . $temp[$ctr] . ",";
924 }
925 chop($newline);
926 push(@tmparray,$newline);
927 }
928 }
929 if ($alpnum eq 'n') {
930 @tmparray = sort {$a <=> $b} @tmparray;
931 } else {
932 @tmparray = (sort @tmparray);
933 }
934 foreach $line (@tmparray)
935 {
936 chomp($line);
937 if ($line ne '') {
938 my @temp = split(/\,/,$line);
939 my $tmpholder = $temp[0];
940 $temp[0] = $temp[$colno];
941 $temp[$colno] = $tmpholder;
942 $newline = "";
943 for ($ctr=0; $ctr < $ttlitems ; $ctr++){
944 $newline=$newline . $temp[$ctr] . ",";
945 }
946 chop($newline);
947 push(@srtedarray,$newline);
948 }
949 }
950
951 if ($srtdir eq 'dsc') {
952 @tmparray = reverse(@srtedarray);
953 return (@tmparray);
954 } else {
955 return (@srtedarray);
956 }
957}
958
959sub FetchPublicIp {
960 my %proxysettings;
961 &General::readhash("${General::swroot}/proxy/settings", \%proxysettings);
962 if ($_=$proxysettings{'UPSTREAM_PROXY'}) {
963 my ($peer, $peerport) = (/^(?:[a-zA-Z ]+\:\/\/)?(?:[A-Za-z0-9\_\.\-]*?(?:\:[A-Za-z0-9\_\.\-]*?)?\@)?([a-zA-Z0-9\.\_\-]*?)(?:\:([0-9]{1,5}))?(?:\/.*?)?$/);
964 Net::SSLeay::set_proxy($peer,$peerport,$proxysettings{'UPSTREAM_USER'},$proxysettings{'UPSTREAM_PASSWORD'} );
965 }
b2f8244a 966 my $user_agent = &MakeUserAgent();
0aa0cdcd 967 my ($out, $response) = Net::SSLeay::get_http( 'checkip4.dns.lightningwirelabs.com',
ac1cfefa
MT
968 80,
969 "/",
b2f8244a 970 Net::SSLeay::make_headers('User-Agent' => $user_agent )
ac1cfefa
MT
971 );
972 if ($response =~ m%HTTP/1\.. 200 OK%) {
5a2935b1 973 $out =~ /Your IP address is: (\d+.\d+.\d+.\d+)/;
ac1cfefa
MT
974 return $1;
975 }
976 return '';
977}
978
979#
980# Check if hostname.domain provided have IP provided
981# use gethostbyname to verify that
982# Params:
983# IP
984# hostname
985# domain
986# Output
987# 1 IP matches host.domain
988# 0 not in sync
989#
990sub DyndnsServiceSync ($;$;$) {
991
992 my ($ip,$hostName,$domain) = @_;
993 my @addresses;
994
995 #fix me no ip GROUP, what is the name ?
996 $hostName =~ s/$General::noipprefix//;
997 if ($hostName) { #may be empty
998 $hostName = "$hostName.$domain";
999 @addresses = gethostbyname($hostName);
1000 }
1001
1002 if ($addresses[0] eq '') { # nothing returned ?
1003 $hostName = $domain; # try resolving with domain only
1004 @addresses = gethostbyname($hostName);
1005 }
1006
1007 if ($addresses[0] ne '') { # got something ?
1008 #&General::log("name:$addresses[0], alias:$addresses[1]");
1009 # Build clear text list of IP
1010 @addresses = map ( &Socket::inet_ntoa($_), @addresses[4..$#addresses]);
1011 if (grep (/$ip/, @addresses)) {
1012 return 1;
1013 }
1014 }
1015 return 0;
1016}
1017#
1018# This sub returns the red IP used to compare in DyndnsServiceSync
1019#
1020sub GetDyndnsRedIP {
1021 my %settings;
1022 &General::readhash("${General::swroot}/ddns/settings", \%settings);
1023
1024 open(IP, "${General::swroot}/red/local-ipaddress") or return 'unavailable';
1025 my $ip = <IP>;
1026 close(IP);
1027 chomp $ip;
1028
057dbeeb 1029 # 100.64.0.0/10 is reserved for dual-stack lite (http://tools.ietf.org/html/rfc6598).
ac1cfefa
MT
1030 if (&General::IpInSubnet ($ip,'10.0.0.0','255.0.0.0') ||
1031 &General::IpInSubnet ($ip,'172.16.0.0.','255.240.0.0') ||
057dbeeb
MT
1032 &General::IpInSubnet ($ip,'192.168.0.0','255.255.0.0') ||
1033 &General::IpInSubnet ($ip,'100.64.0.0', '255.192.0.0'))
ac1cfefa
MT
1034 {
1035 if ($settings{'BEHINDROUTER'} eq 'FETCH_IP') {
1036 my $RealIP = &General::FetchPublicIp;
1037 $ip = (&General::validip ($RealIP) ? $RealIP : 'unavailable');
1038 }
1039 }
1040 return $ip;
1041}
c545beb1
MT
1042
1043# Translate ICMP code to text
1044# ref: http://www.iana.org/assignments/icmp-parameters
1045sub GetIcmpDescription ($) {
1046 my $index = shift;
1047 my @icmp_description = (
1048 'Echo Reply', #0
1049 'Unassigned',
1050 'Unassigned',
1051 'Destination Unreachable',
1052 'Source Quench',
1053 'Redirect',
1054 'Alternate Host Address',
1055 'Unassigned',
1056 'Echo',
1057 'Router Advertisement',
1058 'Router Solicitation', #10
1059 'Time Exceeded',
1060 'Parameter Problem',
1061 'Timestamp',
1062 'Timestamp Reply',
1063 'Information Request',
1064 'Information Reply',
1065 'Address Mask Request',
1066 'Address Mask Reply',
1067 'Reserved (for Security)',
1068 'Reserved (for Robustness Experiment)', #20
1069 'Reserved',
1070 'Reserved',
1071 'Reserved',
1072 'Reserved',
1073 'Reserved',
1074 'Reserved',
1075 'Reserved',
1076 'Reserved',
1077 'Reserved',
1078 'Traceroute', #30
1079 'Datagram Conversion Error',
1080 'Mobile Host Redirect',
1081 'IPv6 Where-Are-You',
1082 'IPv6 I-Am-Here',
1083 'Mobile Registration Request',
1084 'Mobile Registration Reply',
1085 'Domain Name Request',
1086 'Domain Name Reply',
1087 'SKIP',
1088 'Photur', #40
1089 'Experimental');
a2b3eba9 1090 if ($index>41) {return 'unknown'} else {return $icmp_description[$index]};
c545beb1 1091}
b2f8244a
MT
1092
1093sub GetCoreUpdateVersion() {
1094 my $core_update;
1095
1096 open(FILE, "/opt/pakfire/db/core/mine");
1097 while (<FILE>) {
1098 $core_update = $_;
1099 last;
1100 }
1101 close(FILE);
1102
1103 return $core_update;
1104}
1105
1106sub MakeUserAgent() {
1107 my $user_agent = "IPFire/$General::version";
1108
1109 my $core_update = &GetCoreUpdateVersion();
1110 if ($core_update ne "") {
1111 $user_agent .= "/$core_update";
1112 }
1113
1114 return $user_agent;
1115}
1116
61027579
MT
1117sub RedIsWireless() {
1118 # This function checks if a network device is a wireless device.
1119
1120 my %settings = ();
1121 &readhash("${General::swroot}/ethernet/settings", \%settings);
1122
1123 # Find the name of the network device.
1124 my $device = $settings{'RED_DEV'};
1125
1126 # Exit, if no device is configured.
1127 return 0 if ($device eq "");
1128
1129 # Return 1 if the device is a wireless one.
1130 my $path = "/sys/class/net/$device/wireless";
1131 if (-d $path) {
1132 return 1;
1133 }
1134
1135 # Otherwise return zero.
1136 return 0;
1137}
1138
dfee7582
SS
1139# Function to read a file with UTF-8 charset.
1140sub read_file_utf8 ($) {
1141 my ($file) = @_;
1142
1143 open my $in, '<:encoding(UTF-8)', $file or die "Could not open '$file' for reading $!";
1144 local $/ = undef;
1145 my $all = <$in>;
1146 close $in;
1147
3e862ce4 1148 return $all;
dfee7582
SS
1149}
1150
1151# Function to write a file with UTF-8 charset.
1152sub write_file_utf8 ($) {
1153 my ($file, $content) = @_;
1154
1155 open my $out, '>:encoding(UTF-8)', $file or die "Could not open '$file' for writing $!";;
1156 print $out $content;
1157 close $out;
1158
1159 return;
1160}
1161
6d8eb5de 1162my $FIREWALL_RELOAD_INDICATOR = "${General::swroot}/firewall/reread";
0e430797
MT
1163
1164sub firewall_config_changed() {
1165 open FILE, ">$FIREWALL_RELOAD_INDICATOR" or die "Could not open $FIREWALL_RELOAD_INDICATOR";
1166 close FILE;
1167}
1168
1169sub firewall_needs_reload() {
1170 if (-e "$FIREWALL_RELOAD_INDICATOR") {
1171 return 1;
1172 }
1173
1174 return 0;
1175}
1176
1177sub firewall_reload() {
8039a710 1178 system("/usr/local/bin/firewallctrl");
0e430797
MT
1179}
1180
4cb523d4
SS
1181# Function which will return the used interface for the red network zone (red0, ppp0, etc).
1182sub get_red_interface() {
1183
1184 open(IFACE, "${General::swroot}/red/iface") or die "Could not open /var/ipfire/red/iface";
1185
1186 my $interface = <IFACE>;
1187 close(IFACE);
1188 chomp $interface;
1189
1190 return $interface;
1191}
1192
ac1cfefa 11931;