1# solaris-lib.pl 2# Networking functions for solaris 3 4$min_virtual_number = 1; 5 6# active_interfaces() 7# Returns a list of currently ifconfig'd interfaces 8sub active_interfaces 9{ 10local(@rv, @lines, $l); 11&open_execute_command(IFC, "ifconfig -a", 1, 1); 12while(<IFC>) { 13 s/\r|\n//g; 14 if (/^\S+:/) { push(@lines, $_); } 15 else { $lines[$#lines] .= $_; } 16 } 17close(IFC); 18foreach $l (@lines) { 19 local %ifc; 20 $l =~ /^([^:\s]+):/; $ifc{'name'} = $1; 21 $l =~ /^(\S+):/; $ifc{'fullname'} = $1; 22 if ($l =~ /inet6\s+(\S+)\/(\d+)/) { 23 # Found an IPv6 interface, which might be real or virtual. Look 24 # for the previous interface with the same name, and add to it. 25 my ($address6, $netmask6) = ($1, $2); 26 my ($ifc4) = grep { $_->{'fullname'} eq $ifc{'name'} } @rv; 27 if ($ifc4) { 28 push(@{$ifc4->{'address6'}}, $address6); 29 push(@{$ifc4->{'netmask6'}}, $netmask6); 30 } 31 next; 32 } 33 if ($l =~ /^(\S+):(\d+):\s/) { $ifc{'virtual'} = $2; } 34 if ($l =~ /inet\s+(\S+)/) { $ifc{'address'} = $1; } 35 if ($l =~ /netmask\s+(\S+)/) { $ifc{'netmask'} = &parse_hex($1); } 36 if ($l =~ /broadcast\s+(\S+)/) { $ifc{'broadcast'} = $1; } 37 if ($l =~ /ether\s+(\S+)/) { $ifc{'ether'} = $1; } 38 if ($l =~ /mtu\s+(\S+)/) { $ifc{'mtu'} = $1; } 39 if ($l =~ /zone\s+(\S+)/) { $ifc{'zone'} = $1; } 40 $ifc{'up'}++ if ($l =~ /\<UP/); 41 $ifc{'edit'} = ($ifc{'name'} !~ /ipdptp|ppp/ && !$ifc{'zone'}); 42 $ifc{'index'} = scalar(@rv); 43 if ($ifc{'ether'}) { 44 $ifc{'ether'} = join(":", map { sprintf "%2.2X", hex($_) } 45 split(/:/, $ifc{'ether'})); 46 } 47 push(@rv, \%ifc); 48 } 49return @rv; 50} 51 52# activate_interface(&details) 53# Create or modify an interface 54sub activate_interface 55{ 56local $a = $_[0]; 57 58# Check if already up 59local @active = &active_interfaces(); 60local ($already) = grep { $_->{'fullname'} eq $_[0]->{'fullname'} } @active; 61if (!$already) { 62 # Bring up for the first time 63 if ($a->{'virtual'} eq "") { 64 local $out = &backquote_logged( 65 "ifconfig $a->{'name'} plumb 2>&1"); 66 if ($out) { &error(&text('aifc_eexist', &html_escape($a->{'name'}))); } 67 } 68 elsif ($gconfig{'os_version'} >= 8) { 69 &system_logged( 70 "ifconfig $a->{'name'}:$a->{'virtual'} plumb >/dev/null 2>&1"); 71 } 72 } 73 74# Set IP address and netmask 75local $cmd = "ifconfig $a->{'name'}"; 76if ($a->{'virtual'} ne "") { $cmd .= ":$a->{'virtual'}"; } 77$cmd .= " $a->{'address'}"; 78if ($a->{'netmask'}) { $cmd .= " netmask $a->{'netmask'}"; } 79else { $cmd .= " netmask +"; } 80if ($a->{'broadcast'}) { $cmd .= " broadcast $a->{'broadcast'}"; } 81else { $cmd .= " broadcast +"; } 82if ($a->{'mtu'}) { $cmd .= " mtu $a->{'mtu'}"; } 83if ($a->{'zone'}) { $cmd .= " zone $a->{'zone'}"; } 84if ($a->{'up'}) { $cmd .= " up"; } 85else { $cmd .= " down"; } 86local $out = &backquote_logged("$cmd 2>&1"); 87if ($?) { &error("$cmd : $out"); } 88 89if ($_[0]->{'virtual'} eq '') { 90 # Remove existing IPv6 addresses, except for ones we want to keep 91 my %need6 = map { $_, 1 } @{$_[0]->{'address6'}}; 92 if ($already) { 93 if (@{$already->{'address6'}}) { 94 # Never remove first IPv6 address, which is dynamic 95 $need6{$already->{'address6'}->[0]} = 1; 96 } 97 foreach my $a (@{$already->{'address6'}}) { 98 if (!$need6{$a}) { 99 # Not needed, can remove 100 local $cmd = "ifconfig $_[0]->{'name'} inet6 ". 101 "removeif ".$a; 102 local $out = &backquote_logged("$cmd 2>&1"); 103 if ($?) { &error("$cmd : $out"); } 104 } 105 else { 106 # Don't need to add this one later 107 $need6{$a} = 0; 108 } 109 } 110 } 111 112 # Add all new addresses 113 for(my $i=0; $i<@{$_[0]->{'address6'}}; $i++) { 114 if ($need6{$_[0]->{'address6'}->[$i]}) { 115 local $cmd = "ifconfig $_[0]->{'name'} inet6 addif ". 116 $_[0]->{'address6'}->[$i]."/". 117 $_[0]->{'netmask6'}->[$i]." up"; 118 local $out = &backquote_logged("$cmd 2>&1"); 119 if ($?) { &error("$cmd : $out"); } 120 } 121 } 122 # XXX routes too 123 } 124 125# Set MAC address 126if ($a->{'ether'}) { 127 $out = &backquote_logged( 128 "ifconfig $a->{'name'} ether $a->{'ether'} 2>&1"); 129 if ($? && $out !~ /Device busy/) { &error($out); } 130 } 131} 132 133# deactivate_interface(&details) 134# Deactive an interface 135sub deactivate_interface 136{ 137local $a = $_[0]; 138local $cmd; 139if ($a->{'virtual'} eq "") { 140 $cmd = "ifconfig $a->{'name'} unplumb"; 141 } 142elsif ($gconfig{'os_version'} >= 8) { 143 $cmd = "ifconfig $a->{'name'}:$a->{'virtual'} unplumb"; 144 } 145else { 146 $cmd = "ifconfig $a->{'name'}:$a->{'virtual'} 0.0.0.0 down"; 147 } 148local $out = &backquote_logged("$cmd 2>&1"); 149if ($?) { &error($out); } 150} 151 152# boot_interfaces() 153# Returns a list of interfaces brought up at boot time 154sub boot_interfaces 155{ 156local (@rv, $f, %mask); 157push(@rv, { 'name' => 'lo0', 158 'fullname' => 'lo0', 159 'address' => '127.0.0.1', 160 'netmask' => '255.0.0.0', 161 'up' => 1, 162 'edit' => 0 }); 163 164# Search for IPv4 interface files 165local $etc = &translate_filename("/etc"); 166opendir(ETC, $etc); 167while($f = readdir(ETC)) { 168 if ($f =~ /^hostname\.(\S+):(\d+)$/ || $f =~ /^hostname\.(\S+)/) { 169 local %ifc; 170 $ifc{'fullname'} = $ifc{'name'} = $1; 171 $ifc{'virtual'} = $2 if (defined($2)); 172 $ifc{'fullname'} .= ":$2" if (defined($2)); 173 $ifc{'index'} = scalar(@rv); 174 $ifc{'edit'}++; 175 $ifc{'file'} = "$etc/$f"; 176 open(FILE, "<$etc/$f"); 177 chop($ifc{'address'} = <FILE>); 178 close(FILE); 179 if ($ifc{'address'}) { 180 $ifc{'netmask'} = &automatic_netmask($ifc{'address'}); 181 $ifc{'broadcast'} = &compute_broadcast($ifc{'address'}, 182 $ifc{'netmask'}); 183 } 184 else { 185 $ifc{'dhcp'}++; 186 } 187 $ifc{'up'}++; 188 push(@rv, \%ifc); 189 } 190 } 191closedir(ETC); 192 193# Re-scan for /etc/hostname6 files, for IPv6 addresses 194opendir(ETC, $etc); 195while($f = readdir(ETC)) { 196 if ($f =~ /^hostname6\.(\S+):(\d+)$/ || $f =~ /^hostname6\.(\S+)/) { 197 local ($name, $virtual) = ($1, $2); 198 local ($ifc) = grep { $_->{'fullname'} eq $name } @rv; 199 next if (!$ifc); 200 local $address6 = &read_file_contents("/etc/$f"); 201 chop($address6); 202 if ($address6) { 203 # Has a static IPv6 address 204 local $netmask6; 205 ($address6, $netmask6) = split(/\//, $address6); 206 $netmask6 ||= 64; 207 push(@{$ifc->{'address6'}}, $address6); 208 push(@{$ifc->{'netmask6'}}, $netmask6); 209 $ifc->{'auto6'} = 0; 210 } 211 elsif (!$address6 && $virtual eq '' && 212 !@{$ifc->{'address6'}}) { 213 # Empty hostname6.xxx file, indicating dynamic address 214 $ifc->{'auto6'} = 1; 215 } 216 } 217 } 218closedir(ETC); 219 220return @rv; 221} 222 223# save_interface(&details) 224# Create or update a boot-time interface 225sub save_interface 226{ 227# Update IPv4 config file 228local $name = $_[0]->{'virtual'} ne "" ? $_[0]->{'name'}.":".$_[0]->{'virtual'} 229 : $_[0]->{'name'}; 230&open_lock_tempfile(IFACE, ">/etc/hostname.$name"); 231if (!$_[0]->{'dhcp'}) { 232 &print_tempfile(IFACE, $_[0]->{'address'},"\n"); 233 } 234&close_tempfile(IFACE); 235 236if ($_[0]->{'virtual'} eq '') { 237 # Create IPv6 config files 238 if ($_[0]->{'auto6'} || @{$_[0]->{'address6'}}) { 239 # Create empty file for main interface 240 &open_lock_tempfile(IFACE, ">/etc/hostname6.$name"); 241 &close_tempfile(IFACE); 242 243 # Create a file for each virtual interface 244 my %created; 245 for(my $i=0; $i<@{$_[0]->{'address6'}}; $i++) { 246 my $n = $i + 1; 247 my $f = "/etc/hostname6.${name}:${n}"; 248 &open_lock_tempfile(IFACE, ">$f"); 249 &print_tempfile(IFACE, $_[0]->{'address6'}->[$i]."/". 250 $_[0]->{'netmask6'}->[$i]."\n"); 251 &close_tempfile(IFACE); 252 $created{$f} = 1; 253 } 254 255 # Delete other IPv6 alias files 256 foreach my $f (glob("/etc/hostname6.".$name.":*")) { 257 if (!$created{$f}) { 258 &unlink_logged($f); 259 } 260 } 261 } 262 else { 263 # Delete all IPv6 files 264 &unlink_logged("/etc/hostname6.$name"); 265 foreach my $f (glob("/etc/hostname6.".$name.":*")) { 266 &unlink_logged($f); 267 } 268 } 269 } 270} 271 272# delete_interface(&details) 273# Delete a boot-time interface 274sub delete_interface 275{ 276local $name = $_[0]->{'virtual'} ne "" ? $_[0]->{'name'}.":".$_[0]->{'virtual'} 277 : $_[0]->{'name'}; 278&unlink_logged("/etc/hostname.$name"); 279&unlink_logged("/etc/hostname6.$name"); 280foreach my $f (glob("/etc/hostname6.".$name.":*")) { 281 &unlink_logged($f); 282 } 283} 284 285# iface_type(name) 286# Returns a human-readable interface type name 287sub iface_type 288{ 289return "Fast Ethernet" if ($_[0] =~ /^hme/); 290return "Loopback" if ($_[0] =~ /^lo/); 291return "Token Ring" if ($_[0] =~ /^tr/); 292return "PPP" if ($_[0] =~ /^ipdptp/ || $_[0] =~ /^ppp/); 293return "Ethernet"; 294} 295 296# iface_hardware(name) 297# Does some interface have an editable hardware address 298sub iface_hardware 299{ 300return $_[0] !~ /^(lo|ipdptp|ppp)/; 301} 302 303# can_edit(what) 304# Can some boot-time interface parameter be edited? 305sub can_edit 306{ 307return $_[0] eq "dhcp"; 308} 309 310sub can_broadcast_def 311{ 312return 0; 313} 314 315# valid_boot_address(address) 316# Is some address valid for a bootup interface 317sub valid_boot_address 318{ 319return &to_ipaddress($_[0]) ? 1 : 0; 320} 321 322# get_dns_config() 323# Returns a hashtable containing keys nameserver, domain, search & order 324sub get_dns_config 325{ 326local $dns; 327&open_readfile(RESOLV, "/etc/resolv.conf"); 328while(<RESOLV>) { 329 s/\r|\n//g; 330 s/#.*$//g; 331 if (/nameserver\s+(.*)/) { 332 push(@{$dns->{'nameserver'}}, split(/\s+/, $1)); 333 } 334 elsif (/domain\s+(\S+)/) { 335 $dns->{'domain'} = [ $1 ]; 336 } 337 elsif (/search\s+(.*)/) { 338 $dns->{'domain'} = [ split(/\s+/, $1) ]; 339 } 340 } 341close(RESOLV); 342&open_readfile(SWITCH, "/etc/nsswitch.conf"); 343while(<SWITCH>) { 344 s/\r|\n//g; 345 if (/hosts:\s+(.*)/) { 346 $dns->{'order'} = $1; 347 } 348 } 349close(SWITCH); 350$dns->{'files'} = [ "/etc/resolv.conf", "/etc/nsswitch.conf" ]; 351return $dns; 352} 353 354# save_dns_config(&config) 355# Writes out the resolv.conf and nsswitch.conf files 356sub save_dns_config 357{ 358&lock_file("/etc/resolv.conf"); 359&open_readfile(RESOLV, "/etc/resolv.conf"); 360local @resolv = <RESOLV>; 361close(RESOLV); 362&open_tempfile(RESOLV, ">/etc/resolv.conf"); 363foreach (@{$_[0]->{'nameserver'}}) { 364 &print_tempfile(RESOLV, "nameserver $_\n"); 365 } 366if ($_[0]->{'domain'}) { 367 if ($_[0]->{'domain'}->[1]) { 368 &print_tempfile(RESOLV, "search ",join(" ", @{$_[0]->{'domain'}}),"\n"); 369 } 370 else { 371 &print_tempfile(RESOLV, "domain $_[0]->{'domain'}->[0]\n"); 372 } 373 } 374foreach (@resolv) { 375 &print_tempfile(RESOLV, $_) if (!/^\s*(nameserver|domain|search)\s+/); 376 } 377&close_tempfile(RESOLV); 378&unlock_file("/etc/resolv.conf"); 379 380&lock_file("/etc/nsswitch.conf"); 381&open_readfile(SWITCH, "/etc/nsswitch.conf"); 382local @switch = <SWITCH>; 383close(SWITCH); 384&open_tempfile(SWITCH, ">/etc/nsswitch.conf"); 385foreach (@switch) { 386 if (/hosts:\s+/) { 387 &print_tempfile(SWITCH, "hosts:\t$_[0]->{'order'}\n"); 388 } 389 else { 390 &print_tempfile(SWITCH, $_); 391 } 392 } 393&close_tempfile(SWITCH); 394&unlock_file("/etc/nsswitch.conf"); 395} 396 397$max_dns_servers = 3; 398 399# order_input(&dns) 400# Returns HTML for selecting the name resolution order 401sub order_input 402{ 403return &common_order_input("order", $_[0]->{'order'}, 404 [ [ "files", "Hosts" ], [ "dns", "DNS" ], [ "nis", "NIS" ], 405 [ "nisplus", "NIS+" ], [ "ldap", "LDAP" ] ]); 406} 407 408# parse_order(&dns) 409# Parses the form created by order_input() 410sub parse_order 411{ 412if (defined($in{'order'})) { 413 $in{'order'} =~ /\S/ || &error($text{'dns_eorder'}); 414 $_[0]->{'order'} = $in{'order'}; 415 } 416else { 417 local($i, @order); 418 for($i=0; defined($in{"order_$i"}); $i++) { 419 push(@order, $in{"order_$i"}) if ($in{"order_$i"}); 420 } 421 $_[0]->{'order'} = join(" ", @order); 422 } 423} 424 425# get_hostname() 426sub get_hostname 427{ 428local $hn = &read_file_contents("/etc/nodename"); 429$hn =~ s/\r|\n//g; 430if ($hn) { 431 return $hn; 432 } 433return &get_system_hostname(); 434} 435 436# save_hostname(name) 437sub save_hostname 438{ 439&system_logged("hostname $_[0] >/dev/null 2>&1"); 440if (-r "/etc/nodename") { 441 &open_tempfile(NODENAME, ">/etc/nodename"); 442 &print_tempfile(NODENAME, $_[0],"\n"); 443 &close_tempfile(NODENAME); 444 } 445undef(@main::get_system_hostname); # clear cache 446} 447 448# get_domainname() 449sub get_domainname 450{ 451local $d; 452&execute_command("domainname", undef, \$d, undef); 453chop($d); 454return $d; 455} 456 457# save_domainname(domain) 458sub save_domainname 459{ 460&system_logged("domainname ".quotemeta($_[0])); 461&lock_file("/etc/defaultdomain"); 462if ($_[0]) { 463 &open_tempfile(DOMAIN, ">/etc/defaultdomain"); 464 &print_tempfile(DOMAIN, $_[0],"\n"); 465 &close_tempfile(DOMAIN); 466 } 467else { 468 &unlink_file("/etc/defaultdomain"); 469 } 470&unlock_file("/etc/defaultdomain"); 471} 472 473sub routing_config_files 474{ 475return ( "/etc/defaultrouter", "/etc/defaultrouter6", 476 "/etc/notrouter", "/etc/gateways" ); 477} 478 479sub network_config_files 480{ 481return ( "/etc/nodename" ); 482} 483 484# get_defaultrouters() 485# Returns a list of all default routers 486sub get_defaultrouters 487{ 488local @defrt; 489&open_readfile(DEFRT, "/etc/defaultrouter"); 490while(<DEFRT>) { 491 s/#.*$//g; 492 if (/(\S+)/) { push(@defrt, $1); } 493 } 494close(DEFRT); 495return @defrt; 496} 497 498# get_ipv6_defaultrouters() 499# Returns a list of all IPv6 default routers 500sub get_ipv6_defaultrouters 501{ 502local @defrt; 503&open_readfile(DEFRT, "/etc/defaultrouter6"); 504while(<DEFRT>) { 505 s/#.*$//g; 506 if (/(\S+)/) { push(@defrt, $1); } 507 } 508close(DEFRT); 509return @defrt; 510} 511 512sub routing_input 513{ 514# Show default IPv4 router(s) input 515local @defrt = &get_defaultrouters(); 516print &ui_table_row($text{'routes_defaults'}, 517 &ui_textarea("defrt", join("\n", @defrt), 3, 40)); 518 519# Show default IPv6 router(s) input 520local @defrt6 = &get_ipv6_defaultrouters(); 521print &ui_table_row($text{'routes_defaults6'}, 522 &ui_textarea("defrt6", join("\n", @defrt6), 3, 40)); 523 524# Show router input 525local $notrt = (-r "/etc/notrouter"); 526local $gatew = (-r "/etc/gateways"); 527print &ui_table_row($text{'routes_forward'}, 528 &ui_radio("router", $gatew && !$notrt ? 0 : 529 !$gatew && !$notrt ? 1 : 2, 530 [ [ 0, $text{'yes'} ], 531 [ 1, $text{'routes_possible'} ], 532 [ 2, $text{'no'} ] ])); 533} 534 535sub parse_routing 536{ 537# Save IPv4 default routers 538local @defrt = split(/\s+/, $in{'defrt'}); 539foreach my $d (@defrt) { 540 &to_ipaddress($d) || &error(&text('routes_edefault', &html_escape($d))); 541 } 542&lock_file("/etc/defaultrouter"); 543if (@defrt) { 544 &open_tempfile(DEFRT, ">/etc/defaultrouter"); 545 foreach $d (@defrt) { &print_tempfile(DEFRT, $d,"\n"); } 546 &close_tempfile(DEFRT); 547 } 548else { 549 &unlink_file("/etc/defaultrouter"); 550 } 551&unlock_file("/etc/defaultrouter"); 552 553# Save IPv6 default routers 554local @defrt6 = split(/\s+/, $in{'defrt6'}); 555foreach my $d (@defrt6) { 556 &to_ip6address($d) || &error(&text('routes_edefault6', &html_escape($d))); 557 } 558&lock_file("/etc/defaultrouter6"); 559if (@defrt6) { 560 &open_tempfile(DEFRT, ">/etc/defaultrouter6"); 561 foreach $d (@defrt6) { &print_tempfile(DEFRT, $d,"\n"); } 562 &close_tempfile(DEFRT); 563 } 564else { 565 &unlink_file("/etc/defaultrouter6"); 566 } 567&unlock_file("/etc/defaultrouter6"); 568 569# Save router enabled flag 570&lock_file("/etc/gateways"); 571&lock_file("/etc/notrouter"); 572if ($in{'router'} == 0) { 573 &create_empty_file("/etc/gateways"); 574 &unlink_file("/etc/notrouter"); 575 } 576elsif ($in{'router'} == 2) { 577 &create_empty_file("/etc/notrouter"); 578 &unlink_file("/etc/gateways"); 579 } 580else { 581 &unlink_file("/etc/gateways"); 582 &unlink_file("/etc/notrouter"); 583 } 584&unlock_file("/etc/gateways"); 585&unlock_file("/etc/notrouter"); 586} 587 588# create_empty_file(filename) 589sub create_empty_file 590{ 591if (!-r $_[0]) { 592 &open_tempfile(EMPTY,">$_[0]"); 593 &close_tempfile(EMPTY); 594 } 595} 596 597# get_default_gateway() 598# Returns the default gateway IP (if one is set) boot time 599# settings. 600sub get_default_gateway 601{ 602local @defrt = &get_defaultrouters(); 603return @defrt ? ( $defrt[0] ) : ( ); 604} 605 606# set_default_gateway(gateway, device) 607# Sets the default gateway to the given IP accessible via the given device, 608# in the boot time settings. 609sub set_default_gateway 610{ 611&lock_file("/etc/defaultrouter"); 612if ($_[0]) { 613 &open_tempfile(DEF, ">/etc/defaultrouter"); 614 &print_tempfile(DEF, $_[0],"\n"); 615 &close_tempfile(DEF); 616 } 617else { 618 &unlink_file("/etc/defaultrouter"); 619 } 620&unlock_file("/etc/defaultrouter"); 621} 622 623# get_default_ipv6_gateway() 624# Returns the default gateway IPv6 address (if one is set) boot time 625# settings. 626sub get_default_ipv6_gateway 627{ 628local @defrt = &get_ipv6_defaultrouters(); 629return @defrt ? ( $defrt[0] ) : ( ); 630} 631 632# set_default_ipv6_gateway(gateway, device) 633# Sets the default gateway to the given IP accessible via the given device, 634# in the boot time settings. 635sub set_default_ipv6_gateway 636{ 637&lock_file("/etc/defaultrouter6"); 638if ($_[0]) { 639 &open_tempfile(DEF, ">/etc/defaultrouter6"); 640 &print_tempfile(DEF, $_[0],"\n"); 641 &close_tempfile(DEF); 642 } 643else { 644 &unlink_file("/etc/defaultrouter6"); 645 } 646&unlock_file("/etc/defaultrouter6"); 647} 648 649# list_routes() 650# Returns a list of active routes 651sub list_routes 652{ 653local @rv; 654&open_execute_command(ROUTES, "netstat -rn", 1, 1); 655while(<ROUTES>) { 656 s/\s+$//; 657 if (/^([0-9a-f:\.\/]+|default)\s+([0-9a-f:\.]+)\s+\S+\s+\S+\s+\S+(\s+(\S+))?$/) { 658 local $r = { 'dest' => $1 eq "default" ? "0.0.0.0" : $1, 659 'gateway' => $2, 660 'iface' => $4 }; 661 $r->{'netmask'} = $r->{'dest'} eq '0.0.0.0' ? undef : 662 $r->{'dest'} =~ /\.0\.0\.0$/ ? "255.0.0.0" : 663 $r->{'dest'} =~ /\.0\.0$/ ? "255.255.0.0" : 664 $r->{'dest'} =~ /\.0$/ ? "255.255.255.0" : 665 undef; 666 if ($r->{'dest'} =~ s/\/(\d+)$//) { 667 $r->{'netmask'} = $1; 668 } 669 push(@rv, $r); 670 } 671 } 672close(ROUTES); 673return @rv; 674} 675 676# delete_route(&route) 677# Delete one active route, as returned by list_routes. Returns an error message 678# on failure, or undef on success 679sub delete_route 680{ 681local ($route) = @_; 682local $cmd = "route delete"; 683local $inet6 = &check_ip6address($route->{'dest'}) || 684 &check_ip6address($route->{'gateway'}); 685if ($inet6) { 686 $cmd .= " -inet6"; 687 } 688if (!$route->{'dest'} || $route->{'dest'} eq '0.0.0.0') { 689 $cmd .= " default"; 690 } 691else { 692 $cmd .= " $route->{'dest'}"; 693 if ($route->{'netmask'} && $inet6) { 694 $cmd .= "/$route->{'netmask'}"; 695 } 696 } 697if ($route->{'gateway'}) { 698 $cmd .= " $route->{'gateway'}"; 699 } 700elsif ($route->{'iface'}) { 701 local @act = &active_interfaces(); 702 local ($aiface) = grep { $_->{'fullname'} eq $route->{'iface'} } @act; 703 if ($aiface) { 704 $cmd .= " $aiface->{'address'}"; 705 } 706 } 707if ($route->{'netmask'} && !$inet6) { 708 $cmd .= " $route->{'netmask'}"; 709 } 710local $out = &backquote_logged("$cmd 2>&1 </dev/null"); 711return $? ? $out : undef; 712} 713 714# create_route(&route) 715# Adds a new active route 716sub create_route 717{ 718local ($route) = @_; 719local $inet6 = &check_ip6address($route->{'dest'}) || 720 &check_ip6address($route->{'gateway'}); 721local $cmd = "route add "; 722if ($inet6) { 723 $cmd .= " -inet6"; 724 } 725if (!$route->{'dest'}) { 726 $cmd .= " default"; 727 } 728else { 729 $cmd .= " $route->{'dest'}"; 730 if ($route->{'netmask'} && $inet6) { 731 $cmd .= "/$route->{'netmask'}"; 732 } 733 } 734if ($route->{'gateway'}) { 735 $cmd .= " $route->{'gateway'}"; 736 } 737elsif ($route->{'iface'}) { 738 local @act = &active_interfaces(); 739 local ($aiface) = grep { $_->{'fullname'} eq $route->{'iface'} } @act; 740 if ($aiface) { 741 $cmd .= " $aiface->{'address'}"; 742 } 743 } 744if ($route->{'netmask'} && !$inet6) { 745 $cmd .= " $route->{'netmask'}"; 746 } 747local $out = &backquote_logged("$cmd 2>&1 </dev/null"); 748return $? ? $out : undef; 749} 750 751# apply_network() 752# Apply the interface and routing settings, by activating all interfaces and 753# adding the default route 754sub apply_network 755{ 756local (%done, $b, $a); 757 758# Activate all boot-time interfaces 759foreach $b (&boot_interfaces()) { 760 next if ($b->{'name'} eq 'lo0'); 761 &apply_interface($b); 762 $done{$b->{'fullname'}}++; 763 } 764foreach $a (&active_interfaces()) { 765 next if ($a->{'name'} eq 'lo0'); 766 if (!$done{$a->{'fullname'}} && !$a->{'zone'}) { 767 &deactive_interface($a); 768 } 769 } 770 771# Apply default IPv4 router 772local @infile = &get_defaultrouters(); 773local @routes = &list_routes(); 774local @inmem = map { $_->{'gateway'} } 775 grep { $_->{'dest'} eq "0.0.0.0" && 776 !&check_ip6address($_->{'gateway'}) } @routes; 777if (join(" ", @infile) ne join(" ", @inmem)) { 778 # Fix up default routes 779 local $r; 780 foreach $r (@inmem) { 781 &system_logged("route delete default $r >/dev/null 2>&1"); 782 } 783 foreach $r (@infile) { 784 &system_logged("route add default $r >/dev/null 2>&1"); 785 } 786 } 787 788# Apply default IPv6 router 789local @infile = &get_ipv6_defaultrouters(); 790local @routes = &list_routes(); 791local @inmem = map { $_->{'gateway'} } 792 grep { $_->{'dest'} eq "0.0.0.0" && 793 &check_ip6address($_->{'gateway'}) } @routes; 794if (join(" ", @infile) ne join(" ", @inmem)) { 795 # Fix up default routes 796 local $r; 797 foreach $r (@inmem) { 798 &system_logged("route delete -inet6 default $r >/dev/null 2>&1"); 799 } 800 foreach $r (@infile) { 801 &system_logged("route add -inet6 default $r >/dev/null 2>&1"); 802 } 803 } 804} 805 806# apply_interface(&iface) 807# Calls an OS-specific function to make a boot-time interface active 808sub apply_interface 809{ 810if ($_[0]->{'dhcp'}) { 811 local $out = &backquote_logged("cd / ; ifconfig $_[0]->{'fullname'} 0.0.0.0 ; ifconfig $_[0]->{'fullname'} dhcp 2>&1 </dev/null"); 812 return $? || $out =~ /error/i ? $out : undef; 813 } 814else { 815 &activate_interface($_[0]); 816 } 817} 818 819# automatic_netmask(address) 820# Returns the netmask for some address, based on /etc/netmasks 821sub automatic_netmask 822{ 823local ($address) = @_; 824local %mask; 825&open_readfile(MASK, "/etc/netmasks"); 826while(<MASK>) { 827 s/\r|\n//g; 828 s/#.*$//g; 829 if (/([0-9\.]+)\s+([0-9\.]+)/) { 830 $mask{$1} = $2; 831 } 832 } 833close(MASK); 834local ($a1, $a2, $a3, $a4) = split(/\./, $address); 835local $netmask = "255.255.255.0"; 836local $netaddr; 837foreach $netaddr (keys %mask) { 838 $mask{$netaddr} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; 839 local $na = sprintf "%d.%d.%d.%d", 840 int($a1) & int($1), 841 int($a2) & int($2), 842 int($a3) & int($3), 843 int($a4) & int($4); 844 $netmask = $mask{$netaddr} if ($na eq $netaddr); 845 } 846return $netmask; 847} 848 849# supports_address6([&iface]) 850# Returns 1 if managing IPv6 interfaces is supported 851sub supports_address6 852{ 853local ($iface) = @_; 854return !$iface || $iface->{'virtual'} eq '' ? 1 : 0; 855} 856 8571; 858 859