2 # Networking functions for FreeBSD
4 $virtual_netmask = "255.255.255.255"; # Netmask for virtual interfaces
7 # Returns a list of currently ifconfig'd interfaces
10 local(@rv, @lines, $l);
11 local @boot = &boot_interfaces();
12 local %boot = map { $_->{'address'}, $_ } @boot;
13 local %bootname = map { $_->{'fullname'}, $_ } @boot;
14 &open_execute_command(IFC, "ifconfig -a", 1, 1);
17 if (/^\S+:/) { push(@lines, $_); }
18 elsif (@lines) { $lines[$#lines] .= $_; }
24 $ifc{'name'} = $ifc{'fullname'} = $1;
25 if ($l =~ /^(\S+):(\d+):\s/) { $ifc{'virtual'} = $2; }
26 local $bootiface = $bootname{$ifc{'fullname'}};
27 local $bootip = $bootiface ? $bootiface->{'address'} : undef;
28 if ($l =~ s/inet\s+($bootip)\s+netmask\s+(\S+)\s+broadcast\s+(\S+)// ||
29 $l =~ s/inet\s+(\S+)\s+netmask\s+(\S+)\s+broadcast\s+(\S+)//) {
31 $ifc{'netmask'} = &parse_hex($2);
32 $ifc{'broadcast'} = $3;
34 elsif ($l =~ s/inet\s+($bootip)\s+netmask\s+(\S+)// ||
35 $l =~ s/inet\s+(\S+)\s+netmask\s+(\S+)//) {
37 $ifc{'netmask'} = &parse_hex($2);
40 if ($l =~ /ether\s+(\S+)/) { $ifc{'ether'} = $1; }
41 if ($l =~ /mtu\s+(\S+)/) { $ifc{'mtu'} = $1; }
42 $ifc{'up'}++ if ($l =~ /\<UP/);
43 $ifc{'edit'} = &iface_type($ifc{'name'}) =~ /ethernet|loopback/i;
44 $ifc{'index'} = scalar(@rv);
46 $ifc{'ether'} = join(":", map { length($_) == 1 ? "0".$_ : $_ }
47 split(/:/, $ifc{'ether'}));
51 # Add aliases as virtual interfaces. Try to match boot-time interface
52 # numbers where possible
53 local %vtaken = map { $_->{'virtual'}, 1 }
54 grep { $_->{'name'} eq $vifc{'name'} &&
55 $_->{'virtual'} ne "" } @boot;
56 while($l =~ s/inet\s+(\S+)\s+netmask\s+(\S+)(\s+broadcast\s+(\S+))?//) {
58 $vifc{'address'} = $1;
59 $vifc{'netmask'} = &parse_hex($2);
60 $vifc{'broadcast'} = $4;
62 $vifc{'edit'} = $ifc{'edit'};
63 local $boot = $boot{$vifc{'address'}};
65 $vifc{'virtual'} = $boot->{'virtual'};
68 for($vifc{'virtual'}=0; $vtaken{$vifc{'virtual'}};
69 $vifc{'virtual'}++) { }
71 $vtaken{$vifc{'virtual'}}++;
72 $vifc{'fullname'} = $vifc{'name'}.':'.$vifc{'virtual'};
73 $vifc{'index'} = scalar(@rv);
80 # activate_interface(&details)
81 # Create or modify an interface
82 sub activate_interface
85 map { $act{$_->{'fullname'}} = $_ } &active_interfaces();
86 local $old = $act{$_[0]->{'fullname'}};
87 $act{$_[0]->{'fullname'}} = $_[0];
88 &interface_sync(\%act, $_[0]->{'name'}, $_[0]->{'fullname'});
91 # deactivate_interface(&details)
92 # Deactive an interface
93 sub deactivate_interface
96 local @act = &active_interfaces();
97 if ($_[0]->{'virtual'} eq '') {
98 @act = grep { $_->{'name'} ne $_[0]->{'name'} } @act;
101 @act = grep { $_->{'fullname'} ne $_[0]->{'fullname'} } @act;
103 map { $act{$_->{'fullname'}} = $_ } @act;
104 &interface_sync(\%act, $_[0]->{'name'}, $_[0]->{'fullname'});
107 # interface_sync(interfaces, name, changee)
110 # Remove all IP addresses except for the primary one (unless it is being edited)
111 local $pri = $_[0]->{$_[1]};
112 local $ifconfig = &has_command("ifconfig");
115 &execute_command("$ifconfig $_[1]", undef, \$out);
116 last if ($out !~ /([\000-\377]*)\s+inet\s+(\d+\.\d+\.\d+\.\d+)/);
117 last if ($2 eq $pri->{'address'} && $_[2] ne $pri->{'fullname'});
118 &system_logged("$ifconfig $_[1] delete $2 >/dev/null 2>&1");
121 # Add them back again, except for the primary unless it is being changed
122 foreach $a (sort { $a->{'fullname'} cmp $b->{'fullname'} }
123 grep { $_->{'name'} eq $_[1] } values(%{$_[0]})) {
124 next if ($a->{'fullname'} eq $pri->{'fullname'} &&
125 $_[2] ne $pri->{'fullname'});
126 local $cmd = "$ifconfig $a->{'name'}";
127 if ($a->{'virtual'} ne '') {
128 $cmd .= " alias $a->{'address'}";
131 $cmd .= " $a->{'address'}";
133 if ($a->{'netmask'}) { $cmd .= " netmask $a->{'netmask'}"; }
134 if ($a->{'broadcast'}) { $cmd .= " broadcast $a->{'broadcast'}"; }
135 if ($a->{'mtu'}) { $cmd .= " mtu $a->{'mtu'}"; }
136 local $out = &backquote_logged("$cmd 2>&1");
137 #if ($? && $out !~ /file exists/i) {
141 if ($a->{'virtual'} eq '') {
142 if ($a->{'up'}) { $out = &backquote_logged("$ifconfig $a->{'name'} up 2>&1"); }
143 else { $out = &backquote_logged("$ifconfig $a->{'name'} down 2>&1"); }
144 &error($out) if ($?);
150 # Returns a list of interfaces brought up at boot time
153 local %rc = &get_rc_conf();
155 foreach $r (keys %rc) {
158 if ($r =~ /^ifconfig_([a-z0-9]+)$/) {
159 # Non-virtual interface
160 %ifc = ( 'name' => $1,
163 elsif ($r =~ /^ifconfig_([a-z0-9]+)_alias(\d+)$/) {
165 %ifc = ( 'name' => $1,
167 'fullname' => "$1:$2" );
171 if ($v =~ /^inet\s+(\S+)/ || /^([0-9\.]+)/) {
172 $ifc{'address'} = $1;
175 local @a = split(/\./, $ifc{'address'});
176 if ($v =~ /netmask\s+(0x\S+)/) {
177 $ifc{'netmask'} = &parse_hex($1);
179 elsif ($v =~ /netmask\s+([0-9\.]+)/) {
180 $ifc{'netmask'} = $1;
183 $ifc{'netmask'} = $a[0] >= 192 ? "255.255.255.0" :
184 $a[0] >= 128 ? "255.255.0.0" :
187 if ($v =~ /broadcast\s+(0x\S+)/) {
188 $ifc{'broadcast'} = &parse_hex($1);
190 elsif ($v =~ /broadcast\s+([0-9\.]+)/) {
191 $ifc{'broadcast'} = $1;
194 local @n = split(/\./, $ifc{'netmask'});
195 $ifc{'broadcast'} = sprintf "%d.%d.%d.%d",
196 ($a[0] | ~int($n[0]))&0xff,
197 ($a[1] | ~int($n[1]))&0xff,
198 ($a[2] | ~int($n[2]))&0xff,
199 ($a[3] | ~int($n[3]))&0xff;
201 $ifc{'mtu'} = $1 if ($v =~ /mtu\s+(\d+)/);
204 $ifc{'index'} = scalar(@rv);
205 $ifc{'file'} = "/etc/rc.conf";
211 # save_interface(&details)
212 # Create or update a boot-time interface
215 local $str = "inet $_[0]->{'address'}";
216 $str .= " netmask $_[0]->{'netmask'}" if ($_[0]->{'netmask'});
217 $str .= " broadcast $_[0]->{'broadcast'}" if ($_[0]->{'broadcast'});
218 &lock_file("/etc/rc.conf");
219 if ($_[0]->{'virtual'} eq '') {
220 &save_rc_conf('ifconfig_'.$_[0]->{'name'}, $str);
223 local @boot = &boot_interfaces();
224 local ($old) = grep { $_->{'fullname'} eq $_[0]->{'fullname'} } @boot;
225 if (!$old && $_[0]->{'virtual'} ne '') {
226 # A new virtual interface .. pick a virtual number automaticlly
228 $_[0]->{'virtual'} = 0;
229 foreach $b (&boot_interfaces()) {
230 if ($b->{'name'} eq $_[0]->{'name'} &&
231 $b->{'virtual'} ne '' &&
232 $b->{'virtual'} >= $_[0]->{'virtual'}) {
233 $_[0]->{'virtual'} = $b->{'virtual'}+1;
236 $_[0]->{'fullname'} = $_[0]->{'name'}.':'.$_[0]->{'virtual'};
238 &save_rc_conf('ifconfig_'.$_[0]->{'name'}.'_alias'.$_[0]->{'virtual'},
241 &unlock_file("/etc/rc.conf");
244 # delete_interface(&details, [noshift])
245 # Delete a boot-time interface
248 &lock_file("/etc/rc.conf");
249 if ($_[0]->{'virtual'} eq '') {
250 # Remove the real interface
251 &save_rc_conf('ifconfig_'.$_[0]->{'name'});
254 # Remove a virtual interface, and shift down all aliases above it
255 &save_rc_conf('ifconfig_'.$_[0]->{'name'}.'_alias'.$_[0]->{'virtual'});
258 foreach $b (&boot_interfaces()) {
259 if ($b->{'name'} eq $_[0]->{'name'} &&
260 $b->{'virtual'} ne '' &&
261 $b->{'virtual'} > $_[0]->{'virtual'}) {
262 # This one needs to be shifted down
268 &delete_interface(\%lastb, 1) if (%lastb);
271 &unlock_file("/etc/rc.conf");
275 # Returns a human-readable interface type name
278 return $_[0] =~ /^tun/ ? "Loopback tunnel" :
279 $_[0] =~ /^sl/ ? "SLIP" :
280 $_[0] =~ /^ppp/ ? "PPP" :
281 $_[0] =~ /^lo/ ? "Loopback" :
282 $_[0] =~ /^ar/ ? "Arnet" :
283 $_[0] =~ /^(wlan|athi|ral)/ ? "Wireless ethernet" :
284 $_[0] =~ /^(bge|em|myk)/ ? "Gigabit ethernet" :
285 $_[0] =~ /^(ax|mx|nve|pn|rl|tx|wb|nfe|sis)/ ? "Fast ethernet" :
286 $_[0] =~ /^(cs|dc|de|ed|el|ex|fe|fxp|ie|le|lnc|tl|vr|vx|xl|ze|zp)/ ? "Ethernet" : $text{'ifcs_unknown'};
289 # iface_hardware(name)
290 # Does some interface have an editable hardware address
297 # Can some boot-time interface parameter be edited?
300 return $_[0] =~ /netmask|broadcast/;
303 # valid_boot_address(address)
304 # Is some address valid for a bootup interface
305 sub valid_boot_address
307 return &check_ipaddress($_[0]);
311 # Returns a hashtable containing keys nameserver, domain, search & order
315 &open_readfile(RESOLV, "/etc/resolv.conf");
319 if (/nameserver\s+(.*)/) {
320 push(@{$dns->{'nameserver'}}, split(/\s+/, $1));
322 elsif (/domain\s+(\S+)/) {
323 $dns->{'domain'} = [ $1 ];
325 elsif (/search\s+(.*)/) {
326 $dns->{'domain'} = [ split(/\s+/, $1) ];
333 if (-r "/etc/nsswitch.conf") {
334 # FreeBSD 5.0 and later use nsswitch.conf
335 $orderfile = "/etc/nsswitch.conf";
336 &open_readfile(SWITCH, $orderfile);
339 if (/^\s*hosts:\s+(.*)/) {
340 $dns->{'order'} = $1;
346 # Older versions use host.conf
347 $orderfile = "/etc/host.conf";
348 &open_readfile(HOST, $orderfile);
352 push(@order, $_) if (/\S/);
355 $dns->{'order'} = join(" ", @order);
357 $dns->{'files'} = [ "/etc/resolv.conf", $orderfile ];
361 # save_dns_config(&config)
362 # Writes out the resolv.conf and host.conf files
365 &lock_file("/etc/resolv.conf");
366 &open_readfile(RESOLV, "/etc/resolv.conf");
367 local @resolv = <RESOLV>;
369 &open_tempfile(RESOLV, ">/etc/resolv.conf");
370 foreach (@{$_[0]->{'nameserver'}}) {
371 print RESOLV "nameserver $_\n";
373 if ($_[0]->{'domain'}) {
374 if ($_[0]->{'domain'}->[1]) {
375 &print_tempfile(RESOLV, "search ",join(" ", @{$_[0]->{'domain'}}),"\n");
378 &print_tempfile(RESOLV, "domain $_[0]->{'domain'}->[0]\n");
382 &print_tempfile(RESOLV, $_) if (!/^\s*(nameserver|domain|search)\s+/);
384 &close_tempfile(RESOLV);
385 &unlock_file("/etc/resolv.conf");
387 if (-r "/etc/nsswitch.conf") {
388 # Save to new nsswitch.conf, for FreeBSD 5.0 and later
389 &lock_file("/etc/nsswitch.conf");
390 &open_readfile(SWITCH, "/etc/nsswitch.conf");
391 local @switch = <SWITCH>;
393 &open_tempfile(SWITCH, ">/etc/nsswitch.conf");
395 if (/^\s*hosts:\s+/) {
396 &print_tempfile(SWITCH, "hosts:\t$_[0]->{'order'}\n");
399 &print_tempfile(SWITCH, $_);
402 &close_tempfile(SWITCH);
403 &unlock_file("/etc/nsswitch.conf");
406 # Save to older host.conf
407 &open_lock_tempfile(HOST, ">/etc/host.conf");
408 foreach my $o (split(/\s+/, $_[0]->{'order'})) {
409 &print_tempfile(HOST, $o,"\n");
411 &close_tempfile(HOST);
415 $max_dns_servers = 3;
418 # Returns HTML for selecting the name resolution order
421 if (-r "/etc/nsswitch.conf") {
422 # FreeBSD 5.0 and later use nsswitch.conf with more options
423 return &common_order_input("order", $_[0]->{'order'},
424 [ [ "files", "Files" ], [ "dns", "DNS" ],
425 [ "nis", "NIS" ], [ "cache", "NSCD" ] ]);
428 # Older FreeBSD's have fewer options
429 local $dnsopt = $_[0]->{'order'} =~ /dns/ ? 'dns' : 'bind';
430 return &common_order_input("order", $_[0]->{'order'},
431 [ [ "hosts", "Hosts" ], [ $dnsopt, "DNS" ], [ "nis", "NIS" ] ]);
436 # Parses the form created by order_input()
439 if (defined($in{'order'})) {
440 $in{'order'} =~ /\S/ || &error($text{'dns_eorder'});
441 $_[0]->{'order'} = $in{'order'};
445 for($i=0; defined($in{"order_$i"}); $i++) {
446 push(@order, $in{"order_$i"}) if ($in{"order_$i"});
448 $_[0]->{'order'} = join(" ", @order);
455 local %rc = &get_rc_conf();
456 if ($rc{'hostname'}) {
457 return $rc{'hostname'};
459 return &get_system_hostname();
462 # save_hostname(name)
465 &lock_file("/etc/rc.conf");
466 &system_logged("hostname $_[0] >/dev/null 2>&1");
467 &save_rc_conf('hostname', $_[0]);
468 &unlock_file("/etc/rc.conf");
469 undef(@main::get_system_hostname); # clear cache
472 sub routing_config_files
474 return ( "/etc/defaults/rc.conf", "/etc/rc.conf" );
479 local %rc = &get_rc_conf();
482 local $defr = $rc{'defaultrouter'};
483 print &ui_table_row($text{'routes_default'},
484 &ui_opt_textbox("defr", $defr eq 'NO' ? '' : $defr, 20,
485 $text{'routes_none'}));
488 local $gw = $rc{'gateway_enable'};
489 print &ui_table_row($text{'routes_forward'},
490 &ui_radio("gw", $gw || 'NO', [ [ 'YES', $text{'yes'} ],
491 [ 'NO', $text{'no'} ] ]));
493 # Run route discovery
494 local $rd = $rc{'router_enable'};
495 print &ui_table_row($text{'routes_routed'},
496 &ui_radio("rd", $rd || 'NO', [ [ 'YES', $text{'yes'} ],
497 [ 'NO', $text{'no'} ] ]));
502 &lock_file("/etc/rc.conf");
503 $in{'defr_def'} || &check_ipaddress($in{'defr'}) ||
504 &error(&text('routes_edefault', $in{'defr'}));
505 &save_rc_conf('defaultrouter', $in{'defr_def'} ? 'NO' : $in{'defr'});
506 &save_rc_conf('gateway_enable', $in{'gw'});
507 &save_rc_conf('router_enable', $in{'rd'});
508 &unlock_file("/etc/rc.conf");
511 # save_rc_conf(name, value)
515 &open_readfile(CONF, "/etc/rc.conf");
516 local @conf = <CONF>;
518 &open_tempfile(CONF, ">/etc/rc.conf");
520 if (/^\s*([^=]+)\s*=\s*(.*)/ && $1 eq $_[0]) {
521 &print_tempfile(CONF, "$_[0]=\"$_[1]\"\n") if (@_ > 1);
525 &print_tempfile(CONF, $_);
528 if (!$found && @_ > 1) {
529 &print_tempfile(CONF, "$_[0]=\"$_[1]\"\n");
531 &close_tempfile(CONF);
538 foreach $file ("/etc/defaults/rc.conf",
539 glob("/etc/rc.conf.d/*"),
541 &open_readfile(FILE, $file);
545 if (/^\s*([^=\s]+)\s*=\s*"(.*)"/ ||
546 /^\s*([^=\s]+)\s*=\s*(\S+)/) {
556 # Apply the interface and routing settings
559 local $oldpwd = &get_current_dir();
562 # Take down all active alias interfaces, and any that no longer exist
563 local %boot = map { $_->{'fullname'}, $_ } &boot_interfaces();
564 foreach my $i (&active_interfaces()) {
565 if ($i->{'virtual'} ne '' || !$boot{$i->{'fullname'}}) {
566 &deactivate_interface($i);
569 # Bring everything up
570 &system_logged("/etc/netstart >/dev/null 2>&1");
574 sub os_feedback_files
576 return ( "/etc/rc.conf", "/etc/resolv.conf", "/etc/host.conf",
577 "/etc/resolv.conf" );
580 # supports_address6([&iface])
581 # Returns 1 if managing IPv6 interfaces is supported
582 sub supports_address6