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