1# redhat-linux-lib.pl 2# Networking functions for redhat linux 3 4if ($gconfig{'os_type'} eq 'openmamba-linux') { 5 # OpenMamba Linux 6 $net_scripts_dir = "/etc/sysconfig/network-devices"; 7 $devices_dir = "/etc/sysconfig/network-devices"; 8 } 9else { 10 # Redhat, Mandrake, etc.. 11 $net_scripts_dir = "/etc/sysconfig/network-scripts"; 12 $devices_dir = "/etc/sysconfig/networking/devices"; 13 } 14$route_files_dir = -d $devices_dir ? $devices_dir : $net_scripts_dir; 15$network_config = "/etc/sysconfig/network"; 16$static_route_config = "/etc/sysconfig/static-routes"; 17$sysctl_config = "/etc/sysctl.conf"; 18 19# Redhat 7.2+ and Mandrake 9.1+ support separate gateways in each interface file 20$supports_dev_gateway = ($gconfig{'os_type'} eq 'redhat-linux' && 21 $gconfig{'os_version'} >= 7.2) || 22 ($gconfig{'os_type'} eq 'mandrake-linux' && 23 $gconfig{'os_version'} >= 9.1) || 24 ($gconfig{'os_type'} eq 'coherant-linux' && 25 $gconfig{'os_version'} >= 3.0) || 26 ($gconfig{'os_type'} eq 'trustix-linux'); 27 28# Redhat 8.0+ and Mandrake 9.1+ have a separate file for static routes for 29# each interface 30$supports_dev_routes = ($gconfig{'os_type'} eq 'redhat-linux' && 31 $gconfig{'os_version'} >= 8.0) || 32 ($gconfig{'os_type'} eq 'mandrake-linux' && 33 $gconfig{'os_version'} >= 9.1) || 34 ($gconfig{'os_type'} eq 'coherant-linux' && 35 $gconfig{'os_version'} >= 3.0) || 36 ($gconfig{'os_type'} eq 'trustix-linux'); 37 38# Redhat 10 (ES/AS 3) uses route-$dev instead of $dev.route 39$supports_route_dev = ($gconfig{'os_type'} eq 'redhat-linux' && 40 $gconfig{'os_version'} >= 10.0) || 41 ($gconfig{'os_type'} eq 'coherant-linux' && 42 $gconfig{'os_version'} >= 3.0); 43 44# Redhat 9.0+ uses the ONPARENT variable for virtual interfaces 45$uses_on_parent = ($gconfig{'os_type'} eq 'redhat-linux' && 46 $gconfig{'os_version'} >= 9.0) || 47 ($gconfig{'os_type'} eq 'mandrake-linux' && 48 $gconfig{'os_version'} >= 9.1) || 49 ($gconfig{'os_type'} eq 'coherant-linux' && 50 $gconfig{'os_version'} >= 3.0); 51 52# Redhat versions 7.2 and above allow the MTU to be set at boot time 53$supports_mtu = ($gconfig{'os_type'} eq 'redhat-linux' && 54 $gconfig{'os_version'} >= 7.2) || 55 ($gconfig{'os_type'} eq 'coherant-linux' && 56 $gconfig{'os_version'} >= 3.0); 57 58do 'linux-lib.pl'; 59 60# boot_interfaces() 61# Returns a list of interfaces brought up at boot time 62sub boot_interfaces 63{ 64my (@rv, $f); 65my %bridge_map; 66my @active; 67opendir(CONF, &translate_filename($net_scripts_dir)); 68while($f = readdir(CONF)) { 69 my (%conf, $b); 70 if ($f =~ /^ifcfg-([a-z0-9:\.]+)\-range([a-z0-9\.\_]+)$/) { 71 # A range of addresses 72 &read_env_file("$net_scripts_dir/$f", \%conf); 73 $b->{'fullname'} = "$1-range$2"; 74 $b->{'name'} = $1; 75 $b->{'range'} = $2; 76 $b->{'start'} = $conf{'IPADDR_START'}; 77 $b->{'end'} = $conf{'IPADDR_END'}; 78 $b->{'num'} = $conf{'CLONENUM_START'}; 79 $b->{'up'} = 1; 80 $b->{'edit'} = 1; 81 $b->{'desc'} = $conf{'NAME'}; 82 $b->{'index'} = scalar(@rv); 83 $b->{'file'} = "$net_scripts_dir/$f"; 84 push(@rv, $b); 85 } 86 elsif ($f !~ /\.(bak|old)$/i && $f =~ /^ifcfg-([a-zA-Z_0-9:\.]+)$/) { 87 # Normal interface 88 my $fname = $1; 89 &read_env_file("$net_scripts_dir/$f", \%conf); 90 if ($conf{'DEVICE'}) { 91 # Device is set in the file 92 $b->{'fullname'} = $conf{'DEVICE'}; 93 } 94 elsif (&iface_type($fname) ne $text{'ifcs_unknown'}) { 95 # Filename looks like a regular device 96 $b->{'fullname'} = $fname; 97 } 98 elsif ($conf{'HWADDR'}) { 99 # Filename is something odd, like Auto_Ethernet .. so 100 # lookup real device by MAC 101 if (!@active) { 102 @active = &active_interfaces(1); 103 } 104 my ($a) = grep { lc($_->{'ether'}) eq 105 lc($conf{'HWADDR'}) && 106 $_->{'name'} !~ /^br/ } @active; 107 next if (!$a); 108 $b->{'fullname'} = $a->{'fullname'}; 109 # XXX virtuals? 110 } 111 else { 112 # No idea what to do here, probably isn't even an 113 # interface file 114 next; 115 } 116 if ($b->{'fullname'} =~ /(\S+):(\d+)/) { 117 $b->{'name'} = $1; 118 $b->{'virtual'} = $2; 119 } 120 else { 121 $b->{'name'} = $b->{'fullname'}; 122 } 123 if ($b->{'fullname'} =~ /(\S+)\.(\d+)/) { 124 my ($k, $v) = split(/\./, $b->{'fullname'}); 125 $b->{'physical'} = $k; 126 $b->{'vlanid'} = $v; 127 $b->{'vlan'} = 1; 128 } 129 $b->{'up'} = defined($conf{'ONPARENT'}) && 130 $b->{'virtual'} ne '' ? 131 ($conf{'ONPARENT'} eq 'yes') : 132 ($conf{'ONBOOT'} eq 'yes'); 133 $b->{'address'} = $conf{'IPADDR'} || $conf{'IPADDR0'}; 134 $b->{'netmask'} = $conf{'NETMASK'} || $conf{'NETMASK0'}; 135 if (!$conf{'NETMASK'} && $conf{'PREFIX'}) { 136 $b->{'netmask'} = &prefix_to_mask($conf{'PREFIX'}); 137 } 138 elsif (!$conf{'NETMASK'} && $conf{'PREFIX0'}) { 139 $b->{'netmask'} = &prefix_to_mask($conf{'PREFIX0'}); 140 } 141 $b->{'broadcast'} = $conf{'BROADCAST'}; 142 $b->{'gateway'} = $conf{'GATEWAY'}; 143 $b->{'gateway6'} = $conf{'IPV6_DEFAULTGW'}; 144 $b->{'mtu'} = $conf{'MTU'}; 145 if ($b->{'fullname'} =~ /^bond/) { 146 $b->{'partner'} = &get_teaming_partner($conf{'DEVICE'}); 147 } 148 my @values = split(/\s+/, $conf{'BONDING_OPTS'}); 149 foreach my $val (@values) { 150 my ($k, $v) = split(/=/, $val, 2); 151 if ($k eq "mode") { 152 $b->{'mode'} = $v; 153 } 154 elsif ($k eq "miimon") { 155 $b->{'miimon'} = $v; 156 } 157 elsif ($k eq "updelay") { 158 $b->{'updelay'} = $v; 159 } 160 elsif ($k eq "downdelay") { 161 $b->{'downdelay'} = $v; 162 } 163 } 164 $b->{'ether'} = $conf{'MACADDR'}; 165 $b->{'dhcp'} = ($conf{'BOOTPROTO'} eq 'dhcp'); 166 $b->{'bootp'} = ($conf{'BOOTPROTO'} eq 'bootp'); 167 my @ip6s; 168 push(@ip6s, [ split(/\//, $conf{'IPV6ADDR'}) ]) 169 if ($conf{'IPV6ADDR'}); 170 push(@ip6s, map { [ split(/\//, $_) ] } 171 split(/\s+/, $conf{'IPV6ADDR_SECONDARIES'})); 172 if (@ip6s) { 173 # Static IPv6 addresses 174 $b->{'address6'} = [ map { $_->[0] } @ip6s ]; 175 $b->{'netmask6'} = [ map { $_->[1] } @ip6s ]; 176 } 177 elsif (lc($conf{'IPV6INIT'}) eq 'yes') { 178 $b->{'auto6'} = 1; 179 } 180 $b->{'edit'} = ($b->{'name'} !~ /^ppp|irlan/); 181 $b->{'desc'} = $conf{'NAME'}; 182 $b->{'index'} = scalar(@rv); 183 $b->{'file'} = "$net_scripts_dir/$f"; 184 if ($conf{'BRIDGE'}) { 185 $bridge_map{$conf{'BRIDGE'}} = $b->{'fullname'}; 186 } 187 push(@rv, $b); 188 189 # Extra IPADDRn lines in the config are virtual IPs 190 my $i = 0; 191 while(defined($conf{'IPADDR'.($i+1)})) { 192 my $acfg = { 'name' => $b->{'name'}, 193 'virtual' => $i, 194 'fullname' => $b->{'name'}.":".$i, 195 'file' => $b->{'file'}, 196 'parent' => $b->{'fullname'}, 197 'edit' => 1, 198 'up' => 1, }; 199 $acfg->{'address'} = $conf{'IPADDR'.($i+1)}; 200 $acfg->{'netmask'} = $conf{'NETMASK'.($i+1)} || 201 &prefix_to_mask($conf{'PREFIX'.($i+1)}) || 202 $b->{'netmask'}; 203 $acfg->{'index'} = scalar(@rv); 204 push(@rv, $acfg); 205 $i++; 206 } 207 } 208 } 209closedir(CONF); 210foreach my $b (@rv) { 211 if ($b->{'fullname'} =~ /^br\d+$/) { 212 $b->{'bridge'} = 1; 213 $b->{'bridgeto'} = $bridge_map{$b->{'fullname'}}; 214 } 215 } 216return @rv; 217} 218 219# save_bond_interface(device, master) 220# Create or update a boot-time bond slave interface 221sub save_bond_interface 222{ 223local(%conf); 224&lock_file("$net_scripts_dir/ifcfg-$_[0]"); 225$conf{'DEVICE'} = $_[0]; 226$conf{'BOOTPROTO'} = none; 227$conf{'ONBOOT'} = yes; 228$conf{'MASTER'} = $_[1]; 229$conf{'SLAVE'} = "yes"; 230$conf{'USERCTL'} = "no"; 231&write_env_file("$net_scripts_dir/ifcfg-$_[0]", \%conf); 232&unlock_file("$net_scripts_dir/ifcfg-$_[0]"); 233} 234 235# save_interface(&details) 236# Create or update a boot-time interface 237sub save_interface 238{ 239my ($b) = @_; 240my $pmode = $b->{'parent'} && $b->{'virtual'} ne '' || 241 $gconfig{'os_version'} >= 16 && $b->{'virtual'} ne ''; 242my $name = $b->{'range'} ne "" ? $b->{'name'}."-range".$b->{'range'} : 243 $b->{'virtual'} ne "" ? $b->{'name'}.":".$b->{'virtual'} : 244 $b->{'vlanid'} ne "" ? $b->{'physical'}.".".$b->{'vlanid'} 245 : $b->{'name'}; 246if ($pmode) { 247 # Virtual interface being updated in it's parent's config file 248 $name =~ s/:\d+$//; 249 my $file = $b->{'file'} || "$net_scripts_dir/ifcfg-$name"; 250 &lock_file($file); 251 &read_env_file($file, \%conf); 252 my $n = $b->{'virtual'} + 1; 253 $conf{'IPADDR'.$n} = $b->{'address'}; 254 $conf{'PREFIX'.$n} = &mask_to_prefix($b->{'netmask'}); 255 delete($conf{'NETMASK'.$n}); 256 &write_env_file($file, \%conf); 257 &unlock_file($file); 258 $b->{'parent'} ||= $b->{'name'}; 259 } 260else { 261 # Interface has it's own file 262 local(%conf); 263 my $file = $b->{'file'} || "$net_scripts_dir/ifcfg-$name"; 264 &lock_file($file); 265 &read_env_file($file, \%conf); 266 if ($b->{'range'} ne "") { 267 # Special case - saving a range 268 $conf{'IPADDR_START'} = $b->{'start'}; 269 $conf{'IPADDR_END'} = $b->{'end'}; 270 $conf{'CLONENUM_START'} = $b->{'num'}; 271 } 272 else { 273 # Saving a normal interface 274 $conf{'DEVICE'} = $name; 275 my $pfx = $conf{'IPADDR0'} ? '0' : ''; 276 $conf{'IPADDR'.$pfx} = $b->{'address'}; 277 $conf{'NETMASK'.$pfx} = $b->{'netmask'}; 278 delete($conf{'PREFIX'.$pfx}); 279 if ($b->{'address'} && $b->{'netmask'}) { 280 $conf{'NETWORK'.$pfx} = &compute_network($b->{'address'}, 281 $b->{'netmask'}); 282 } 283 else { 284 $conf{'NETWORK'.$pfx} = ''; 285 } 286 $conf{'BROADCAST'.$pfx} = $b->{'broadcast'}; 287 if ($b->{'gateway'}) { 288 $conf{'GATEWAY'} = $b->{'gateway'}; 289 } 290 else { 291 delete($conf{'GATEWAY'}); 292 } 293 if ($b->{'gateway6'}) { 294 $conf{'IPV6_DEFAULTGW'} = $b->{'gateway6'}; 295 } 296 else { 297 delete($conf{'IPV6_DEFAULTGW'}); 298 } 299 $conf{'MTU'} = $b->{'mtu'}; 300 $conf{'MACADDR'} = $b->{'ether'}; 301 $conf{'ONBOOT'} = $b->{'up'} ? "yes" : "no"; 302 $conf{'ONPARENT'} = $b->{'up'} ? "yes" : "no" 303 if ($b->{'virtual'} ne ''); 304 $conf{'BOOTPROTO'} = $b->{'bootp'} ? "bootp" : 305 $b->{'dhcp'} ? "dhcp" : "none"; 306 delete($conf{'IPV6ADDR'}); 307 delete($conf{'IPV6ADDR_SECONDARIES'}); 308 my @ip6s; 309 for(my $i=0; $i<@{$b->{'address6'}}; $i++) { 310 push(@ip6s, $b->{'address6'}->[$i]."/". 311 $b->{'netmask6'}->[$i]); 312 } 313 if ((@ip6s || $b->{'auto6'}) && lc($conf{'IPV6INIT'}) ne 'yes') { 314 $conf{'IPV6INIT'} = 'yes'; 315 } 316 elsif (!@ip6s && !$b->{'auto6'}) { 317 $conf{'IPV6INIT'} = 'no'; 318 } 319 if (@ip6s) { 320 $conf{'IPV6ADDR'} = shift(@ip6s); 321 $conf{'IPV6ADDR_SECONDARIES'} = join(" ", @ip6s); 322 } 323 if ($b->{'fullname'} =~ /^br(\d+)$/) { 324 &has_command("brctl") || 325 &error("Bridges cannot be created unless the brctl ". 326 "command is installed"); 327 $conf{'TYPE'} = 'Bridge'; 328 } 329 if ($b->{'fullname'} =~ /^bond(\d+)$/) { 330 $conf{'BONDING_OPTS'} = "mode=$b->{'mode'}"; 331 if ($b->{'miimon'}) { 332 $conf{'BONDING_OPTS'} .= " miimon=$b->{'miimon'}"; 333 } 334 if ($b->{'updelay'}) { 335 $conf{'BONDING_OPTS'} .= " updelay=$b->{'updelay'}"; 336 } 337 if ($b->{'downdelay'}) { 338 $conf{'BONDING_OPTS'} .= " downdelay=$b->{'downdelay'}"; 339 } 340 341 my @values = split(/\s+/, $b->{'partner'}); 342 foreach my $val (@values) { 343 &save_bond_interface($val, $b->{'fullname'}); 344 } 345 } 346 if ($b->{'vlan'} == 1) { 347 $conf{'VLAN'} = "yes"; 348 } 349 } 350 $conf{'NAME'} = $b->{'desc'}; 351 if (!-r $file) { 352 # New interfaces shouldn't be controller by network manager 353 $conf{'NM_CONTROLLED'} = 'no'; 354 } 355 &write_env_file($file, \%conf); 356 357 # If this is a bridge, set BRIDGE in real interface 358 if ($b->{'bridge'}) { 359 foreach my $efile (glob("$net_scripts_dir/ifcfg-e*")) { 360 my %bconf; 361 &lock_file($efile); 362 &read_env_file($efile, \%bconf); 363 if ($bconf{'DEVICE'} eq $b->{'bridgeto'} && 364 $b->{'bridgeto'}) { 365 # Correct device for bridge 366 $bconf{'BRIDGE'} = $b->{'fullname'}; 367 &write_env_file($efile, \%bconf); 368 } 369 elsif ($bconf{'BRIDGE'} eq $b->{'fullname'} && 370 $bconf{'BRIDGE'}) { 371 # Was using this bridge, shouldn't be 372 delete($bconf{'BRIDGE'}); 373 &write_env_file($efile, \%bconf); 374 } 375 &unlock_file($efile); 376 } 377 } 378 379 # Link to devices directory 380 if (-d &translate_filename($devices_dir)) { 381 &link_file($file, "$devices_dir/ifcfg-$name"); 382 } 383 &unlock_file($file); 384 385 # Make sure IPv6 is enabled globally 386 if (@{$b->{'address6'}}) { 387 my %conf; 388 &lock_file($network_config); 389 &read_env_file($network_config, \%conf); 390 if (lc($conf{'NETWORKING_IPV6'}) ne 'yes') { 391 $conf{'NETWORKING_IPV6'} = 'yes'; 392 &write_env_file($network_config, \%conf); 393 } 394 &unlock_file($network_config); 395 } 396 } 397} 398 399# delete_interface(&details) 400# Delete a boot-time interface 401sub delete_interface 402{ 403my ($b) = @_; 404my $file = $b->{'file'} || "$net_scripts_dir/ifcfg-$name"; 405if ($b->{'virtual'} ne '' && $b->{'parent'}) { 406 # Virtual interface in it's parent's file .. just remove the IP 407 my %conf; 408 my $i = $b->{'virtual'}+1; 409 &lock_file($file); 410 &read_env_file($file, \%conf); 411 for($n=$i; defined($conf{'IPADDR'.$n}); $n++) { 412 $conf{'IPADDR'.$n} = $conf{'IPADDR'.($n+1)}; 413 $conf{'NETMASK'.$n} = $conf{'NETMASK'.($n+1)}; 414 $conf{'PREFIX'.$n} = $conf{'PREFIX'.($n+1)}; 415 delete($conf{'IPADDR'.($n+1)}); 416 delete($conf{'NETMASK'.($n+1)}); 417 delete($conf{'PREFIX'.($n+1)}); 418 } 419 &write_env_file($file, \%conf); 420 &unlock_file($file); 421 } 422else { 423 # Interface has it's own file that should be deleted 424 my $name = $b->{'range'} ne "" ? $b->{'name'}."-range". 425 $b->{'range'} : 426 $b->{'virtual'} ne "" ? $b->{'name'}.":".$b->{'virtual'} 427 : $b->{'name'}; 428 &lock_file($file); 429 &unlink_file("$net_scripts_dir/ifcfg-$name"); 430 if (-d &translate_filename($devices_dir)) { 431 &unlink_file("$devices_dir/ifcfg-$name"); 432 } 433 &unlock_file($file); 434 } 435} 436 437# can_edit(what) 438# Can some boot-time interface parameter be edited? 439sub can_edit 440{ 441if ($supports_mtu) { 442 return 1; 443 } 444else { 445 return $_[0] ne "mtu"; 446 } 447} 448 449sub can_broadcast_def 450{ 451return 1; 452} 453 454# valid_boot_address(address) 455# Is some address valid for a bootup interface 456sub valid_boot_address 457{ 458return &check_ipaddress($_[0]); 459} 460 461# get_hostname() 462sub get_hostname 463{ 464my %conf; 465&read_env_file($network_config, \%conf); 466if ($conf{'HOSTNAME'}) { 467 return $conf{'HOSTNAME'}; 468 } 469return &get_system_hostname(); 470} 471 472# save_hostname(name) 473sub save_hostname 474{ 475my $old = &get_hostname(); 476my %conf; 477&system_logged("hostname ".quotemeta($_[0])." >/dev/null 2>&1"); 478&open_lock_tempfile(HOST, ">/etc/HOSTNAME"); 479&print_tempfile(HOST, $_[0],"\n"); 480&close_tempfile(HOST); 481&lock_file($network_config); 482&read_env_file($network_config, \%conf); 483$conf{'HOSTNAME'} = $_[0]; 484&write_env_file($network_config, \%conf); 485&unlock_file($network_config); 486 487# If any ifcfg-XXX files have the old hostname in DHCP_HOSTNAME, fix it 488foreach my $b (&boot_interfaces()) { 489 my %ifc; 490 &read_env_file($b->{'file'}, \%ifc); 491 if ($ifc{'DHCP_HOSTNAME'} eq $old) { 492 $ifc{'DHCP_HOSTNAME'} = $_[0]; 493 &lock_file($b->{'file'}); 494 &write_env_file($b->{'file'}, \%ifc); 495 &unlock_file($b->{'file'}); 496 } 497 } 498 499# Update /etc/hostname if exists 500if (-r "/etc/hostname") { 501 &open_lock_tempfile(HOST, ">/etc/hostname"); 502 &print_tempfile(HOST, $_[0],"\n"); 503 &close_tempfile(HOST); 504 } 505 506# Use the hostnamectl command as well 507if (&has_command("hostnamectl")) { 508 &system_logged("hostnamectl set-hostname ".quotemeta($_[0]). 509 " >/dev/null 2>&1"); 510 } 511 512undef(@main::get_system_hostname); # clear cache 513} 514 515# get_domainname() 516sub get_domainname 517{ 518my $d; 519&execute_command("domainname", undef, \$d, undef); 520chop($d); 521return $d; 522} 523 524# save_domainname(domain) 525sub save_domainname 526{ 527my %conf; 528&execute_command("domainname ".quotemeta($_[0])); 529&read_env_file($network_config, \%conf); 530if ($_[0]) { 531 $conf{'NISDOMAIN'} = $_[0]; 532 } 533else { 534 delete($conf{'NISDOMAIN'}); 535 } 536&write_env_file($network_config, \%conf); 537} 538 539sub routing_config_files 540{ 541my @rv = ( $network_config, $sysctl_config ); 542if (!$supports_dev_routes) { 543 push(@rv, $static_route_config); 544 } 545else { 546 foreach my $dir ($devices_dir, $net_scripts_dir) { 547 opendir(DIR, &translate_filename($dir)); 548 while(my $f = readdir(DIR)) { 549 if ($f =~ /^([a-z]+\d*(\.\d+)?(:\d+)?)\.route$/ || 550 $f =~ /^route\-([a-z]+\d*(\.\d+)?(:\d+)?)$/) { 551 push(@rv, "$dir/$f"); 552 } 553 } 554 closedir(DIR); 555 } 556 } 557return @rv; 558} 559 560sub network_config_files 561{ 562return ( "/etc/HOSTNAME", $network_config ); 563} 564 565sub routing_input 566{ 567my (%conf, @st, @hr, %sysctl); 568&read_env_file($network_config, \%conf); 569if (!$supports_dev_gateway) { 570 # show default router and device 571 print &ui_table_row($text{'routes_default'}, 572 &ui_opt_textbox("gateway", $conf{'GATEWAY'}, 15, 573 $text{'routes_none'})); 574 575 print &ui_table_row($text{'routes_device2'}, 576 &ui_opt_textbox("gatewaydev", $conf{'GATEWAYDEV'}, 6, 577 $text{'routes_none'})); 578 } 579else { 580 # multiple default routers can exist, one per interface 581 my @table; 582 my $r = 0; 583 if ($conf{'GATEWAY'} || $conf{'IPV6_DEFAULTGW'}) { 584 push(@table, [ 585 &interface_sel("gatewaydev$r", 586 $conf{'GATEWAYDEV'} || 587 $conf{'IPV6_DEFAULTDEV'} || "*"), 588 &ui_textbox("gateway$r", $conf{'GATEWAY'}, 15), 589 &ui_textbox("gateway6$r", $conf{'IPV6_DEFAULTGW'}, 30), 590 ]); 591 $r++; 592 } 593 my @boot = &boot_interfaces(); 594 foreach $b (grep { $_->{'gateway'} && $_->{'virtual'} eq '' } @boot) { 595 push(@table, [ &interface_sel("gatewaydev$r", $b->{'name'}), 596 &ui_textbox("gateway$r", $b->{'gateway'}, 15), 597 &ui_textbox("gateway6$r", $b->{'gateway6'}, 30), 598 ]); 599 $r++; 600 } 601 push(@table, [ &interface_sel("gatewaydev$r"), 602 &ui_textbox("gateway$r", undef, 15), 603 &ui_textbox("gateway6$r", undef, 30), ]); 604 print &ui_table_row($text{'routes_default2'}, 605 &ui_columns_table( 606 [ $text{'routes_ifc'}, $text{'routes_gateway'}, 607 $text{'routes_gateway6'} ], 608 undef, \@table, undef, 1)); 609 } 610 611# show routing 612if ($gconfig{'os_version'} < 7.0) { 613 print &ui_table_row($text{'routes_forward'}, 614 &ui_yesno_radio("forward", 615 $conf{'FORWARD_IPV4'} eq "yes" ? 1 : 0)); 616 } 617else { 618 &read_env_file($sysctl_config, \%sysctl); 619 print &ui_table_row($text{'routes_forward'}, 620 &ui_yesno_radio("forward", 621 $sysctl{'net.ipv4.ip_forward'} ? 1 : 0)); 622 } 623 624if (!$supports_dev_routes) { 625 # get static routes from single file 626 &open_readfile(STATIC, $static_route_config); 627 while(<STATIC>) { 628 if (/(\S+)\s+net\s+(\S+)\s+netmask\s+(\S+)\s+gw\s+(\S+)/) { 629 push(@st, [ $1, $2, $3, $4 ]); 630 } 631 elsif (/(\S+)\s+host\s+(\S+)\s+gw\s+(\S+)/) { 632 push(@st, [ $1, $2, '255.255.255.255', $3 ]); 633 } 634 elsif (/(\S+)\s+net\s+(\S+)\s+netmask\s+(\S+)/) { 635 push(@hr, [ $1, $2, $3 ]); 636 } 637 elsif (/(\S+)\s+host\s+(\S+)/) { 638 push(@hr, [ $1, $2, '255.255.255.255' ]); 639 } 640 } 641 close(STATIC); 642 } 643else { 644 # get static routes from per-interface files 645 my $f; 646 opendir(DIR, &translate_filename($route_files_dir)); 647 while($f = readdir(DIR)) { 648 if ($f =~ /^([a-z]+\d*(\.\d+)?(:\d+)?)\.route$/ || 649 $f =~ /^route\-([a-z]+\d*(\.\d+)?(:\d+)?)$/) { 650 my $dev = $1; 651 my (%rfile, $i); 652 &read_env_file("$route_files_dir/$f", \%rfile); 653 for($i=0; defined($rfile{"ADDRESS$i"}); $i++) { 654 if ($rfile{"GATEWAY$i"}) { 655 push(@st, [ $dev, $rfile{"ADDRESS$i"}, 656 $rfile{"NETMASK$i"}, 657 $rfile{"GATEWAY$i"}]); 658 } 659 else { 660 push(@hr, [ $dev, $rfile{"ADDRESS$i"}, 661 $rfile{"NETMASK$i"} || 662 "255.255.255.255" ]); 663 } 664 } 665 } 666 } 667 closedir(DIR); 668 } 669 670# Show static network routes 671my @table; 672for($i=0; $i<=@st; $i++) { 673 my $st = $st[$i]; 674 push(@table, [ &ui_textbox("dev_$i", $st->[0], 6), 675 &ui_textbox("net_$i", $st->[1], 15), 676 &ui_textbox("netmask_$i", $st->[2], 15), 677 &ui_textbox("gw_$i", $st->[3], 15) ]); 678 } 679print &ui_table_row($text{'routes_static'}, 680 &ui_columns_table([ $text{'routes_ifc'}, $text{'routes_net'}, 681 $text{'routes_mask'}, $text{'routes_gateway'} ], 682 undef, \@table, undef, 1)); 683 684# Show static host routes 685my @table; 686for($i=0; $i<=@hr; $i++) { 687 my $st = $hr[$i]; 688 push(@table, [ &ui_textbox("ldev_$i", $st->[0], 6), 689 &ui_textbox("lnet_$i", $st->[1], 15), 690 &ui_textbox("lnetmask_$i", $st->[2], 15) ]); 691 } 692print &ui_table_row($text{'routes_local'}, 693 &ui_columns_table([ $text{'routes_ifc'}, $text{'routes_net'}, 694 $text{'routes_mask'} ], 695 undef, \@table, undef, 1)); 696} 697 698sub parse_routing 699{ 700my (%conf, @st, %sysctl, %st, @boot); 701&lock_file($network_config); 702&read_env_file($network_config, \%conf); 703if (!$supports_dev_gateway) { 704 # Just update a single file 705 if ($in{'gateway_def'}) { delete($conf{'GATEWAY'}); } 706 elsif (!&to_ipaddress($in{'gateway'})) { 707 &error(&text('routes_edefault', &html_escape($in{'gateway'}))); 708 } 709 else { $conf{'GATEWAY'} = $in{'gateway'}; } 710 711 if ($in{'gatewaydev_def'}) { delete($conf{'GATEWAYDEV'}); } 712 elsif ($in{'gatewaydev'} !~ /^\S+$/) { 713 &error(&text('routes_edevice', &html_escape($in{'gatewaydev'}))); 714 } 715 else { $conf{'GATEWAYDEV'} = $in{'gatewaydev'}; } 716 } 717else { 718 # Multiple defaults can be specified! 719 my ($r, $b); 720 @boot = grep { $->{'virtual'} eq '' } &boot_interfaces(); 721 foreach $b (@boot) { 722 delete($b->{'gateway'}); 723 } 724 delete($conf{'GATEWAY'}); 725 delete($conf{'GATEWAYDEV'}); 726 delete($conf{'IPV6_DEFAULTDEV'}); 727 delete($conf{'IPV6_DEFAULTGW'}); 728 729 for($r=0; defined($in{"gatewaydev$r"}); $r++) { 730 next if (!$in{"gatewaydev$r"}); 731 &check_ipaddress($in{"gateway$r"}) || 732 &error(&text('routes_edefault2', $r+1)); 733 if ($in{"gatewaydev$r"} eq "*") { 734 # For any interface 735 $conf{'GATEWAY'} && &error(&text('routes_eclash')); 736 $conf{'GATEWAY'} = $in{"gateway$r"}; 737 $conf{'IPV6_DEFAULTGW'} && 738 &error(&text('routes_eclash6')); 739 $conf{'IPV6_DEFAULTGW'} = $in{"gateway6$r"}; 740 } 741 else { 742 # For a specific interface 743 my ($b) = grep { $_->{'fullname'} eq 744 $in{"gatewaydev$r"} } @boot; 745 $b->{'gateway'} && &error(&text('routes_eclash2', 746 &html_escape($in{"gatewaydev$r"}))); 747 $b->{'gateway'} = $in{"gateway$r"}; 748 $b->{'gateway6'} = $in{"gateway6$r"}; 749 } 750 } 751 } 752 753if ($gconfig{'os_version'} < 7.0) { 754 if ($in{'forward'}) { $conf{'FORWARD_IPV4'} = 'yes'; } 755 else { $conf{'FORWARD_IPV4'} = 'no'; } 756 } 757else { 758 &lock_file($sysctl_config); 759 &read_env_file($sysctl_config, \%sysctl); 760 $sysctl{'net.ipv4.ip_forward'} = $in{'forward'}; 761 } 762 763# Parse static and my routes 764for($i=0; defined($dev = $in{"dev_$i"}); $i++) { 765 next if (!$dev); 766 $net = $in{"net_$i"}; $netmask = $in{"netmask_$i"}; $gw = $in{"gw_$i"}; 767 $dev =~ /^\S+$/ || &error(&text('routes_edevice', &html_escape($dev))); 768 &to_ipaddress($net) || &error(&text('routes_enet', &html_escape($net))); 769 &check_ipaddress($netmask) || &error(&text('routes_emask', &html_escape($netmask))); 770 &to_ipaddress($gw) || &error(&text('routes_egateway', &html_escape($gw))); 771 if ($netmask eq "255.255.255.255") { 772 push(@st, "$dev host $net gw $gw\n"); 773 } 774 else { 775 push(@st, "$dev net $net netmask $netmask gw $gw\n"); 776 } 777 push(@{$st{$dev}}, [ $net, $netmask, $gw ]); 778 } 779for($i=0; defined($dev = $in{"ldev_$i"}); $i++) { 780 $net = $in{"lnet_$i"}; $netmask = $in{"lnetmask_$i"}; 781 next if (!$dev && !$net); 782 $dev =~ /^\S+$/ || &error(&text('routes_edevice', &html_escape($dev))); 783 &to_ipaddress($net) || 784 $net =~ /^(\S+)\/(\d+)$/ && &to_ipaddress("$1") || 785 &error(&text('routes_enet', &html_escape($net))); 786 &check_ipaddress($netmask) || &error(&text('routes_emask', &html_escape($netmask))); 787 if ($netmask eq "255.255.255.255") { 788 push(@st, "$dev host $net\n"); 789 } 790 else { 791 push(@st, "$dev net $net netmask $netmask\n"); 792 } 793 push(@{$st{$dev}}, [ $net, $netmask ]); 794 } 795if (!$supports_dev_routes) { 796 # Write to a single file 797 &open_lock_tempfile(STATIC, ">$static_route_config"); 798 &print_tempfile(STATIC, @st); 799 &close_tempfile(STATIC); 800 } 801else { 802 # Write to one file per interface (delete old, then save new/updated) 803 my $f; 804 opendir(DIR, &translate_filename($route_files_dir)); 805 while($f = readdir(DIR)) { 806 if (($f =~ /^([a-z]+\d*(\.\d+)?(:\d+)?)\.route$/ || 807 $f =~ /^route\-([a-z]+\d*(\.\d+)?(:\d+)?)$/) && !$st{$1}) { 808 &unlink_logged("$devices_dir/$f"); 809 &unlink_logged("$net_scripts_dir/$f"); 810 } 811 } 812 closedir(DIR); 813 foreach $dev (keys %st) { 814 $f = $supports_route_dev ? "route-$dev" : "$dev.route"; 815 my (%rfile, $i); 816 for($i=0; $i<@{$st{$dev}}; $i++) { 817 $rfile{"ADDRESS$i"} = $st{$dev}->[$i]->[0]; 818 $rfile{"NETMASK$i"} = $st{$dev}->[$i]->[1]; 819 $rfile{"GATEWAY$i"} = $st{$dev}->[$i]->[2]; 820 } 821 &lock_file("$route_files_dir/$f"); 822 &write_env_file("$route_files_dir/$f", \%rfile); 823 &unlock_file("$route_files_dir/$f"); 824 if ($route_files_dir ne $net_scripts_dir) { 825 &lock_file("$net_scripts_dir/$f"); 826 &link_file("$route_files_dir/$f", 827 "$net_scripts_dir/$f"); 828 &unlock_file("$net_scripts_dir/$f"); 829 } 830 } 831 } 832&write_env_file($network_config, \%conf); 833&unlock_file($network_config); 834if (%sysctl) { 835 &write_env_file($sysctl_config, \%sysctl); 836 &unlock_file($sysctl_config); 837 } 838if (@boot) { 839 my $b; 840 foreach $b (@boot) { 841 &save_interface($b); 842 } 843 } 844} 845 846# interface_sel(name, value) 847# Returns a menu for all boot-time interfaces 848sub interface_sel 849{ 850my ($name, $value) = @_; 851my @opts = ( [ "", " " ], 852 [ "*", $text{'routes_any'} ] ); 853@boot_interfaces_cache = sort { $a->{'fullname'} cmp $b->{'fullname'} } 854 &boot_interfaces() if (!@boot_interfaces_cache); 855foreach $b (@boot_interfaces_cache) { 856 push(@opts, [ $b->{'fullname'}, $b->{'fullname'} ]); 857 } 858return &ui_select($name, $value, \@opts); 859} 860 861# apply_network() 862# Apply the interface and routing settings 863sub apply_network 864{ 865&foreign_require("init"); 866if (&init::action_status("network")) { 867 # Older system that has an init.d/network script 868 my $err = &init::restart_action("network"); 869 &error($err) if ($err); 870 } 871else { 872 # Probably CentOS 8 that has no apply command 873 foreach my $b (&boot_interfaces()) { 874 &apply_interface($b) if ($b->{'virtual'} eq ''); 875 } 876 } 877} 878 879# apply_interface(&iface) 880# Calls an OS-specific function to make a boot-time interface active 881sub apply_interface 882{ 883my ($i) = @_; 884my $out; 885if ($i->{'parent'} && $i->{'virtual'} ne '') { 886 $out = &backquote_logged( 887 "cd / ; ". 888 "ifup ".quotemeta($i->{'name'})." 2>&1 </dev/null"); 889 } 890else { 891 $out = &backquote_logged( 892 "cd / ; ". 893 "ifdown ".quotemeta($i->{'fullname'}). 894 " >/dev/null 2>&1 </dev/null ; ". 895 "ifup ".quotemeta($i->{'fullname'}). 896 " 2>&1 </dev/null"); 897 } 898return $? || $out =~ /error/i ? $out : undef; 899} 900 901# unapply_interface(&iface) 902# Calls an OS-specific function to make a boot-time interface inactive 903#sub unapply_interface 904#{ 905#my ($i) = @_; 906#my $out = &backquote_logged( 907# "cd / ; ifdown ".quotemeta($i->{'fullname'})." 2>&1 </dev/null"); 908#return $? ? $out : undef; 909#} 910 911# get_default_gateway() 912# Returns the default gateway IP (if one is set) and device (if set) boot time 913# settings. 914sub get_default_gateway 915{ 916my %conf; 917&read_env_file($network_config, \%conf); 918my @boot = &boot_interfaces(); 919my ($gifc) = grep { $_->{'gateway'} && $_->{'virtual'} eq '' } @boot; 920return ( $gifc->{'gateway'}, $gifc->{'fullname'} ) if ($gifc); 921return $conf{'GATEWAY'} ? ( $conf{'GATEWAY'}, $conf{'GATEWAYDEV'} ) : ( ); 922} 923 924# set_default_gateway(gateway, device) 925# Sets the default gateway to the given IP accessible via the given device, 926# in the boot time settings. 927sub set_default_gateway 928{ 929my ($gw, $gwdev) = @_; 930my %conf; 931&lock_file($network_config); 932&read_env_file($network_config, \%conf); 933if (!$supports_dev_gateway) { 934 # Just update the network config file 935 if ($gw) { 936 $conf{'GATEWAY'} = $gw; 937 $conf{'GATEWAYDEV'} = $gwdev; 938 } 939 else { 940 delete($conf{'GATEWAY'}); 941 delete($conf{'GATEWAYDEV'}); 942 } 943 } 944else { 945 # Set the gateway in the specified interface file, and clear the rest 946 my @boot = grep { $->{'virtual'} eq '' } &boot_interfaces(); 947 foreach $b (@boot) { 948 delete($b->{'gateway'}); 949 if ($gw && $b->{'fullname'} eq $gwdev) { 950 $b->{'gateway'} = $gw; 951 &save_interface($b); 952 } 953 } 954 delete($conf{'GATEWAY'}); 955 delete($conf{'GATEWAYDEV'}); 956 } 957&write_env_file($network_config, \%conf); 958&unlock_file($network_config); 959} 960 961# get_default_ipv6_gateway() 962# Returns the default gateway IPv6 address (if one is set) and device (if set) 963# boot time settings. 964sub get_default_ipv6_gateway 965{ 966my %conf; 967&read_env_file($network_config, \%conf); 968my @boot = &boot_interfaces(); 969my ($gifc) = grep { $_->{'gateway6'} && $_->{'virtual'} eq '' } @boot; 970return ( $gifc->{'gateway6'}, $gifc->{'fullname'} ) if ($gifc); 971return $conf{'IPV6_DEFAULTGW'} ? ( $conf{'IPV6_DEFAULTGW'}, 972 $conf{'IPV6_DEFAULTDEV'} ) : ( ); 973} 974 975# set_default_ipv6_gateway(gateway, device) 976# Sets the default gateway to the given IPv6 address accessible via the given 977# device, in the boot time settings. 978sub set_default_ipv6_gateway 979{ 980&lock_file($network_config); 981&read_env_file($network_config, \%conf); 982my @boot = grep { $->{'virtual'} eq '' } &boot_interfaces(); 983foreach $b (@boot) { 984 delete($b->{'gateway6'}); 985 if ($_[0] && $b->{'fullname'} eq $_[1]) { 986 $b->{'gateway6'} = $_[0]; 987 &save_interface($b); 988 } 989 } 990delete($conf{'IPV6_DEFAULTGW'}); 991delete($conf{'IPV6_DEFAULTDEV'}); 992&write_env_file($network_config, \%conf); 993&unlock_file($network_config); 994} 995 996# supports_ranges() 997# Returns 1 for newer redhat versions 998sub supports_ranges 999{ 1000return ($gconfig{'os_type'} eq 'redhat-linux' && 1001 $gconfig{'os_version'} >= 7.3) || 1002 ($gconfig{'os_type'} eq 'mandrake-linux' && 1003 $gconfig{'os_version'} >= 8.0) || 1004 ($gconfig{'os_type'} eq 'coherant-linux' && 1005 $gconfig{'os_version'} >= 3.0); 1006} 1007 1008sub supports_bonding 1009{ 1010return $gconfig{'os_type'} eq 'redhat-linux' && 1011 $gconfig{'os_version'} >= 13.0 && 1012 &has_command("ifenslave"); 1013} 1014 1015sub supports_vlans 1016{ 1017return $gconfig{'os_type'} eq 'redhat-linux' && 1018 $gconfig{'os_version'} >= 13.0 && 1019 &has_command("vconfig"); 1020} 1021 1022 1023# range_input([&interface]) 1024# Print HTML for a IP range interface 1025sub range_input 1026{ 1027my $new = !$_[0]; 1028 1029# Range description 1030print &ui_table_row($text{'ifcs_desc'}, 1031 &ui_textbox("desc", $_[0] ? $_[0]->{'desc'} : undef, 60)); 1032 1033# Base interface 1034my $ifaceinput; 1035if ($new) { 1036 $ifaceinput = &ui_select("iface", $_[0]->{'name'}, 1037 [ map { $_->{'fullname'} } grep { $b->{'virtual'} eq '' } 1038 &boot_interfaces() ]); 1039 } 1040else { 1041 $ifaceinput = "<tt>$_[0]->{'name'}</tt>"; 1042 } 1043print &ui_table_row($text{'range_iface'}, $ifaceinput); 1044 1045# Name for this range 1046print &ui_table_row($text{'range_name'}, 1047 $new ? &ui_textbox("range", undef, 10) 1048 : "<tt>$_[0]->{'range'}</tt>"); 1049 1050# Start 1051print &ui_table_row($text{'range_start'}, 1052 &ui_textbox("start", $_[0]->{'start'}, 15)); 1053 1054# Stop 1055print &ui_table_row($text{'range_end'}, 1056 &ui_textbox("end", $_[0]->{'end'}, 15)); 1057 1058# Base number 1059print &ui_table_row($text{'range_num'}, 1060 &ui_textbox("num", $_[0]->{'num'}, 5)); 1061} 1062 1063# parse_range(&range, &in) 1064sub parse_range 1065{ 1066my %in = %{$_[1]}; 1067if ($in{'new'}) { 1068 $_[0]->{'name'} = $in{'iface'}; 1069 $in{'range'} =~ /^[a-z0-9\.\_]+$/ || &error($text{'range_ename'}); 1070 $_[0]->{'range'} = $in{'range'}; 1071 $_[0]->{'fullname'} = $in{'iface'}."-range".$in{'range'}; 1072 } 1073$_[0]->{'desc'} = $in{'desc'}; 1074 1075&check_ipaddress($in{'start'}) || &error($text{'range_estart'}); 1076$_[0]->{'start'} = $in{'start'}; 1077 1078&check_ipaddress($in{'end'}) || &error($text{'range_eend'}); 1079$_[0]->{'end'} = $in{'end'}; 1080 1081my @sip = split(/\./, $in{'start'}); 1082my @eip = split(/\./, $in{'end'}); 1083$sip[0] == $eip[0] && $sip[1] == $eip[1] && $sip[2] == $eip[2] || 1084 &error($text{'range_eclass'}); 1085$sip[3] <= $eip[3] || &error($text{'range_ebefore'}); 1086 1087$in{'num'} =~ /^\d+$/ || &error($text{'range_enum'}); 1088$_[0]->{'num'} = $in{'num'}; 1089} 1090 1091# get_dhcp_hostname() 1092# Returns 0 if the hostname is not set by DHCP, 1 if it is, or -1 if this 1093# feature is not supported on this OS. 1094sub get_dhcp_hostname 1095{ 1096return -1 if ($gconfig{'os_type'} ne 'redhat-linux' || 1097 $gconfig{'os_version'} < 11); 1098my @boot = &boot_interfaces(); 1099my ($eth) = grep { $_->{'fullname'} =~ /^(eth|em)\d+$/ } @boot; 1100return -1 if (!$eth); 1101my %eth; 1102&read_env_file($eth->{'file'}, \%eth); 1103return $eth{'DHCP_HOSTNAME'} ne &get_system_hostname(); 1104} 1105 1106# save_dhcp_hostname(set) 1107# If called with a parameter of 0, the hostname is fixed and not set by 1108# DHCP. If called with 1, the hostname is chosen by DHCP. 1109sub save_dhcp_hostname 1110{ 1111} 1112 1113# get_teaming_partner(devicename) 1114# Gets the teamingpartners of a configured bond interface 1115sub get_teaming_partner 1116{ 1117my ($g, $return); 1118opendir(CONF2, &translate_filename($net_scripts_dir)); 1119while($g = readdir(CONF2)) { 1120 my %conf2; 1121 if ($g !~ /\.(bak|old)$/i && $g =~ /^ifcfg-([a-z0-9:\.]+)$/) { 1122 &read_env_file("$net_scripts_dir/$g", \%conf2); 1123 if ($conf2{'MASTER'} eq "$_[0]") { 1124 $return .= $conf2{'DEVICE'}." "; 1125 } 1126 } 1127 } 1128return $return; 1129} 1130 1131sub boot_iface_hardware 1132{ 1133return $_[0] =~ /^(eth|em)/; 1134} 1135 1136# supports_address6([&iface]) 1137# Returns 1 if managing IPv6 interfaces is supported 1138sub supports_address6 1139{ 1140my ($iface) = @_; 1141return !$iface || $iface->{'virtual'} eq ''; 1142} 1143 1144# Returns 1, as boot-time interfaces on Redhat can exist without an IP (such as 1145# for bridging) 1146sub supports_no_address 1147{ 1148return 1; 1149} 1150 1151# Bridge interfaces can be created on redhat 1152sub supports_bridges 1153{ 1154return 1; 1155} 1156 1157# os_save_dns_config(&config) 1158# Updates DNSx lines in all network-scripts files that have them 1159sub os_save_dns_config 1160{ 1161my ($conf) = @_; 1162foreach my $b (&boot_interfaces()) { 1163 my %ifc; 1164 &read_env_file($b->{'file'}, \%ifc); 1165 next if (!defined($ifc{'DNS1'})); 1166 &lock_file($b->{'file'}); 1167 foreach my $k (keys %ifc) { 1168 delete($ifc{$k}) if ($k =~ /^DNS\d+$/); 1169 } 1170 &write_env_file($b->{'file'}, \%ifc); 1171 my $i = 1; 1172 foreach my $ns (@{$conf->{'nameserver'}}) { 1173 $ifc{'DNS'.$i} = $ns; 1174 $i++; 1175 } 1176 if (!@{$conf->{'nameserver'}}) { 1177 # Add an empty DNS1 line so that we know to update this file 1178 # later if DNS resolves come back 1179 $ifc{'DNS1'} = '' 1180 } 1181 my @d = @{$conf->{'domain'}}; 1182 if (@d) { 1183 $ifc{'DOMAIN'} = $d[0]; 1184 } 1185 else { 1186 delete($ifc{'DOMAIN'}); 1187 } 1188 &write_env_file($b->{'file'}, \%ifc); 1189 &unlock_file($b->{'file'}); 1190 } 1191return (0, 0); 1192} 1193 11941; 1195 1196