Handle hostnames with upper-case letters
[webmin.git] / net / debian-linux-lib.pl
1 # debian-linux-lib.pl
2 # Networking functions for Debian linux >= 2.2 (aka. potato)
3 # Really, this won't work with releases prior to 2.2, don't even try it.
4 #
5 # Rene Mayrhofer, July 2000
6 # Some code has been taken from redhat-linux-lib.pl
7
8 use File::Copy;
9
10 $network_interfaces_config = '/etc/network/interfaces';
11 $modules_config = '/etc/modprobe.d/arch/i386';
12 if (!-d $modules_config) {
13         ($modules_config) = glob('/etc/modprobe.d/arch/*');
14         }
15 $network_interfaces = '/proc/net/dev';
16
17 do 'linux-lib.pl';
18
19 # boot_interfaces()
20 # Returns a list of interfaces brought up at boot time
21 sub boot_interfaces
22 {
23 my @ifaces = &get_interface_defs();
24 my @autos = &get_auto_defs();
25 my @rv;
26 my %v6map;
27 foreach $iface (@ifaces) {
28         my ($name, $addrfam, $method, $options) = @$iface;
29         if ($addrfam eq 'inet') {
30                 # IPv4 interface .. parse and add to list
31                 my $cfg = { };
32                 $cfg->{'fullname'} = $name;
33                 if ($cfg->{'fullname'} =~ /(\S+):(\d+)/) {
34                         $cfg->{'name'} = $1;
35                         $cfg->{'virtual'} = $2;
36                         }
37                 else {
38                         $cfg->{'name'} = $cfg->{'fullname'};
39                         }
40                 if ($cfg->{'fullname'} =~ /^br(\d+)$/) {
41                         $cfg->{'bridge'} = 1;
42                         }
43                 if ($gconfig{'os_version'} >= 3 || scalar(@autos)) {
44                         $cfg->{'up'} = &indexof($name, @autos) >= 0;
45                         }
46                 else {
47                         $cfg->{'up'} = 1;
48                         }
49                 foreach $option (@$options) {
50                         my ($param, $value) = @$option;
51                         if ($param eq 'noauto') {
52                                 $cfg->{'up'} = 0;
53                                 }
54                         elsif($param eq 'up') { 
55                                 $cfg->{"partner"} =
56                                   &get_teaming_partner($cfg->{'name'}, $value);
57                                 %options = &get_module_defs($name);
58                                 $cfg->{'mode'} = $options{'mode'};
59                                 $cfg->{'miimon'} = $options{'miimon'};
60                                 $cfg->{'downdelay'} = $options{'downdelay'};
61                                 $cfg->{'updelay'} = $options{'updelay'};
62                                 }
63                         elsif($param eq 'bond_mode') { 
64                                 $cfg->{'mode'} = $value;
65                                 }
66                         elsif($param eq 'bond_miimon') { 
67                                 $cfg->{'miimon'} = $value;
68                                 }
69                         elsif($param eq 'bond_downdelay') { 
70                                 $cfg->{'downdelay'} = $value;
71                                 }
72                         elsif($param eq 'bond_updelay') { 
73                                 $cfg->{'updelay'} = $value;
74                                 }
75                         elsif($param eq 'slaves') { 
76                                 $cfg->{'partner'} = $value;
77                                 }
78                         elsif($param eq 'hwaddr') {
79                                 local @v = split(/\s+/, $value);
80                                 $cfg->{'ether_type'} = $v[0];
81                                 $cfg->{'ether'} = $v[1];
82                                 }
83                         elsif ($param eq 'bridge_ports') {
84                                 $cfg->{'bridgeto'} = $value;
85                                 }
86                         elsif ($param eq 'pre-up' &&
87                                $value =~ /brctl\s+addif\s+br\d+\s+(\S+)/) {
88                                 $cfg->{'bridgeto'} = $1;
89                                 }
90                         else {
91                                 $cfg->{$param} = $value;
92                                 }
93                         }
94                 $cfg->{'dhcp'} = ($method eq 'dhcp');
95                 $cfg->{'bootp'} = ($method eq 'bootp');
96                 $cfg->{'edit'} = ($cfg->{'name'} !~ /^ppp|lo/);
97                 $cfg->{'index'} = scalar(@rv);  
98                 $cfg->{'file'} = $network_interfaces_config;
99                 if (!$cfg->{'broadcast'} &&
100                     $cfg->{'address'} && $cfg->{'netmask'}) {
101                         $cfg->{'broadcast'} = &compute_broadcast(
102                                 $cfg->{'address'}, $cfg->{'netmask'});
103                         }
104                 push(@rv, $cfg);
105                 }
106         elsif ($addrfam eq "inet6") {
107                 # IPv6 interface .. add to matching v4 block
108                 my $v6cfg = { 'address6' => [ ],
109                               'netmask6' => [ ] };
110                 foreach $option (@$options) {
111                         my ($param, $value) = @$option;
112                         if ($param eq "address") {
113                                 push(@{$v6cfg->{'address6'}}, $value);
114                                 }
115                         elsif ($param eq "netmask") {
116                                 push(@{$v6cfg->{'netmask6'}}, $value);
117                                 }
118                         elsif ($param eq "up" &&
119                                $value =~ /ifconfig\s+(\S+)\s+inet6\s+add\s+([a-f0-9:]+)\/(\d+)/ &&
120                                 $1 eq $name) {
121                                 # Additional v6 address
122                                 push(@{$v6cfg->{'address6'}}, $2);
123                                 push(@{$v6cfg->{'netmask6'}}, $3);
124                                 }
125                         }
126                 if ($method eq "manual" && !@{$v6cfg->{'address6'}}) {
127                         $v6cfg->{'auto6'} = 1;
128                         }
129                 $v6map{$name} = $v6cfg;
130                 }
131         }
132 # Merge in v6 interface settings
133 foreach my $iface (@rv) {
134         my $v6cfg = $v6map{$iface->{'fullname'}};
135         if ($v6cfg) {
136                 foreach my $k (keys %$v6cfg) {
137                         $iface->{$k} = $v6cfg->{$k};
138                         }
139                 }
140         }
141 return @rv;
142 }
143
144 # save_interface(&details)
145 # Create or update a boot-time interface
146 sub save_interface
147 {
148 my $cfg = $_[0];
149 my $name = $cfg->{'virtual'} ne "" ? $cfg->{'name'}.":".$cfg->{'virtual'}
150                                        : $cfg->{'name'};
151 my @options;
152 my $method;
153 my @modules_var;
154 if ($cfg->{'dhcp'} == 1) {
155         $method = 'dhcp';
156         }
157 elsif ($cfg->{'bootp'} == 1) {
158         $method = 'bootp';
159         }
160 elsif ($cfg->{'address'}) {
161         $method = 'static';
162         push(@options, ['address', $cfg->{'address'}]);
163         push(@options, ['netmask', $cfg->{'netmask'}]);
164         push(@options, ['broadcast', $cfg->{'broadcast'}])
165                 if ($cfg->{'broadcast'});
166         my ($ip1, $ip2, $ip3, $ip4) = split(/\./, $cfg->{'address'});
167         my ($nm1, $nm2, $nm3, $nm4) = split(/\./, $cfg->{'netmask'});
168         if ($cfg->{'address'} && $cfg->{'netmask'}) {
169                 my $network = sprintf "%d.%d.%d.%d",
170                                         ($ip1 & int($nm1))&0xff,
171                                         ($ip2 & int($nm2))&0xff,
172                                         ($ip3 & int($nm3))&0xff,
173                                         ($ip4 & int($nm4))&0xff;
174                 push(@options, ['network', $network]);
175                 }
176         }
177 else {
178         $method = 'manual';
179         }
180 my @autos = get_auto_defs();
181 my $amode = $gconfig{'os_version'} > 3 || scalar(@autos);
182 if (!$cfg->{'up'} && !$amode) { push(@options, ['noauto', '']); }
183 if ($cfg->{'ether'}) {
184         push(@options, [ 'hwaddr', ($cfg->{'ether_type'} || 'ether').' '.
185                                    $cfg->{'ether'} ]);
186         }
187 if ($cfg->{'bridge'}) {
188         &has_command("brctl") || &error("Bridges cannot be created unless the ".
189                                         "brctl command is installed");
190         push(@options, [ 'bridge_ports', $cfg->{'bridgeto'} ]);
191         }
192
193 # Set bonding parameters
194 if(($cfg->{'bond'} == 1) && ($gconfig{'os_version'} >= 5)) {
195         push(@options, ['bond_mode '.$cfg->{'mode'}]);
196         push(@options, ['bond_miimon '.$cfg->{'miimon'}]);
197         push(@options, ['bond_updelay '.$cfg->{'updelay'}]);
198         push(@options, ['bond_downdelay '.$cfg->{'downdelay'}]);
199         push(@options, ['slaves '.$cfg->{'partner'}]);
200         }
201 elsif ($cfg->{'bond'} == 1) {
202         push(@options, ['up', '/sbin/ifenslave '.$cfg->{'name'}." ".
203                               $cfg->{'partner'}]);
204         push(@options, ['down', '/sbin/ifenslave -d '.$cfg->{'name'}." ".
205                                 $cfg->{'partner'}]);
206         }
207
208 # Set specific lines for vlan tagging
209 if(($cfg->{'vlan'} == 1) && ($gconfig{'os_version'} >= 5)) {
210         push(@options, ['vlan_raw_device '.$cfg->{'physical'}]);
211         }
212 elsif($cfg->{'vlan'} == 1){
213         push(@options, ['pre-up', 'vconfig add '.$cfg->{'physical'}.' '.
214                                   $cfg->{'vlanid'}]);
215         push(@options, ['post-down', 'vconfig rem '.$cfg->{'physical'}.' '.
216                                      $cfg->{'vlanid'}]);
217         }
218
219 # Find the existing interface section
220 my @ifaces = get_interface_defs();
221 my $found = 0;
222 my $found6 = 0;
223 foreach $iface (@ifaces) {
224         local $address;
225         foreach my $opt (@{$iface->[3]}) {
226                 if($opt->[0] eq 'address'){
227                         $address = $opt->[1];
228                         last;
229                         }
230                 }
231         if ($iface->[0] eq $cfg->{'fullname'} && $iface->[1] eq 'inet') {
232                 # Found interface to change
233                 $found = 1;
234                 foreach my $o (@{$iface->[3]}) {
235                         if ($o->[0] eq 'gateway' ||
236                             $o->[0] eq 'pre-up' && $o->[1] =~ /brctl/) {
237                                 push(@options, $o);
238                                 }
239                         }
240                 }
241         if ($iface->[0] eq $cfg->{'fullname'} && $iface->[1] eq 'inet6') {
242                 # Found IPv6 block
243                 $found6 = 1;
244                 }
245         }
246
247 if (!$found) {
248         # Add a new interface section
249         if ($in{'vlan'} == 1) {
250                 &new_interface_def($cfg->{'physical'}.'.'.$cfg->{'vlanid'},
251                                    'inet', $method, \@options);
252                 }
253         else {
254                 &new_interface_def($cfg->{'fullname'},
255                                    'inet', $method, \@options);
256                 }
257         if ($cfg->{'bond'} == 1 && $gconfig{'os_version'} >= 5) {
258                 # Not sure why nothing needs to be done here?
259                 }
260         elsif ($cfg->{'bond'} == 1) {
261                 &new_module_def($cfg->{'fullname'}, $cfg->{'mode'},
262                                 $cfg->{'miimon'}, $cfg->{'downdelay'},
263                                 $cfg->{'updelay'});
264                 }
265         } 
266 else {
267         # Update existing section
268         if($in{'vlan'} == 1) {
269                 &modify_interface_def($cfg->{'physical'}.'.'.$cfg->{'vlanid'},
270                                       'inet', $method, \@options, 0);
271                 }
272         else {
273                 &modify_interface_def($cfg->{'fullname'},
274                                       'inet', $method, \@options, 0);
275                 }
276         if ($cfg->{'bond'} == 1 && $gconfig{'os_version'} >= 5) {
277                 # Not sure why nothing needs to be done here?
278                 }
279         elsif ($cfg->{'bond'} == 1) {
280                 &modify_module_def($cfg->{'fullname'}, 0, $cfg->{'mode'},
281                                    $cfg->{'miimon'}, $cfg->{'downdelay'},
282                                    $cfg->{'updelay'});
283                 }
284         }
285
286 # Create IPv6 options
287 my @options6;
288 my @address6 = @{$cfg->{'address6'}};
289 my @netmask6 = @{$cfg->{'netmask6'}};
290 if (@address6 || $cfg->{'auto6'}) {
291         push(@options6, ['pre-up', '/sbin/modprobe -q ipv6 ; /bin/true']);
292         }
293 if (@address6) {
294         push(@options6, [ "address", shift(@address6) ]);
295         push(@options6, [ "netmask", shift(@netmask6) ]);
296         }
297 while(@address6) {
298         my $a = shift(@address6);
299         my $n = shift(@netmask6);
300         push(@options6, [ "up","ifconfig $cfg->{'fullname'} inet6 add $a/$n" ]);
301         }
302
303 # Add, update or delete IPv6 inteface
304 my $method = $cfg->{'auto6'} ? "manual" : "static";
305 if (!$found6 && @options6) {
306         # Need to add IPv6 block
307         &new_interface_def($cfg->{'fullname'},
308                            'inet6', $method, \@options6);
309         }
310 elsif ($found6 && @options6) {
311         # Need to update IPv6 block
312         &modify_interface_def($cfg->{'fullname'},
313                               'inet6', $method, \@options6, 0);
314         }
315 elsif ($found6 && !@options6) {
316         # Need to delete IPv6 block
317         &delete_interface_def($cfg->{'fullname'}, 'inet6');
318         }
319
320 # Set auto option to include this interface, or not
321 if ($amode) {
322         if ($cfg->{'up'}) {
323                 if($in{'vlan'} == 1) {
324                         @autos = &unique(@autos, $cfg->{'physical'}.'.'.
325                                                  $cfg->{'vlanid'});
326                         }
327                 else {
328                         @autos = &unique(@autos, $cfg->{'fullname'});
329                         }
330                 }
331         else {
332                 @autos = grep { $_ ne $cfg->{'fullname'} } @autos;
333                 }
334         &modify_auto_defs(@autos);
335         }
336 }
337
338 # Modifies a entry in /etc/modprobe.d/arch/i386 that concerns
339 # to the interface mentioned
340 # modify_module_def(name, delete, mode, miimon, downdelay, updelay)
341 sub modify_module_def
342 {
343         return if (!$modules_config);
344         my ($name, $delete, $mode, $miimon, $downdelay, $updelay) = @_;
345         my $modify_block = 0;
346         
347         # make a backup copy
348         copy("$modules_config", "$modules_config~");
349         local *OLDCFGFILE, *NEWCFGFILE;
350         &open_readfile(OLDCFGFILE, "$modules_config~") ||
351                 error("Unable to open $modules_config");
352         &lock_file($network_interfaces_config);
353         &open_tempfile(NEWCFGFILE, "> $modules_config", 1) ||
354                 error("Unable to open $modules_config");
355
356                 
357         while (defined ($line=<OLDCFGFILE>)) {
358                 chomp($line);
359                 @splitted_line = split(" ", $line);
360                 if($splitted_line[0] eq 'alias' && $splitted_line[1] eq $name) {
361                         # Found start of block we are changing
362                         $modify_block = 1;              
363                         if (!$delete) {
364                                 &print_tempfile(NEWCFGFILE, $line . "\n");
365                                 }
366                 } elsif ($splitted_line[0] eq 'alias' && !($splitted_line[1] eq $name)){
367                         # Found start of another block
368                         $modify_block = 0;
369                 }
370                 
371                 # if $delete == 1; write nothing
372                 if($modify_block == 1 && $splitted_line[0] eq "options" && $delete != 1) {
373                         $options_line = "options bonding ";
374                         for($i = 2; $i < scalar(@splitted_line); $i++){
375                                         ($key, $value) = split("=", $splitted_line[$i]);
376                                         if($key eq "mode") {
377                                                 $options_line .= "mode=" . $mode . " ";
378                                         } elsif($key eq "miimon") {
379                                                 $options_line .= "miimon=" . $miimon . " ";
380                                         } elsif($key eq "downdelay") {
381                                                 $options_line .= "downdelay=" . $downdelay . " ";
382                                         } elsif($key eq "updelay") {
383                                                 $options_line .= "updelay=" . $updelay . " ";
384                                         } else {
385                                                 $options_line .= $key . "=" . $value . " ";
386                                         }                       
387                         }
388                         
389                         chop($options_line);
390                         &print_tempfile(NEWCFGFILE, $options_line . "\n");
391                         $modify_block == 0;
392                 } elsif($modify_block == 0) {
393                         &print_tempfile(NEWCFGFILE, $line . "\n");
394                 }
395         }
396         &close_tempfile(NEWCFGFILE);
397         &unlock_file($modules_config);
398 }
399
400 # Deletes an the module concerning entry
401 # delete_module_def(name) 1 for deleting operation
402 sub delete_module_def
403 {
404         my ($name) = @_;
405         modify_interface_def($name, 1);
406 }
407
408 # get_module_defs(device)
409 # Returns the modul options form /etc/modprobe.d/arch/i386
410 # for a special device
411 # Return hash: ($mode, $miimon, $downdelay, $updelay)
412 sub get_module_defs
413 {
414         return ( ) if (!$modules_config);
415         local *CFGFILE;
416         my($device) = @_;
417         my %ret;
418         &open_readfile(CFGFILE, $modules_config) ||
419                 error("Unable to open $modules_config");
420         
421         $line = <CFGFILE>;
422         while(defined($line)){
423                 chomp($line);
424                 @params = split(" ", $line);
425                 
426                 # Search for an entry concerning to the device
427                 if($params[0] eq "alias" && $params[1] eq $device){
428                         $line = <CFGFILE>;
429                         chomp $line;
430                         @params = split(" ", $line);            
431                         # Check if it is an options line
432                         if($params[0] eq "options" && $params[1] eq "bonding") {
433                                 for($i = 2; $i < scalar(@params); $i++){
434                                         ($key, $value) = split("=", $params[$i]);
435                                         $ret{$key} = $value;    
436                                 }
437                         }               
438                 }
439                 $line = <CFGFILE>;              
440         }
441         return %ret;
442 }
443
444 # creates a new entry for in the modules file
445 # Parameters should be (name, mode, miimon, downdelay, updelay)
446 sub new_module_def
447 {
448         local ($name, $mode, $miimon, $downdelay, $updelay) = @_;
449         return if (!$modules_config);
450         copy("$modules_config", "$modules_config~");
451         local *CFGFILE;
452         &open_lock_tempfile(CFGFILE, ">> $modules_config") ||
453                 error("Unable to open $modules_config");
454         &print_tempfile(CFGFILE, "alias " . $name . " bonding");
455         &print_tempfile(CFGFILE, "\noptions bonding");
456         
457         if($mode ne '') {
458                 &print_tempfile(CFGFILE, " mode=" . $mode);
459         }
460         if($miimon ne '') {
461                 &print_tempfile(CFGFILE, " miimon=" . $miimon);
462         }
463         if($downdelay ne '') {
464                 &print_tempfile(CFGFILE, " downdelay=" . $downdelay);
465         }
466         if($updelay ne '') {
467                 &print_tempfile(CFGFILE, " updelay=" . $updelay);
468         }
469         # Add Newline
470         &print_tempfile(CFGFILE, "\n");
471         &close_tempfile(CFGFILE);
472 }
473
474 # delete_interface(&details)
475 # Delete a boot-time interface
476 sub delete_interface
477 {
478 my $cfg = $_[0];
479 &delete_interface_def($cfg->{'fullname'}, 'inet');
480 if (@{$cfg->{'address6'}}) {
481         &delete_interface_def($cfg->{'fullname'}, 'inet6');
482         }
483 my @autos = get_auto_defs();
484 if ($gconfig{'os_version'} >= 3 || scalar(@autos)) {
485         @autos = grep { $_ ne $cfg->{'fullname'} } @autos;
486         &modify_auto_defs(@autos);
487         }
488 }
489
490 # can_edit(what)
491 # Can some boot-time interface parameter be edited?
492 sub can_edit
493 {
494 return $_[0] ne "mtu";
495 }
496
497 # valid_boot_address(address)
498 # Is some address valid for a bootup interface
499 sub valid_boot_address
500 {
501 return &check_ipaddress_any($_[0]);
502 }
503
504 # get_hostname()
505 sub get_hostname
506 {
507 local $hn = &read_file_contents("/etc/hostname");
508 $hn =~ s/\r|\n//g;
509 if ($hn) {
510         return $hn;
511         }
512 return &get_system_hostname(1);
513 }
514
515 # save_hostname(name)
516 sub save_hostname
517 {
518 local (%conf, $f);
519 &system_logged("hostname $_[0] >/dev/null 2>&1");
520 foreach $f ("/etc/hostname", "/etc/HOSTNAME", "/etc/mailname") {
521         if (-r $f) {
522                 &open_lock_tempfile(HOST, ">$f");
523                 &print_tempfile(HOST, $_[0],"\n");
524                 &close_tempfile(HOST);
525                 }
526         }
527 undef(@main::get_system_hostname);      # clear cache
528 }
529
530 # get_domainname()
531 sub get_domainname
532 {
533 local $d;
534 &execute_command("domainname", undef, \$d, undef);
535 chop($d);
536 return $d;
537 }
538
539 # save_domainname(domain)
540 sub save_domainname
541 {
542 local %conf;
543 &execute_command("domainname ".quotemeta($_[0]));
544 }
545
546 sub routing_config_files
547 {
548 return ( $network_interfaces_config );
549 }
550
551 sub network_config_files
552 {
553 return ( "/etc/hostname", "/etc/HOSTNAME", "/etc/mailname" );
554 }
555
556 # show default router and device
557 sub routing_input
558 {
559 local ($addr, $router) = &get_default_gateway();
560 local ($addr6, $router6) = &get_default_ipv6_gateway();
561 local @ifaces = grep { $_->[1] eq 'inet' && $_->[0] ne 'lo' }
562                      &get_interface_defs();
563 local @ifaces6 = grep { $_->[1] eq 'inet6' && $_->[0] ne 'lo' }
564                       &get_interface_defs();
565
566 # Show default gateway
567 print &ui_table_row($text{'routes_default'},
568         &ui_radio("gateway_def", $addr ? 0 : 1,
569                   [ [ 1, $text{'routes_none'} ],
570                     [ 0, $text{'routes_gateway'}." ".
571                          &ui_textbox("gateway", $addr, 15)." ".
572                          &ui_select("gatewaydev", $router,
573                                 [ map { $_->[0] } @ifaces ]) ] ]));
574
575 if (@ifaces6) {
576         # Show default IPv6 gateway
577         print &ui_table_row($text{'routes_default6'},
578                 &ui_radio("gateway6_def", $addr6 ? 0 : 1,
579                           [ [ 1, $text{'routes_none'} ],
580                             [ 0, $text{'routes_gateway'}." ".
581                                  &ui_textbox("gateway6", $addr6, 30)." ".
582                                  &ui_select("gatewaydev6", $router6,
583                                         [ map { $_->[0] } @ifaces6 ]) ] ]));
584         }
585
586 # Get static routes
587 local ($d, @st, @hr);
588 foreach $d (@ifaces) {
589         local ($name, $addrfam, $method, $options) = @$d;
590         local $o;
591         local $onum = -1;
592         foreach $o (@$options) {
593                 $onum++;
594                 next if ($o->[0] ne "up");
595                 if ($o->[1] =~ /^ip\s+route\s+add\s+([0-9\.]+)\/(\d+)\s+dev\s+(\S+)/) {
596                         push(@hr, [ $name, $1, &prefix_to_mask($2) ]);
597                         }
598                 elsif ($o->[1] =~ /^ip\s+route\s+add\s+([0-9\.]+)\/(\d+)\s+via\s+(\S+)/) {
599                         push(@st, [ $name, $1, &prefix_to_mask($2), $3 ]);
600                         }
601                 }
602         }
603
604 # Show static routes via gateways
605 my @table;
606 for($i=0; $i<=@st; $i++) {
607         local $st = $st[$i];
608         push(@table, [ &ui_textbox("dev_$i", $st->[0], 6),
609                        &ui_textbox("net_$i", $st->[1], 15),
610                        &ui_textbox("netmask_$i", $st->[2], 15),
611                        &ui_textbox("gw_$i", $st->[3], 15), ]);
612         }
613 print &ui_table_row($text{'routes_static'},
614         &ui_columns_table([ $text{'routes_ifc'}, $text{'routes_net'},
615                             $text{'routes_mask'}, $text{'routes_gateway'} ],
616                           undef, \@table, undef, 1));
617
618 # Show static host routes
619 my @table;
620 for($i=0; $i<=@hr; $i++) {
621         local $st = $hr[$i];
622         push(@table, [ &ui_textbox("ldev_$i", $st->[0], 6),
623                        &ui_textbox("lnet_$i", $st->[1], 15),
624                        &ui_textbox("lnetmask_$i", $st->[2], 15) ]);
625         }
626 print &ui_table_row($text{'routes_local'},
627         &ui_columns_table([ $text{'routes_ifc'}, $text{'routes_net'},
628                             $text{'routes_mask'} ],
629                           undef, \@table, undef, 1));
630 }
631
632 sub parse_routing
633 {
634 # Save IPv4 address
635 local ($dev, $gw);
636 if (!$in{'gateway_def'}) {
637         &check_ipaddress($in{'gateway'}) ||
638                 &error(&text('routes_egateway', $in{'gateway'}));
639         $gw = $in{'gateway'};
640         $dev = $in{'gatewaydev'};
641         }
642 &set_default_gateway($gw, $dev);
643
644 # Save IPv6 address
645 local @ifaces6 = grep { $_->[1] eq 'inet6' && $_->[0] ne 'lo' }
646                       &get_interface_defs();
647 if (@ifaces6) {
648         local ($dev6, $gw6);
649         if (!$in{'gateway6_def'}) {
650                 &check_ip6address($in{'gateway6'}) ||
651                         &error(&text('routes_egateway6', $in{'gateway6'}));
652                 $gw6 = $in{'gateway6'};
653                 $dev6 = $in{'gatewaydev6'};
654                 }
655         &set_default_ipv6_gateway($gw6, $dev6);
656         }
657
658 # Parse static and local routes
659 local %st;
660 local $i;
661 local $dev;
662 for($i=0; defined($dev = $in{"dev_$i"}); $i++) {
663         next if (!$dev);
664         local $net = $in{"net_$i"};
665         local $netmask = $in{"netmask_$i"};
666         local $gw = $in{"gw_$i"};
667         $dev =~ /^\S+$/ || &error(&text('routes_edevice', $dev));
668         &to_ipaddress($net) || &error(&text('routes_enet', $net));
669         &check_ipaddress_any($netmask) ||
670                 &error(&text('routes_emask', $netmask));
671         &to_ipaddress($gw) || &error(&text('routes_egateway', $gw));
672         local $prefix = &mask_to_prefix($netmask);
673         push(@{$st{$dev}}, [ "up", "ip route add $net/$prefix via $gw" ]);
674         }
675 local %hr;
676 for($i=0; defined($dev = $in{"ldev_$i"}); $i++) {
677         local $net = $in{"lnet_$i"};
678         local $netmask = $in{"lnetmask_$i"};
679         next if (!$dev && !$net);
680         $dev =~ /^\S+$/ || &error(&text('routes_edevice', $dev));
681         &to_ipaddress($net) ||
682             $net =~ /^(\S+)\/(\d+)$/ && &to_ipaddress("$1") ||
683                 &error(&text('routes_enet', $net));
684         &check_ipaddress_any($netmask) ||
685                 &error(&text('routes_emask', $netmask));
686         local $prefix = &mask_to_prefix($netmask);
687         push(@{$hr{$dev}}, [ "up", "ip route add $net/$prefix dev $dev" ]);
688         }
689
690 # Replace old routing directives
691 local @ifaces = &get_interface_defs();
692 foreach $iface (@ifaces) {
693         local @o = @{$iface->[3]};
694         @o = grep { $_->[0] ne "up" ||
695                     $_->[1] !~ /^ip\s+route\s+add/ } @o;
696         push(@o, @{$st{$iface->[0]}});
697         push(@o, @{$hr{$iface->[0]}});
698         $iface->[3] = \@o;
699         &modify_interface_def($iface->[0], $iface->[1], $iface->[2],
700                               $iface->[3], 0);
701         }
702 }
703
704
705 ###############################################################################
706 # helper functions for file-internal use
707
708 # gets a list of interface definitions (including their options) from the
709 # central config file
710 # the returned list is an array whose contents are tupels of
711 # (name, addrfam, method, options) with
712 #    name          the interface name (e.g. eth0)
713 #    addrfam       the address family (e.g. inet, inet6)
714 #    method        the address activation method (e.g. static, dhcp, loopback)
715 #    options       is a list of (param, value) pairs
716 sub get_interface_defs
717 {
718 local *CFGFILE;
719 my @ret;
720 &open_readfile(CFGFILE, $network_interfaces_config) ||
721         error("Unable to open $network_interfaces_config");
722 # read the file line by line
723 $line = <CFGFILE>;
724 while (defined $line) {
725         chomp($line);
726         # skip comments
727         if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
728                 $line = <CFGFILE>;
729                 next;
730                 }
731
732         if ($line =~ /^\s*auto/) {
733                 # skip auto stanzas
734                 $line = <CFGFILE>;
735                 while(defined($line) && $line !~ /^\s*(iface|mapping|auto)/) {
736                         $line = <CFGFILE>;
737                         next;
738                         }
739                 }
740         elsif ($line =~ /^\s*mapping/) {
741                 # skip mapping stanzas
742                 $line = <CFGFILE>;
743                 while(defined($line) && $line !~ /^\s*(iface|mapping|auto)/) {
744                         $line = <CFGFILE>;
745                         next;
746                         }
747                 }
748         elsif (my ($name, $addrfam, $method) = ($line =~ /^\s*iface\s+(\S+)\s+(\S+)\s+(\S+)\s*$/) ) {
749                 # only lines starting with "iface" are expected here
750                 my @iface_options;
751                 # now read everything until the next iface definition
752                 $line = <CFGFILE>;
753                 while (defined $line && ! ($line =~ /^\s*(iface|mapping|auto)/)) {
754                         # skip comments and empty lines
755                         if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
756                                 $line = <CFGFILE>;
757                                 next;
758                                 }
759                         my ($param, $value);
760                         if ( ($param, $value) = ($line =~ /^\s*(\S+)\s+(.*)\s*$/) ) {
761                                 push(@iface_options, [$param, $value]);
762                                 }
763                         elsif ( ($param) = ($line =~ /^\s*(\S+)\s*$/) ) {
764                                 push(@iface_options, [$param, '']);
765                                 }
766                         else {
767                                 error("Error in option line: '$line' invalid");
768                                 }
769                         $line = <CFGFILE>;
770                         }
771                 push(@ret, [$name, $addrfam, $method, \@iface_options]);
772                 }
773         else {
774                 error("Error reading file $pathname: unexpected line '$line'");
775                 }
776         }
777 close(CFGFILE);
778 return @ret;
779 }
780
781 # get_auto_defs()
782 # Returns a list of interfaces in auto lines
783 sub get_auto_defs
784 {
785 local @rv;
786 &open_readfile(CFGFILE, $network_interfaces_config);
787 while(<CFGFILE>) {
788         s/\r|\n//g;
789         s/^\s*#.*$//g;
790         if (/^\s*auto\s*(.*)/) {
791                 push(@rv, split(/\s+/, $1));
792                 }
793         }
794 close(CFGFILE);
795 return @rv;
796 }
797
798 # modify_auto_defs(iface, ...)
799 # Replaces all auto lines with one containing the interfaces given as params
800 sub modify_auto_defs
801 {
802 local $lref = &read_file_lines($network_interfaces_config);
803 local $i;
804 local $found;
805 for($i=0; $i<@$lref; $i++) {
806         local $l = $lref->[$i];
807         $l =~ s/\r|\n//g;
808         $l =~ s/^\s*#.*$//g;
809         if ($l =~ /^\s*auto\s*(.*)/) {
810                 if (!$found++) {
811                         # Replace the auto line
812                         $lref->[$i] = "auto ".join(" ", @_);
813                         }
814                 else {
815                         # Remove another auto line
816                         splice(@$lref, $i--, 1);
817                         }
818                 }
819         }
820 if (!$found) {
821         splice(@$lref, 0, 0, "auto ".join(" ", @_));
822         }
823 &flush_file_lines();
824 }
825
826 # modifies the options of an already stored interface definition
827 # the parameters should be (name, addrfam, method, options, mode)
828 # with options being an array of (param, value) pairs
829 # and mode being 0 for modify and 1 for delete
830 sub modify_interface_def
831 {
832 my ($name, $addrfam, $method, $options, $mode) = @_;
833 # make a backup copy
834 copy("$network_interfaces_config", "$network_interfaces_config~");
835 local *OLDCFGFILE, *NEWCFGFILE;
836 &open_readfile(OLDCFGFILE, "$network_interfaces_config~") ||
837         error("Unable to open $network_interfaces_config");
838 &lock_file($network_interfaces_config);
839 &open_tempfile(NEWCFGFILE, "> $network_interfaces_config", 1) ||
840         error("Unable to open $network_interfaces_config");
841
842 my $inside_modify_region = 0;
843 my $iface_line = 0;
844 my $new_options_wrote;
845 while (defined ($line=<OLDCFGFILE>)) {
846         if ($inside_modify_region == 0 &&
847            $line =~ /^\s*iface\s+$name\s+$addrfam\s+\S+\s*$/) {
848                 # Start of the iface section to modify
849                 $inside_modify_region = 1;
850                 $iface_line = 1;
851                 $new_options_wrote = 0;
852                 }
853         elsif ($inside_modify_region == 1 &&
854                ($line =~ /^\s*iface\s+\S+\s+\S+\s+\S+\s*$/ ||
855                 $line =~ /^\s*mapping/ ||
856                 $line =~ /^\s*auto/)) {
857                 # End of an iface section
858                 $inside_modify_region = 0;
859                 }
860         # preserve comments and blank lnks
861         if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
862                 &print_tempfile(NEWCFGFILE, $line);
863                 }
864         # inside modify region or not ?
865         elsif ($inside_modify_region == 0) {
866                &print_tempfile(NEWCFGFILE, $line);
867                 }
868         else {
869                 # should the iface line be changed or the options ?
870                 if ($iface_line == 1 && $mode == 0) {
871                         &print_tempfile(NEWCFGFILE, "iface $name $addrfam $method\n");
872                         }
873                # only write the new options and skip the old ones or just do
874                # nothing if mode is delete
875                # write only upon first entrance here
876                if ($mode == 0 && $new_options_wrote == 0) {
877                        $new_options_wrote = 1;
878                        foreach $option (@$options) {
879                                my ($param, $value) = @$option;
880                                &print_tempfile(NEWCFGFILE, "\t$param $value\n");
881                                 }
882                         }
883                 }
884         $iface_line = 0;
885         }
886
887 close(OLDCFGFILE);
888 &close_tempfile(NEWCFGFILE);
889 &unlock_file($network_interfaces_config);
890 }
891
892 # creates a new interface definition in the config file
893 # the parameters should be (name, addrfam, method, options)
894 # with options being an array of (param, value) pairs
895 # the selection key is (name, addrfam)
896 sub new_interface_def
897 {
898 # make a backup copy
899 copy("$network_interfaces_config", "$network_interfaces_config~");
900 local *CFGFILE;
901 &open_lock_tempfile(CFGFILE, ">> $network_interfaces_config") ||
902         error("Unable to open $network_interfaces_config");
903 local ($name, $addrfam, $method, $options) = @_;
904 &print_tempfile(CFGFILE, "\niface $name $addrfam $method\n");
905 foreach $option (@$options) {
906         my ($param, $value) = @$option;
907         &print_tempfile(CFGFILE, "\t$param $value\n");
908         }
909 &close_tempfile(CFGFILE);
910 }
911
912 # delete an already defined interface
913 # the parameters should be (name, addrfam)
914 sub delete_interface_def
915 {
916 local ($name, $addrfam, $method) = @_;
917 &modify_interface_def($name, $addrfam, '', [], 1);
918 &modify_module_def($name, 1);
919 }
920
921 sub os_feedback_files
922 {
923 return ( $network_interfaces_config, "/etc/nsswitch.conf", "/etc/resolv.conf",
924          "/etc/HOSTNAME" );
925 }
926
927 # apply_network()
928 # Apply the interface and routing settings
929 sub apply_network
930 {
931 &system_logged("(cd / ; /etc/init.d/networking stop ; /etc/init.d/networking start) >/dev/null 2>&1");
932 }
933
934 # get_default_gateway()
935 # Returns the default gateway IP (if one is set) and device (if set) boot time
936 # settings.
937 sub get_default_gateway
938 {
939 local @ifaces = &get_interface_defs();
940 local ($router, $addr);
941 foreach $iface (grep { $_->[1] eq 'inet' } @ifaces) {
942         foreach $o (@{$iface->[3]}) {
943                 if ($o->[0] eq 'gateway') {
944                         return ( $o->[1], $iface->[0] );
945                         }
946                 }
947         }
948 return ( );
949 }
950
951 # set_default_gateway([gateway, device])
952 # Sets the default gateway to the given IP accessible via the given device,
953 # in the boot time settings.
954 sub set_default_gateway
955 {
956 local @ifaces = &get_interface_defs();
957 foreach my $iface (grep { $_->[1] eq 'inet' } @ifaces) {
958         # Remove the gateway directive
959         $iface->[3] = [ grep { $_->[0] ne 'gateway' } @{$iface->[3]} ];
960
961         # Add if needed
962         if ($iface->[0] eq $_[1]) {
963                 push(@{$iface->[3]}, [ 'gateway', $_[0] ]);
964                 }
965         &modify_interface_def(@$iface);
966         }
967 }
968
969 # get_default_ipv6_gateway()
970 # Returns the default gateway IPv6 address (if one is set) and device (if set)
971 # boot time settings.
972 sub get_default_ipv6_gateway
973 {
974 local @ifaces = &get_interface_defs();
975 local ($router, $addr);
976 foreach $iface (grep { $_->[1] eq 'inet6' } @ifaces) {
977         foreach $o (@{$iface->[3]}) {
978                 if ($o->[0] eq 'gateway') {
979                         return ( $o->[1], $iface->[0] );
980                         }
981                 }
982         }
983 return ( );
984 }
985
986 # set_default_ipv6_gateway([gateway, device])
987 # Sets the default IPv6 gateway to the given IP accessible via the given device,
988 # in the boot time settings.
989 sub set_default_ipv6_gateway
990 {
991 local @ifaces = &get_interface_defs();
992 foreach my $iface (grep { $_->[1] eq 'inet6' } @ifaces) {
993         # Remove the gateway directive
994         $iface->[3] = [ grep { $_->[0] ne 'gateway' } @{$iface->[3]} ];
995
996         # Add if needed
997         if ($iface->[0] eq $_[1] && $_[0]) {
998                 push(@{$iface->[3]}, [ 'gateway', $_[0] ]);
999                 }
1000         &modify_interface_def(@$iface);
1001         }
1002 }
1003
1004 # get_teaming_partner(devicename, line)
1005 # Gets the teamingpartner of a configuration line
1006 # Example configuration line: "/sbin/ifenslave bond0 eth0 eth1"
1007 sub get_teaming_partner
1008 {
1009         my($deviceName, $line) = @_;
1010         @params = split(/ /, $line);
1011         my $return;
1012                 
1013         
1014         for($i = scalar(@params); $i > 0; $i--){
1015                 if($deviceName eq $params[$i]){
1016                         break;
1017                 } else {
1018                         $return = $params[$i] . " " . $return;
1019                 }
1020         }
1021         chop $return;
1022         return $return;
1023 }
1024
1025 sub supports_bonding
1026 {
1027 return $gconfig{'os_type'} eq 'debian-linux' && &has_command("ifenslave");
1028 }
1029
1030 sub supports_vlans
1031 {
1032 return $gconfig{'os_type'} eq 'debian-linux' && &has_command("vconfig");
1033 }
1034
1035 sub boot_iface_hardware
1036 {
1037 return $_[0] =~ /^eth/;
1038 }
1039
1040 # supports_address6([&iface])
1041 # Returns 1 if managing IPv6 interfaces is supported
1042 sub supports_address6
1043 {
1044 local ($iface) = @_;
1045 return !$iface || $iface->{'virtual'} eq '';
1046 }
1047
1048 # Returns 1, as boot-time interfaces on Debian can exist without an IP (such as
1049 # for bridging)
1050 sub supports_no_address
1051 {
1052 return 1;
1053 }
1054
1055 # Bridge interfaces can be created on debian
1056 sub supports_bridges
1057 {
1058 return 1;
1059 }
1060
1061 1;
1062