mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
4296 lines
148 KiB
PHP
4296 lines
148 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright (C) 2015-2024 Franco Fichtner <franco@opnsense.org>
|
|
* Copyright (C) 2004-2008 Scott Ullrich <sullrich@gmail.com>
|
|
* Copyright (C) 2008-2009 Ermal Luçi
|
|
* Copyright (C) 2005 Espen Johansen
|
|
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
require_once("interfaces.lib.inc");
|
|
|
|
/*
|
|
* converts a string like "a,b,c,d"
|
|
* into an array like array("a" => "b", "c" => "d")
|
|
*/
|
|
function explode_assoc($delimiter, $string)
|
|
{
|
|
$array = explode($delimiter, $string);
|
|
$result = array();
|
|
$numkeys = floor(count($array) / 2);
|
|
for ($i = 0; $i < $numkeys; $i += 1) {
|
|
$result[$array[$i * 2]] = $array[$i * 2 + 1];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
function return_hex_ipv4($ipv4)
|
|
{
|
|
if (!is_ipaddrv4($ipv4)) {
|
|
return false;
|
|
}
|
|
|
|
/* we need the hex form of the interface IPv4 address */
|
|
$ip4arr = explode(".", $ipv4);
|
|
return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
|
|
}
|
|
|
|
function convert_ipv6_to_128bit($ipv6)
|
|
{
|
|
if (!is_ipaddrv6($ipv6)) {
|
|
return false;
|
|
}
|
|
|
|
$ip6prefix = Net_IPv6::uncompress($ipv6);
|
|
$ip6arr = explode(":", $ip6prefix);
|
|
/* binary presentation of the prefix for all 128 bits. */
|
|
$ip6prefixbin = "";
|
|
foreach ($ip6arr as $element) {
|
|
$ip6prefixbin .= sprintf("%016b", hexdec($element));
|
|
}
|
|
return $ip6prefixbin;
|
|
}
|
|
|
|
function convert_128bit_to_ipv6($ip6bin)
|
|
{
|
|
if (strlen($ip6bin) != 128) {
|
|
return false;
|
|
}
|
|
|
|
$ip6arr = array();
|
|
$ip6binarr = str_split($ip6bin, 16);
|
|
foreach ($ip6binarr as $binpart) {
|
|
$ip6arr[] = dechex(bindec($binpart));
|
|
}
|
|
$ip6addr = Net_IPv6::compress(implode(":", $ip6arr));
|
|
|
|
return $ip6addr;
|
|
}
|
|
|
|
function does_interface_exist($interface, $flag = 'all')
|
|
{
|
|
return !empty($interface) && in_array($interface, legacy_interface_listget($flag));
|
|
}
|
|
|
|
function interfaces_loopback_configure($verbose = false)
|
|
{
|
|
service_log('Configuring loopback interface...', $verbose);
|
|
|
|
legacy_interface_setaddress('lo0', '127.0.0.1');
|
|
interfaces_vips_configure('lo0');
|
|
legacy_interface_flags('lo0', 'up');
|
|
|
|
service_log("done.\n", $verbose);
|
|
}
|
|
|
|
function interfaces_vlan_priorities()
|
|
{
|
|
$priorities = array();
|
|
|
|
$priorities['1'] = gettext('Background (1, lowest)');
|
|
$priorities['0'] = gettext('Best Effort (0, default)');
|
|
$priorities['2'] = gettext('Excellent Effort (2)');
|
|
$priorities['3'] = gettext('Critical Applications (3)');
|
|
$priorities['4'] = gettext('Video (4)');
|
|
$priorities['5'] = gettext('Voice (5)');
|
|
$priorities['6'] = gettext('Internetwork Control (6)');
|
|
$priorities['7'] = gettext('Network Control (7, highest)');
|
|
|
|
return $priorities;
|
|
}
|
|
|
|
function interfaces_vlan_configure($verbose = false)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['vlans']['vlan'])) {
|
|
return;
|
|
}
|
|
|
|
service_log('Configuring VLAN interfaces...', $verbose);
|
|
|
|
/* XXX sorting vlans here on top of $config seems prone to further side effects */
|
|
// Handle QinQ dependencies by sorting list of vlans to create (first all vlans so we can stack QinQ on top)
|
|
usort($config['vlans']['vlan'], function ($a, $b) {
|
|
$aqinq = strpos($a['vlanif'], 'vlan') !== false ? 0 : 1;
|
|
$bqinq = strpos($b['vlanif'], 'vlan') !== false ? 0 : 1;
|
|
if ($aqinq === $bqinq) {
|
|
return $a['vlanif'] <=> $b['vlanif'];
|
|
} else {
|
|
return $aqinq <=> $bqinq;
|
|
}
|
|
});
|
|
|
|
/* requested vlan protocol, when the vlan has vlans as children, the 802.1ad (QinQ) proto should be used */
|
|
$all_parents = [];
|
|
foreach ($config['vlans']['vlan'] as $vlan) {
|
|
if (!in_array($vlan['vlanif'], $all_parents)) {
|
|
if (!isset($all_parents[$vlan['if']])) {
|
|
$all_parents[$vlan['if']] = 0;
|
|
}
|
|
$all_parents[$vlan['if']]++;
|
|
}
|
|
}
|
|
foreach ($config['vlans']['vlan'] as $vlan) {
|
|
if (empty($vlan['proto'])) {
|
|
$vlan['proto'] = empty($all_parents[$vlan['vlanif']]) ? '802.1q' : '802.1ad';
|
|
}
|
|
_interfaces_vlan_configure($vlan);
|
|
}
|
|
|
|
service_log("done.\n", $verbose);
|
|
}
|
|
|
|
function _interfaces_vlan_configure($vlan)
|
|
{
|
|
legacy_interface_flags($vlan['if'], 'up'); /* XXX overreach? */
|
|
|
|
/* destroy is a safety precaution, when configuring via api or gui this function should only be called on new vlans */
|
|
legacy_interface_destroy($vlan['vlanif']);
|
|
legacy_interface_create('vlan', $vlan['vlanif']);
|
|
|
|
legacy_vlan_tag($vlan['vlanif'], $vlan['if'], $vlan['tag'], $vlan['pcp'], $vlan['proto']);
|
|
|
|
legacy_interface_flags($vlan['vlanif'], 'up');
|
|
}
|
|
|
|
function interfaces_wlan_clone($device)
|
|
{
|
|
global $config;
|
|
|
|
foreach (array_keys(get_configured_interface_with_descr()) as $if) {
|
|
if (!isset($config['interfaces'][$if]['wireless'])) {
|
|
continue;
|
|
}
|
|
$realif = get_real_interface($if);
|
|
/* XXX 'if' check only required if parent is still embedded */
|
|
if ($device == $realif || $device == $config['interfaces'][$if]['if']) {
|
|
return _interfaces_wlan_clone($realif, $config['interfaces'][$if]);
|
|
}
|
|
}
|
|
|
|
if (isset($config['wireless']['clone'])) {
|
|
foreach ($config['wireless']['clone'] as $clone) {
|
|
if ($device == $clone['cloneif']) {
|
|
return _interfaces_wlan_clone($clone['cloneif'], $clone);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function interfaces_bridge_configure($device)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['bridges']['bridged'])) {
|
|
return null;
|
|
}
|
|
|
|
foreach ($config['bridges']['bridged'] as $bridge) {
|
|
if ($bridge['bridgeif'] == $device) {
|
|
return _interfaces_bridge_configure($bridge);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function _interfaces_bridge_configure($bridge)
|
|
{
|
|
$ret = $bridge['bridgeif'];
|
|
|
|
/* XXX avoid destroy/create */
|
|
legacy_interface_destroy($bridge['bridgeif']);
|
|
legacy_interface_create($bridge['bridgeif']);
|
|
|
|
$checklist = get_configured_interface_with_descr();
|
|
$members = [];
|
|
|
|
/* find all required members */
|
|
foreach (explode(',', $bridge['members'] ?? '') as $member) {
|
|
if (empty($checklist[$member])) {
|
|
/* ignores disabled ones */
|
|
continue;
|
|
}
|
|
|
|
$device = get_real_interface($member);
|
|
if (!does_interface_exist($device)) {
|
|
log_msg("Device {$bridge['bridgeif']} cannot attach non-existent member {$device}, skipping now.");
|
|
/* continue but mark this as failed for caller so they could retry */
|
|
$ret = null;
|
|
continue;
|
|
}
|
|
|
|
$members[$member] = $device;
|
|
}
|
|
|
|
/* calculate smaller mtu and enforce it */
|
|
$mtu = null;
|
|
foreach ($members as $member => $device) {
|
|
$opts = legacy_interface_stats($device);
|
|
if (!empty($opts['mtu']) && ($mtu == null || $opts['mtu'] < $mtu)) {
|
|
$mtu = $opts['mtu'];
|
|
}
|
|
}
|
|
|
|
mwexecf('/sbin/ifconfig %s inet6 %sauto_linklocal', [$bridge['bridgeif'], isset($bridge['linklocal']) ? '' : '-']);
|
|
|
|
/* add member interfaces to bridge */
|
|
foreach ($members as $member => $device) {
|
|
configure_interface_hardware($device);
|
|
legacy_interface_mtu($device, $mtu);
|
|
legacy_interface_flags($device, 'up');
|
|
legacy_bridge_member($bridge['bridgeif'], $device);
|
|
}
|
|
|
|
if (isset($bridge['enablestp'])) {
|
|
mwexecf('/sbin/ifconfig %s proto %s', [$bridge['bridgeif'], $bridge['proto']]);
|
|
if (!empty($bridge['stp'])) {
|
|
foreach (explode(',', $bridge['stp']) as $stpif) {
|
|
mwexecf('/sbin/ifconfig %s stp %s', [$bridge['bridgeif'], get_real_interface($stpif)]);
|
|
}
|
|
}
|
|
if (!empty($bridge['maxage'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} maxage " . escapeshellarg($bridge['maxage']));
|
|
}
|
|
if (!empty($bridge['fwdelay'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} fwddelay " . escapeshellarg($bridge['fwdelay']));
|
|
}
|
|
if (!empty($bridge['hellotime'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} hellotime " . escapeshellarg($bridge['hellotime']));
|
|
}
|
|
if (!empty($bridge['holdcnt'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} holdcnt " . escapeshellarg($bridge['holdcnt']));
|
|
}
|
|
}
|
|
|
|
if (!empty($bridge['maxaddr'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} maxaddr " . escapeshellarg($bridge['maxaddr']));
|
|
}
|
|
if (!empty($bridge['timeout'])) {
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} timeout " . escapeshellarg($bridge['timeout']));
|
|
}
|
|
if (!empty($bridge['span'])) {
|
|
$realif = get_real_interface($bridge['span']);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} span {$realif}");
|
|
}
|
|
if (!empty($bridge['edge'])) {
|
|
foreach (explode(',', $bridge['edge']) as $edgeif) {
|
|
$realif = get_real_interface($edgeif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} edge {$realif}");
|
|
}
|
|
}
|
|
if (!empty($bridge['autoedge'])) {
|
|
foreach (explode(',', $bridge['autoedge']) as $edgeif) {
|
|
$realif = get_real_interface($edgeif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} -autoedge {$realif}");
|
|
}
|
|
}
|
|
if (!empty($bridge['ptp'])) {
|
|
foreach (explode(',', $bridge['ptp']) as $ptpif) {
|
|
$realif = get_real_interface($ptpif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} ptp {$realif}");
|
|
}
|
|
}
|
|
if (!empty($bridge['autoptp'])) {
|
|
foreach (explode(',', $bridge['autoptp']) as $ptpif) {
|
|
$realif = get_real_interface($ptpif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} -autoptp {$realif}");
|
|
}
|
|
}
|
|
if (!empty($bridge['static'])) {
|
|
foreach (explode(',', $bridge['static']) as $stickyif) {
|
|
$realif = get_real_interface($stickyif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} sticky {$realif}");
|
|
}
|
|
}
|
|
if (!empty($bridge['private'])) {
|
|
foreach (explode(',', $bridge['private']) as $privateif) {
|
|
$realif = get_real_interface($privateif);
|
|
mwexec("/sbin/ifconfig {$bridge['bridgeif']} private {$realif}");
|
|
}
|
|
}
|
|
|
|
legacy_interface_flags($bridge['bridgeif'], 'up');
|
|
|
|
return $ret;
|
|
}
|
|
|
|
function interfaces_lagg_configure($verbose = false)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['laggs']['lagg'])) {
|
|
return;
|
|
}
|
|
|
|
service_log('Configuring LAGG interfaces...', $verbose);
|
|
|
|
foreach ($config['laggs']['lagg'] as $lagg) {
|
|
_interfaces_lagg_configure($lagg);
|
|
}
|
|
|
|
service_log("done.\n", $verbose);
|
|
}
|
|
|
|
function _interfaces_lagg_configure($lagg)
|
|
{
|
|
if (empty($lagg['members'])) {
|
|
/* XXX really necessary? we would like our LAGG anyway */
|
|
log_msg("No members found on {$lagg['laggif']}", LOG_WARNING);
|
|
return;
|
|
}
|
|
|
|
$interface_stats = legacy_interfaces_details();
|
|
$members = explode(',', $lagg['members']);
|
|
if (!empty($lagg['primary_member'])) {
|
|
/* place primary member as first member */
|
|
$members = array_unique(array_merge([$lagg['primary_member']], $members));
|
|
}
|
|
|
|
if (empty($interface_stats[$lagg['laggif']])) {
|
|
legacy_interface_create($lagg['laggif']);
|
|
} else {
|
|
// Already configured, detach child interfaces before attempting to configure.
|
|
// Prevents vlans to loose parent.
|
|
if (!empty($interface_stats[$lagg['laggif']]['laggport'])) {
|
|
foreach (array_keys($interface_stats[$lagg['laggif']]['laggport']) as $laggport) {
|
|
mwexecf('/sbin/ifconfig %s -laggport %s', [$lagg['laggif'], $laggport]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine mtu value to use, either the provided one for the lagg or smallest of its children
|
|
$mtu = null;
|
|
if (!empty($lagg['mtu'])) {
|
|
// mtu provided for lagg
|
|
$mtu = $lagg['mtu'];
|
|
} else {
|
|
// min() mtu of children
|
|
foreach ($members as $member) {
|
|
if (!empty($interface_stats[$member]['mtu']) && ($mtu == null || $interface_stats[$member]['mtu'] < $mtu)) {
|
|
$mtu = $interface_stats[$member]['mtu'];
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($members as $member) {
|
|
if (!empty($interface_stats[$member])) {
|
|
/*
|
|
* XXX A LAGG does not allow to set the MTU on its ports individually
|
|
* so we would rather set the MTU on the LAGG after adding all ports.
|
|
* Thus we should avoid runtime entanglement of MTU of ports to find
|
|
* the smallest one since they already follow the LAGG setting despite
|
|
* showing something else in ifconfig and defaulting to 1500 from the
|
|
* LAGG side anyway.
|
|
*/
|
|
legacy_interface_mtu($member, $mtu);
|
|
configure_interface_hardware($member);
|
|
legacy_interface_flags($member, 'up');
|
|
mwexecf('/sbin/ifconfig %s laggport %s', [$lagg['laggif'], $member]);
|
|
}
|
|
}
|
|
|
|
mwexecf('/sbin/ifconfig %s laggproto %s', [$lagg['laggif'], $lagg['proto']]);
|
|
if (in_array($lagg['proto'], ['lacp', 'loadbalance'])) {
|
|
foreach (['lacp_fast_timeout', 'use_flowid', 'lacp_strict'] as $attr) {
|
|
$attr_proto = strpos($attr, 'lacp_') !== false ? 'lacp' : $lagg['proto'];
|
|
if (isset($lagg[$attr]) && $attr_proto == $lagg['proto']) {
|
|
mwexecf('/sbin/ifconfig %s %s%s', [$lagg['laggif'], empty($lagg[$attr]) ? '-' : '', $attr]);
|
|
}
|
|
}
|
|
$configured_hash = !empty($lagg['lagghash']) ? $lagg['lagghash'] : 'l2,l3,l4';
|
|
mwexecf('/sbin/ifconfig %s lagghash %s', [$lagg['laggif'], $configured_hash]);
|
|
}
|
|
|
|
legacy_interface_flags($lagg['laggif'], 'up');
|
|
}
|
|
|
|
function interfaces_gre_configure($device)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['gres']['gre'])) {
|
|
return null;
|
|
}
|
|
|
|
foreach ($config['gres']['gre'] as $gre) {
|
|
if ($gre['greif'] == $device) {
|
|
return _interfaces_gre_configure($gre);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function _interfaces_gre_configure($gre)
|
|
{
|
|
if (!does_interface_exist($gre['greif'])) {
|
|
/* Only create when not already there */
|
|
legacy_interface_create($gre['greif']);
|
|
}
|
|
|
|
$family = is_ipaddrv4($gre['remote-addr']) ? 'inet' : 'inet6';
|
|
if (!empty($gre['ipaddr'])) {
|
|
/* MVC splits content into if and ipaddr fields, legacy input stores the address in "if",
|
|
in which case get_interface_* will return an address when offered */
|
|
$local_addr = $gre['ipaddr'];
|
|
} elseif ($family == 'inet') {
|
|
$local_addr = get_interface_ip($gre['if']);
|
|
} else {
|
|
$local_addr = get_interface_ipv6($gre['if'], null, is_linklocal($gre['remote-addr']) ? 'scoped' : 'routed');
|
|
}
|
|
|
|
/* ensured device is there, but do not configure unless system is ready */
|
|
if (empty($local_addr)) {
|
|
log_msg("Device {$gre['greif']} missing required local address, skipping now.");
|
|
return null;
|
|
}
|
|
|
|
$remote_addr = $gre['remote-addr'] . (strpos($local_addr, '%') === false ? '' : '%' . explode('%', $local_addr)[1]);
|
|
|
|
mwexecf('/sbin/ifconfig %s %s tunnel %s %s', [$gre['greif'], $family, $local_addr, $remote_addr]);
|
|
|
|
if (is_ipaddrv6($gre['tunnel-local-addr']) || is_ipaddrv6($gre['tunnel-remote-addr'])) {
|
|
/* check if destination is local to source and if not we need the traditional point-to-point setup */
|
|
if ($gre['tunnel-remote-net'] != '128' && ip_in_subnet($gre['tunnel-remote-addr'], "{$gre['tunnel-local-addr']}/{$gre['tunnel-remote-net']}")) {
|
|
mwexecf('/sbin/ifconfig %s inet6 %s prefixlen %s', [
|
|
$gre['greif'],
|
|
$gre['tunnel-local-addr'],
|
|
$gre['tunnel-remote-net'],
|
|
]);
|
|
} else {
|
|
mwexecf('/sbin/ifconfig %s inet6 %s %s prefixlen 128', [
|
|
$gre['greif'],
|
|
$gre['tunnel-local-addr'],
|
|
$gre['tunnel-remote-addr'],
|
|
]);
|
|
}
|
|
} else {
|
|
mwexecf('/sbin/ifconfig %s inet6 ifdisabled', [$gre['greif']]);
|
|
mwexecf('/sbin/ifconfig %s %s %s netmask %s', [
|
|
$gre['greif'],
|
|
$gre['tunnel-local-addr'],
|
|
$gre['tunnel-remote-addr'],
|
|
gen_subnet_mask($gre['tunnel-remote-net']),
|
|
]);
|
|
}
|
|
|
|
legacy_interface_flags($gre['greif'], 'up');
|
|
|
|
/* XXX: write XXX_router file */
|
|
mwexecf('/usr/local/sbin/ifctl -i %s -%s -rd -a %s', [
|
|
$gre['greif'],
|
|
is_ipaddrv4($gre['tunnel-remote-addr']) ? '4' : '6',
|
|
$gre['tunnel-remote-addr'],
|
|
]);
|
|
|
|
return $gre['greif'];
|
|
}
|
|
|
|
function interfaces_gif_configure($device)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['gifs']['gif'])) {
|
|
return null;
|
|
}
|
|
|
|
foreach ($config['gifs']['gif'] as $gif) {
|
|
if ($gif['gifif'] == $device) {
|
|
return _interfaces_gif_configure($gif);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function _interfaces_gif_configure($gif)
|
|
{
|
|
if (!does_interface_exist($gif['gifif'])) {
|
|
/* Only create when not already there */
|
|
legacy_interface_create($gif['gifif']);
|
|
}
|
|
|
|
$interface = !empty($gif['if']) ? explode('_vip', $gif['if'])[0] : null;
|
|
$family = is_ipaddrv4($gif['remote-addr']) ? 'inet' : 'inet6';
|
|
/* XXX: remove routing part in 24.7 */
|
|
$remote_gw = null;
|
|
if (!empty($interface)) {
|
|
$remote_gw = (new \OPNsense\Routing\Gateways())->getInterfaceGateway($interface, $family);
|
|
if ($family == 'inet6' && is_linklocal($remote_gw) && strpos($remote_gw, '%') === false) {
|
|
$remote_gw .= '%' . get_real_interface($interface, 'inet6');
|
|
}
|
|
}
|
|
|
|
if ($family == 'inet') {
|
|
$local_addr = get_interface_ip(!empty($gif['ipaddr']) ? $gif['ipaddr'] : $gif['if']);
|
|
} else {
|
|
if (is_linklocal($gif['remote-addr'])) {
|
|
$local_addr = get_interface_ipv6(!empty($gif['ipaddr']) ? $gif['ipaddr'] : $gif['if'], null, 'scoped');
|
|
} else {
|
|
$local_addr = get_interface_ipv6(!empty($gif['ipaddr']) ? $gif['ipaddr'] : $gif['if'], null, 'routed');
|
|
}
|
|
}
|
|
|
|
/* ensured device is there, but do not configure unless system is ready */
|
|
if (empty($local_addr)) {
|
|
log_msg("Device {$gif['gifif']} missing required local address, skipping now.");
|
|
return null;
|
|
}
|
|
|
|
$remote_addr = $gif['remote-addr'] . (strpos($local_addr, '%') === false ? '' : '%' . explode('%', $local_addr)[1]);
|
|
|
|
mwexecf('/sbin/ifconfig %s %s tunnel %s %s', [$gif['gifif'], $family, $local_addr, $remote_addr]);
|
|
|
|
if (is_ipaddrv6($gif['tunnel-local-addr']) || is_ipaddrv6($gif['tunnel-remote-addr'])) {
|
|
/* check if destination is local to source and if not we need the traditional point-to-point setup */
|
|
if ($gif['tunnel-remote-net'] != '128' && ip_in_subnet($gif['tunnel-remote-addr'], "{$gif['tunnel-local-addr']}/{$gif['tunnel-remote-net']}")) {
|
|
mwexecf('/sbin/ifconfig %s inet6 %s prefixlen %s', [
|
|
$gif['gifif'],
|
|
$gif['tunnel-local-addr'],
|
|
$gif['tunnel-remote-net'],
|
|
]);
|
|
} else {
|
|
mwexecf('/sbin/ifconfig %s inet6 %s %s prefixlen 128', [
|
|
$gif['gifif'],
|
|
$gif['tunnel-local-addr'],
|
|
$gif['tunnel-remote-addr'],
|
|
]);
|
|
}
|
|
} else {
|
|
mwexecf('/sbin/ifconfig %s inet6 ifdisabled', [$gif['gifif']]);
|
|
mwexecf('/sbin/ifconfig %s %s %s netmask %s', [
|
|
$gif['gifif'],
|
|
$gif['tunnel-local-addr'],
|
|
$gif['tunnel-remote-addr'],
|
|
gen_subnet_mask($gif['tunnel-remote-net']),
|
|
]);
|
|
}
|
|
|
|
$flags = (empty($gif['link1']) ? "-" : "") . "link1 " . (empty($gif['link2']) ? "-" : "") . "link2";
|
|
legacy_interface_flags($gif['gifif'], $flags);
|
|
|
|
legacy_interface_flags($gif['gifif'], 'up');
|
|
|
|
/* XXX: remove below routing block in 24.7 */
|
|
if (!empty($remote_gw)) {
|
|
system_host_route($remote_addr, $remote_gw);
|
|
}
|
|
|
|
/* XXX: write XXX_router file */
|
|
mwexecf('/usr/local/sbin/ifctl -i %s -%s -rd -a %s', [
|
|
$gif['gifif'],
|
|
is_ipaddrv4($gif['tunnel-remote-addr']) ? '4' : '6',
|
|
$gif['tunnel-remote-addr'],
|
|
]);
|
|
|
|
return $gif['gifif'];
|
|
}
|
|
|
|
function interfaces_hardware($verbose = false)
|
|
{
|
|
service_log('Configuring hardware interfaces...', $verbose);
|
|
|
|
$intf_details = legacy_interfaces_details();
|
|
|
|
foreach (array_keys(get_interface_list()) as $device) {
|
|
configure_interface_hardware($device, $intf_details);
|
|
}
|
|
|
|
service_log("done.\n", $verbose);
|
|
}
|
|
|
|
function interfaces_configure($verbose = false)
|
|
{
|
|
$interfaces = [];
|
|
$devices = [];
|
|
|
|
foreach (plugins_devices() as $device) {
|
|
if (empty($device['function']) || empty($device['names'])) {
|
|
continue;
|
|
}
|
|
foreach (array_keys($device['names']) as $name) {
|
|
$devices[$name] = $device['function'];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Queues are set up to order interfaces according to their
|
|
* dependencies / requirements of devices or other interfaces.
|
|
* Some queues may overlap, but they are laid out in full to
|
|
* make sure that the configuration flow is as clean as possible.
|
|
* See individual notes in the queued handling below.
|
|
*/
|
|
|
|
$hardware = []; /* hardware devices */
|
|
$virtual = []; /* software devices */
|
|
$track6 = []; /* trackers without bridges */
|
|
$bridge = []; /* bridges that may be trackers, but not dhcp6c interfaces */
|
|
$dhcp6c = []; /* dhcp6c interfaces load last */
|
|
|
|
foreach (legacy_config_get_interfaces(['enable' => true, 'virtual' => false]) as $if => $ifcfg) {
|
|
/* XXX use this to figure out when bridges can be configured */
|
|
$interfaces[$if] = $ifcfg['if'];
|
|
|
|
if (!empty($devices[$ifcfg['if']]) && !strstr($ifcfg['if'], 'bridge')) {
|
|
$virtual[$ifcfg['if']] = $if;
|
|
continue;
|
|
}
|
|
|
|
$is_track6 = !empty($ifcfg['ipaddrv6']) && $ifcfg['ipaddrv6'] == 'track6';
|
|
if (!strstr($ifcfg['if'], 'bridge') && $is_track6) {
|
|
$track6[$ifcfg['if']] = $if;
|
|
continue;
|
|
}
|
|
|
|
$is_dhcp6c = !empty($ifcfg['ipaddrv6']) && ($ifcfg['ipaddrv6'] == 'dhcp6' || $ifcfg['ipaddrv6'] == 'slaac');
|
|
if (strstr($ifcfg['if'], 'bridge') && !$is_dhcp6c) {
|
|
$bridge[$ifcfg['if']] = $if;
|
|
continue;
|
|
} elseif ($is_dhcp6c) {
|
|
$dhcp6c[$ifcfg['if']] = $if;
|
|
continue;
|
|
}
|
|
|
|
$hardware[$ifcfg['if']] = $if;
|
|
}
|
|
|
|
interfaces_loopback_configure($verbose);
|
|
interfaces_lagg_configure($verbose);
|
|
interfaces_vlan_configure($verbose);
|
|
|
|
/* run through priority lists */
|
|
foreach ([$hardware, $virtual, $track6, $bridge, $dhcp6c] as $list) {
|
|
foreach ($list as $device => $if) {
|
|
/* pre-op: configuring the underlying device */
|
|
if (!empty($devices[$device])) {
|
|
/* XXX devices could depend on other devices */
|
|
log_msg("Device $device required for $if, configuring now");
|
|
call_user_func_array($devices[$device], [$device]);
|
|
unset($devices[$device]);
|
|
}
|
|
|
|
/* post-op: removing associated devices and current interface */
|
|
foreach (interface_configure($verbose, $if) as $loaded) {
|
|
if (!empty($devices[$loaded])) {
|
|
log_msg("Device $loaded loaded by $if, skipping now", LOG_INFO);
|
|
unset($devices[$loaded]);
|
|
} else {
|
|
log_msg("Device $loaded reloaded by $if, already skipped", LOG_INFO);
|
|
}
|
|
}
|
|
unset($interfaces[$if]);
|
|
}
|
|
}
|
|
|
|
/* last but not least start all unconfigured devices */
|
|
foreach ($devices as $name => $function) {
|
|
/* XXX devices could depend on other devices */
|
|
log_msg("Device $name is not assigned, configuring late");
|
|
call_user_func_array($function, [$name]);
|
|
}
|
|
}
|
|
|
|
function interface_vip_bring_down($vip)
|
|
{
|
|
$vipif = get_real_interface($vip['interface']);
|
|
switch ($vip['mode']) {
|
|
case 'proxyarp':
|
|
killbypid("/var/run/choparp_{$vipif}.pid");
|
|
break;
|
|
case 'ipalias':
|
|
case 'carp':
|
|
if (does_interface_exist($vipif)) {
|
|
legacy_interface_deladdress($vipif, $vip['subnet'], is_ipaddrv6($vip['subnet']) ? 6 : 4);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
function interface_reset($interface, $ifacecfg = false, $suspend = false)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['interfaces'][$interface]) || ($ifacecfg !== false && !is_array($ifacecfg))) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The function formerly known as interface_bring_down() is largely intact,
|
|
* but its purpose was split between different use cases that it could not
|
|
* handle correctly not being aware of the caller's requirements. Now we
|
|
* split between a suspend and reset mode in order to accomodate the need.
|
|
*/
|
|
if ($ifacecfg === false) {
|
|
$realif = get_real_interface($interface);
|
|
$realifv6 = get_real_interface($interface, "inet6");
|
|
$ifcfg = $config['interfaces'][$interface];
|
|
$ppps = isset($config['ppps']['ppp']) ? $config['ppps']['ppp'] : [];
|
|
} else {
|
|
$ifcfg = $ifacecfg['ifcfg'];
|
|
$ppps = $ifacecfg['ppps'];
|
|
/* When $ifacecfg is passed, it should contain the original interfaces */
|
|
$realif = $ifacecfg['ifcfg']['realif'];
|
|
$realifv6 = $ifacecfg['ifcfg']['realifv6'];
|
|
}
|
|
|
|
if (!$suspend) {
|
|
foreach (config_read_array('virtualip', 'vip') as $vip) {
|
|
if ($vip['interface'] == $interface) {
|
|
interface_vip_bring_down($vip);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cache ifconfig now that VIPs are handled */
|
|
$ifconfig_details = legacy_interfaces_details();
|
|
|
|
/*
|
|
* hostapd and wpa_supplicant do not need to be running when the
|
|
* interface is down. They will also use 100% CPU if running after
|
|
* the wireless clone gets deleted.
|
|
*/
|
|
if (isset($ifcfg['wireless'])) {
|
|
kill_wpasupplicant($realif);
|
|
kill_hostapd($realif);
|
|
}
|
|
|
|
$track6 = array_keys(link_interface_to_track6($interface));
|
|
if (count($track6)) {
|
|
/* bring down radvd and dhcp6 on these interfaces */
|
|
plugins_configure('dhcp', false, ['inet6', $track6]);
|
|
}
|
|
|
|
switch ($ifcfg['ipaddrv6'] ?? 'none') {
|
|
case 'slaac':
|
|
case 'dhcp6':
|
|
interface_dhcpv6_prepare($interface, $ifcfg, true);
|
|
killbypid('/var/run/dhcp6c.pid', 'HUP');
|
|
break;
|
|
case 'track6':
|
|
interface_track6_configure($interface, $ifcfg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!empty($ifcfg['ipaddrv6'])) {
|
|
if (!$suspend) {
|
|
interfaces_addresses_flush($realifv6, 6, $ifconfig_details);
|
|
} elseif (!is_ipaddrv6($ifcfg['ipaddrv6'])) {
|
|
/* edge case: bring down a primary GUA, but not a link-local */
|
|
list ($ip6) = _interfaces_primary_address6($interface, $ifconfig_details, false, false);
|
|
if (!empty($ip6)) {
|
|
mwexecf('/sbin/ifconfig %s inet6 %s delete', [$realifv6, $ip6]);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch ($ifcfg['ipaddr'] ?? 'none') {
|
|
case 'dhcp':
|
|
killbypid("/var/run/dhclient.{$realif}.pid");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!empty($ifcfg['ipaddr'])) {
|
|
if (!$suspend) {
|
|
interfaces_addresses_flush($realif, 4, $ifconfig_details);
|
|
} elseif (!is_ipaddrv4($ifcfg['ipaddr'])) {
|
|
/* dhclient will not flush its address ever so do it here */
|
|
list ($ip4) = interfaces_primary_address($interface, $ifconfig_details);
|
|
if (!empty($ip4)) {
|
|
mwexecf('/sbin/ifconfig %s delete %s', [$realif, $ip4]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check reset of running PPP configuration inside function */
|
|
interface_ppps_reset($interface, $suspend, $ifcfg, $ppps);
|
|
|
|
/* clear stale state associated with this interface */
|
|
mwexecf('/usr/local/sbin/ifctl -4c -i %s', $realif);
|
|
mwexecf('/usr/local/sbin/ifctl -6c -i %s', $realifv6);
|
|
}
|
|
|
|
function interface_suspend($interface)
|
|
{
|
|
/*
|
|
* Suspend uses a subset of interface_reset() to avoid
|
|
* stripping all the static addresses already in use.
|
|
* This helps to retain routes and gateway information
|
|
* also relevant when reloading the packet filter for
|
|
* e.g. NAT rules generation.
|
|
*/
|
|
interface_reset($interface, false, true);
|
|
}
|
|
|
|
function interface_ppps_bound($interface, $family = null)
|
|
{
|
|
global $config;
|
|
|
|
$ifcfg = $config['interfaces'][$interface] ?? null;
|
|
$ppps = $config['ppps']['ppp'] ?? null;
|
|
$bound = false;
|
|
|
|
if (!interface_ppps_capable($ifcfg, $ppps)) {
|
|
return $bound;
|
|
}
|
|
|
|
$ipv4_mode = false;
|
|
$ipv6_mode = false;
|
|
|
|
switch ($ifcfg['ipaddr'] ?? 'none') {
|
|
case 'ppp':
|
|
case 'pppoe':
|
|
case 'pptp':
|
|
case 'l2tp':
|
|
$ipv4_mode = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch ($ifcfg['ipaddrv6'] ?? 'none') {
|
|
case 'dhcp6':
|
|
case 'pppoev6':
|
|
case 'slaac':
|
|
$ipv6_mode = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ($family == 4) {
|
|
/* use this to steer configuration based on newwanip event */
|
|
$bound = $ipv4_mode;
|
|
} elseif ($family == 6) {
|
|
/* use this to steer configuration based on newwanipv6 event */
|
|
$bound = !$ipv4_mode && $ipv6_mode;
|
|
} else {
|
|
/* use this to prevent doing any early setup in general */
|
|
$bound = $ipv4_mode || $ipv6_mode;
|
|
}
|
|
|
|
return $bound;
|
|
}
|
|
|
|
function interface_ppps_capable($ifcfg, $ppps)
|
|
{
|
|
if (empty($ifcfg) || empty($ppps)) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($ppps as $ppp) {
|
|
if ($ifcfg['if'] == $ppp['if']) {
|
|
/* we only test for PPP capability that needs mpd5 */
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function interface_ppps_hardware($device)
|
|
{
|
|
$devices = [$device];
|
|
|
|
foreach (config_read_array('ppps', 'ppp') as $ppp) {
|
|
if ($device == $ppp['if']) {
|
|
$devices = [];
|
|
foreach (explode(',', $ppp['ports']) as $port) {
|
|
/*
|
|
* XXX We only have get_real_interface() here because
|
|
* PPP may be assigned to an assigned interface! :(
|
|
*/
|
|
$devices[] = get_real_interface($port);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $devices;
|
|
}
|
|
|
|
function interface_ppps_reset($interface, $suspend, $ifcfg, $ppps)
|
|
{
|
|
if (!interface_ppps_capable($ifcfg, $ppps)) {
|
|
return;
|
|
}
|
|
|
|
foreach ($ppps as $ppp) {
|
|
if ($ifcfg['if'] == $ppp['if']) {
|
|
if (isset($ppp['ondemand']) && $suspend) {
|
|
configdp_run('interface reconfigure', [$interface], true);
|
|
} else {
|
|
killbypid("/var/run/{$ppp['type']}_{$interface}.pid");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function interface_ppps_configure($interface)
|
|
{
|
|
global $config;
|
|
|
|
$ifcfg = $config['interfaces'][$interface] ?? null;
|
|
$ppps = $config['ppps']['ppp'] ?? null;
|
|
|
|
if (!interface_ppps_capable($ifcfg, $ppps)) {
|
|
return;
|
|
}
|
|
|
|
if (!isset($ifcfg['enable'])) {
|
|
return;
|
|
}
|
|
|
|
$ipv4_mode = $ipv6_mode = 'disable';
|
|
|
|
switch ($ifcfg['ipaddr'] ?? 'none') {
|
|
case 'ppp':
|
|
case 'pppoe':
|
|
case 'pptp':
|
|
case 'l2tp':
|
|
$ipv4_mode = 'enable';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch ($ifcfg['ipaddrv6'] ?? 'none') {
|
|
case 'dhcp6':
|
|
case 'pppoev6':
|
|
case 'slaac':
|
|
$ipv6_mode = 'enable';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ($ipv4_mode != 'enable' && $ipv6_mode != 'enable') {
|
|
return;
|
|
}
|
|
|
|
$ppp = null;
|
|
$idx = 0;
|
|
|
|
foreach ($ppps as $i => $tmp) {
|
|
if ($ifcfg['if'] == $tmp['if']) {
|
|
$ppp = $tmp;
|
|
$idx = $i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$ports = explode(',', $ppp['ports']);
|
|
if ($ppp['type'] != "ppp") {
|
|
foreach ($ports as $pid => $port) {
|
|
$ports[$pid] = get_real_interface($port);
|
|
if (empty($ports[$pid])) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
$localips = isset($ppp['localip']) ? explode(',', $ppp['localip']) : [];
|
|
$gateways = isset($ppp['gateway']) ? explode(',', $ppp['gateway']) : [];
|
|
$subnets = isset($ppp['subnet']) ? explode(',', $ppp['subnet']) : [];
|
|
$mtus = !empty($ppp['mtu']) ? explode(',', $ppp['mtu']) : [];
|
|
|
|
/*
|
|
* We bring up the parent interface first because if DHCP is configured on the parent we need
|
|
* to obtain an address first so we can write it in the mpd .conf file for PPTP and L2TP configs
|
|
*/
|
|
foreach ($ports as $pid => $port) {
|
|
switch ($ppp['type']) {
|
|
case 'pppoe':
|
|
legacy_interface_flags($port, 'up');
|
|
break;
|
|
case 'pptp':
|
|
case 'l2tp':
|
|
/* configure interface */
|
|
if (is_ipaddr($localips[$pid])) {
|
|
// Manually configure interface IP/subnet
|
|
legacy_interface_setaddress($port, "{$localips[$pid]}/{$subnets[$pid]}");
|
|
legacy_interface_flags($port, 'up');
|
|
} elseif (empty($localips[$pid])) {
|
|
$localips[$pid] = get_interface_ip($port); // try to get the interface IP from the port
|
|
}
|
|
|
|
if (!is_ipaddr($localips[$pid])) {
|
|
log_msg("Could not get a Local IP address for PPTP/L2TP link on {$port}. Using 0.0.0.0!", LOG_WARNING);
|
|
$localips[$pid] = '0.0.0.0';
|
|
}
|
|
if (!is_ipaddr($gateways[$pid])) {
|
|
log_msg("Could not get a Remote IP address for PPTP/L2TP link on {$port}.", LOG_WARNING);
|
|
return;
|
|
}
|
|
break;
|
|
case 'ppp':
|
|
if (!file_exists($port)) {
|
|
log_msg("Device {$port} does not exist. PPP link cannot start without the modem device.", LOG_ERR);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
log_msg("Unknown {$ppp['type']} configured as PPP interface.", LOG_ERR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Construct the mpd.conf file
|
|
$mpdconf = <<<EOD
|
|
startup:
|
|
# configure the console
|
|
set console close
|
|
# configure the web server
|
|
set web close
|
|
|
|
default:
|
|
{$ppp['type']}client:
|
|
create bundle static {$interface}
|
|
set bundle {$ipv4_mode} ipcp
|
|
set bundle {$ipv6_mode} ipv6cp
|
|
set iface name {$ifcfg['if']}
|
|
|
|
EOD;
|
|
|
|
if (isset($ppp['ondemand'])) {
|
|
$mpdconf .= " set iface enable on-demand\n";
|
|
} else {
|
|
$mpdconf .= " set iface disable on-demand\n";
|
|
}
|
|
if (!isset($ppp['idletimeout'])) {
|
|
$mpdconf .= " set iface idle 0\n";
|
|
} else {
|
|
$mpdconf .= " set iface idle {$ppp['idletimeout']}\n";
|
|
}
|
|
|
|
if (isset($ppp['ondemand'])) {
|
|
$mpdconf .= " set iface addrs 10.10.1.1 10.10.1.2\n";
|
|
}
|
|
|
|
if (isset($ppp['tcpmssfix'])) {
|
|
$mpdconf .= " set iface disable tcpmssfix\n";
|
|
} else {
|
|
$mpdconf .= " set iface enable tcpmssfix\n";
|
|
}
|
|
|
|
$mpdconf .= " set iface up-script /usr/local/opnsense/scripts/interfaces/ppp-linkup.sh\n";
|
|
$mpdconf .= " set iface down-script /usr/local/opnsense/scripts/interfaces/ppp-linkdown.sh\n";
|
|
|
|
if ($ipv4_mode == 'enable') {
|
|
if ($ppp['type'] == 'ppp') {
|
|
$localip = is_ipaddr($ppp['localip']) ? $ppp['localip'] : '0.0.0.0';
|
|
$gateway = is_ipaddr($ppp['gateway']) ? $ppp['gateway'] : "10.64.64.{$idx}";
|
|
$mpdconf .= " set ipcp ranges {$localip}/0 {$gateway}/0\n";
|
|
} else {
|
|
$mpdconf .= " set ipcp ranges 0.0.0.0/0 0.0.0.0/0\n";
|
|
}
|
|
|
|
if (isset($ppp['vjcomp'])) {
|
|
$mpdconf .= " set ipcp no vjcomp\n";
|
|
}
|
|
|
|
if (isset($config['system']['dnsallowoverride'])) {
|
|
$mpdconf .= " set ipcp enable req-pri-dns\n";
|
|
$mpdconf .= " set ipcp enable req-sec-dns\n";
|
|
}
|
|
}
|
|
|
|
foreach ($ports as $pid => $port) {
|
|
$mpdconf_arr = [];
|
|
$port = get_real_interface($port);
|
|
if ($ppp['type'] == "ppp") {
|
|
$mpdconf_arr[] = "create link static {$interface}_link{$pid} modem";
|
|
} else {
|
|
$mpdconf_arr[] = "create link static {$interface}_link{$pid} {$ppp['type']}";
|
|
}
|
|
$mpdconf_arr[] = "set link action bundle {$interface}";
|
|
if (count($ports) > 1) {
|
|
$mpdconf_arr[] = "set link enable multilink";
|
|
} else {
|
|
$mpdconf_arr[] = "set link disable multilink";
|
|
}
|
|
$mpdconf_arr[] = "set link keep-alive 10 60";
|
|
$mpdconf_arr[] = "set link max-redial 0";
|
|
if (isset($ppp['shortseq'])) {
|
|
$mpdconf_arr[] = "set link no shortseq";
|
|
}
|
|
if (isset($ppp['acfcomp'])) {
|
|
$mpdconf_arr[] = "set link no acfcomp";
|
|
}
|
|
if (isset($ppp['protocomp'])) {
|
|
$mpdconf_arr[] = "set link no protocomp";
|
|
}
|
|
$mpdconf_arr[] = "set link disable chap pap";
|
|
$mpdconf_arr[] = "set link accept chap pap eap";
|
|
$mpdconf_arr[] = "set link disable incoming";
|
|
$bandwidths = !empty($ppp['bandwidth']) ? explode(',', $ppp['bandwidth']) : null;
|
|
if (!empty($bandwidths[$pid])) {
|
|
$mpdconf_arr[] = "set link bandwidth {$bandwidths[$pid]}";
|
|
}
|
|
|
|
if (empty($mtus[$pid])) {
|
|
/* subtract default header when deriving from interface config (as shown there) */
|
|
$mtus[$pid] = !empty($ifcfg['mtu']) ? intval($ifcfg['mtu']) - 8 : 1492;
|
|
}
|
|
if ($ppp['type'] == 'pppoe' && $mtus[$pid] > 1492) {
|
|
$mpdconf_arr[] = "set pppoe max-payload " . $mtus[$pid];
|
|
} else {
|
|
$mpdconf_arr[] = "set link mtu " . $mtus[$pid];
|
|
}
|
|
$mrus = !empty($ppp['mru']) ? explode(',', $ppp['mru']) : null;
|
|
if (!empty($mrus[$pid])) {
|
|
$mpdconf_arr[] = "set link mru {$mrus[$pid]}";
|
|
}
|
|
$mrrus = !empty($ppp['mrru']) ? explode(',', $ppp['mrru']) : null;
|
|
if (!empty($mrrus[$pid])) {
|
|
$mpdconf_arr[] = "set link mrru {$mrrus[$pid]}";
|
|
}
|
|
|
|
if (empty($ppp['username']) && $ppp['type'] == "ppp") {
|
|
$mpdconf_arr[] = "set auth authname \"user\"";
|
|
} else {
|
|
$mpdconf_arr[] = "set auth authname \"{$ppp['username']}\"";
|
|
}
|
|
if (empty($ppp['password']) && $ppp['type'] == "ppp") {
|
|
$mpdconf_arr[] = "set auth password " . base64_decode('none');
|
|
} else {
|
|
$mpdconf_arr[] = "set auth password " . base64_decode($ppp['password']);
|
|
}
|
|
|
|
if ($ppp['type'] == "ppp") {
|
|
// ppp, modem connections
|
|
$mpdconf_arr[] = "set modem device {$ppp['ports']}";
|
|
$mpdconf_arr[] = "set modem script DialPeer";
|
|
$mpdconf_arr[] = "set modem idle-script Ringback";
|
|
$mpdconf_arr[] = "set modem watch -cd";
|
|
$mpdconf_arr[] = "set modem var \$DialPrefix \"DT\"";
|
|
$mpdconf_arr[] = "set modem var \$Telephone \"{$ppp['phone']}\"";
|
|
if (isset($ppp['connect-timeout'])) {
|
|
$mpdconf_arr[] = "set modem var \$ConnectTimeout \"{$ppp['connect-timeout']}\"";
|
|
}
|
|
if (isset($ppp['initstr'])) {
|
|
$initstr = base64_decode($ppp['initstr']);
|
|
$mpdconf_arr[] = "set modem var \$InitString \"{$initstr}\"";
|
|
}
|
|
if (isset($ppp['simpin'])) {
|
|
$mpdconf_arr[] = "set modem var \$SimPin \"{$ppp['simpin']}\"";
|
|
if (!empty($ppp['pin-wait'])) {
|
|
$mpdconf_arr[] = "set modem var \$PinWait \"{$ppp['pin-wait']}\"";
|
|
} else {
|
|
$mpdconf_arr[] = "set modem var \$PinWait \"0\"";
|
|
}
|
|
}
|
|
if (isset($ppp['apn'])) {
|
|
$mpdconf_arr[] = "set modem var \$APN \"{$ppp['apn']}\"";
|
|
if (empty($ppp['apnum'])) {
|
|
$mpdconf_arr[] = "set modem var \$APNum \"1\"";
|
|
} else {
|
|
$mpdconf_arr[] = "set modem var \$APNum \"{$ppp['apnum']}\"";
|
|
}
|
|
}
|
|
} elseif ($ppp['type'] == "pppoe") {
|
|
$provider = $ppp['provider'] ?? '';
|
|
$hostuniq = '';
|
|
if (!empty($ppp['hostuniq'])) {
|
|
$hostuniq = '0x' . strtolower(array_shift(unpack('H*', $ppp['hostuniq']))) . '|';
|
|
}
|
|
$mpdconf_arr[] = "set pppoe service \"{$hostuniq}{$provider}\"";
|
|
$mpdconf_arr[] = "set pppoe iface {$port}";
|
|
} elseif ($ppp['type'] == "pptp" || $ppp['type'] == "l2tp") {
|
|
$mpdconf_arr[] = "set {$ppp['type']} self {$localips[$pid]}";
|
|
$mpdconf_arr[] = "set {$ppp['type']} peer {$gateways[$pid]}";
|
|
}
|
|
|
|
foreach ($mpdconf_arr as $mpdconf_opt) {
|
|
$mpdconf .= " " . $mpdconf_opt . "\n";
|
|
}
|
|
|
|
$mpdconf .= "\topen\n";
|
|
}
|
|
|
|
/* stop the service as a precaution */
|
|
killbypid("/var/run/{$ppp['type']}_{$interface}.pid");
|
|
|
|
/* mpd5 modem chat script expected in the same directory as the mpd_xxx.conf files */
|
|
@copy('/usr/local/opnsense/scripts/interfaces/mpd.script', '/var/etc/mpd.script');
|
|
|
|
/* write the configuration */
|
|
@file_put_contents("/var/etc/mpd_{$interface}.conf", $mpdconf);
|
|
|
|
/* create the uptime log if requested */
|
|
if (isset($ppp['uptime'])) {
|
|
@touch("/conf/{$ifcfg['if']}.log");
|
|
} else {
|
|
@unlink("/conf/{$ifcfg['if']}.log");
|
|
}
|
|
|
|
/* clean up old lock files */
|
|
foreach ($ports as $port) {
|
|
@unlink('/var/spool/lock/LCK..' . basename($port));
|
|
}
|
|
|
|
/* precaution for post-start 'up' check */
|
|
legacy_interface_flags($ifcfg['if'], 'down', false);
|
|
|
|
/* fire up mpd */
|
|
mwexecf(
|
|
'/usr/local/sbin/mpd5 -b -d /var/etc -f %s -p %s -s ppp %s',
|
|
["mpd_{$interface}.conf", "/var/run/{$ppp['type']}_{$interface}.pid", "{$ppp['type']}client"]
|
|
);
|
|
|
|
/* appease netgraph by setting the correct node name */
|
|
shell_safe('/usr/sbin/daemon -f /usr/local/opnsense/scripts/interfaces/ppp-rename.sh %s %s %s', [$interface, $ifcfg['if'], $ppp['type']]);
|
|
|
|
/* wait for functional device */
|
|
$max = 20;
|
|
$i = 0;
|
|
while ($i < $max) {
|
|
sleep(1);
|
|
if (does_interface_exist($ifcfg['if'], 'up')) {
|
|
break;
|
|
}
|
|
$i++;
|
|
}
|
|
|
|
if ($i >= $max) {
|
|
log_msg("interface_ppps_configure() waiting threshold exceeded - device {$ifcfg['if']} is still not up", LOG_WARNING);
|
|
}
|
|
|
|
switch ($ppp['type']) {
|
|
case 'pppoe':
|
|
/* automatically change MAC address if parent interface changes */
|
|
$ng_name = preg_replace('/[.:]/', '_', $ports[0]) . ':';
|
|
mwexecf('/usr/sbin/ngctl msg %s setautosrc 1', [$ng_name]);
|
|
/* FALLTHROUGH */
|
|
default:
|
|
legacy_interface_mtu($ifcfg['if'], $mtus[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function interfaces_pfsync_configure()
|
|
{
|
|
global $config;
|
|
|
|
if (!empty($config['hasync']['pfsyncinterface'])) {
|
|
/*
|
|
* We are just checking the actual attached interface here as get_real_interface()
|
|
* was not dependable when the selected interface does not exist for any reason.
|
|
*
|
|
* What the current method tells us is that we are going to ignore whether this
|
|
* interface is currently enabled or not. To avoid breakage we will keep it so
|
|
* although in reality disabling your pfsync interface should cause it to stop
|
|
* syncing.
|
|
*/
|
|
if (!empty($config['interfaces'][$config['hasync']['pfsyncinterface']]['if'])) {
|
|
$syncdev = $config['interfaces'][$config['hasync']['pfsyncinterface']]['if'];
|
|
}
|
|
}
|
|
|
|
if (!empty($syncdev)) {
|
|
if (!empty($config['hasync']['pfsyncpeerip']) && is_ipaddrv4($config['hasync']['pfsyncpeerip'])) {
|
|
$syncpeer = "syncpeer " . escapeshellarg($config['hasync']['pfsyncpeerip']);
|
|
} else {
|
|
$syncpeer = "-syncpeer";
|
|
}
|
|
|
|
$version = '';
|
|
if (!empty($config['hasync']['pfsyncversion'])) {
|
|
$version = 'version ' . escapeshellarg($config['hasync']['pfsyncversion']);
|
|
}
|
|
|
|
$intf_stats = legacy_interfaces_details(); /* XXX could require passing this down */
|
|
|
|
mwexec("/sbin/ifconfig pfsync0 syncdev {$syncdev} {$syncpeer} {$version} up");
|
|
|
|
if (!empty($intf_stats[$syncdev]['mtu'])) {
|
|
mwexecf('/sbin/ifconfig pfsync0 mtu %s', [$intf_stats[$syncdev]['mtu']]);
|
|
}
|
|
} else {
|
|
mwexec('/sbin/ifconfig pfsync0 -syncdev -syncpeer down');
|
|
}
|
|
}
|
|
|
|
function interface_proxyarp_configure($interface = '')
|
|
{
|
|
global $config;
|
|
|
|
/* kill any running choparp, on restart "all" */
|
|
if (empty($interface)) {
|
|
foreach (glob('/var/run/choparp_*.pid') as $pidfile) {
|
|
killbypid($pidfile);
|
|
}
|
|
}
|
|
|
|
$paa = array();
|
|
if (isset($config['virtualip']['vip'])) {
|
|
/* group by interface */
|
|
foreach ($config['virtualip']['vip'] as $vipent) {
|
|
if ($vipent['mode'] === "proxyarp") {
|
|
if (empty($interface) || $interface == $vipent['interface']) {
|
|
if (empty($paa[$vipent['interface']])) {
|
|
$paa[$vipent['interface']] = [];
|
|
}
|
|
$paa[$vipent['interface']][] = $vipent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($paa as $paif => $paents) {
|
|
$paaifip = get_interface_ip($paif);
|
|
if (!is_ipaddr($paaifip)) {
|
|
continue;
|
|
}
|
|
$vipif = get_real_interface($paif);
|
|
$pid_filename = "/var/run/choparp_{$vipif}.pid";
|
|
$args = "-p {$pid_filename} {$vipif} auto";
|
|
foreach ($paents as $paent) {
|
|
$args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}");
|
|
}
|
|
if (!empty($interface)) {
|
|
killbypid($pid_filename);
|
|
}
|
|
mwexec_bg("/usr/local/sbin/choparp " . $args);
|
|
}
|
|
}
|
|
|
|
function interfaces_vips_configure($interface, $family = null)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($config['virtualip']['vip'])) {
|
|
return;
|
|
}
|
|
|
|
$proxyarp = false;
|
|
$pfsync = false;
|
|
$dad = false;
|
|
|
|
foreach ($config['virtualip']['vip'] as $vip) {
|
|
if ($vip['interface'] != $interface) {
|
|
continue;
|
|
}
|
|
|
|
$inet6 = strpos($vip['subnet'], ':') !== false;
|
|
|
|
if (($family === 4 && $inet6) || ($family === 6 && !$inet6)) {
|
|
continue;
|
|
}
|
|
|
|
/* XXX trigger DAD only through rc.newwanipv6 explicit call for now */
|
|
$dad = $dad || ($inet6 && $family === 6);
|
|
|
|
switch ($vip['mode']) {
|
|
case 'proxyarp':
|
|
$proxyarp = true;
|
|
break;
|
|
case 'ipalias':
|
|
interface_ipalias_configure($vip);
|
|
break;
|
|
case 'carp':
|
|
interface_carp_configure($vip);
|
|
$pfsync = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($pfsync) {
|
|
interfaces_pfsync_configure();
|
|
}
|
|
|
|
if ($proxyarp) {
|
|
interface_proxyarp_configure();
|
|
}
|
|
|
|
if ($dad) {
|
|
waitfordad();
|
|
}
|
|
}
|
|
|
|
function interface_ipalias_configure($vip)
|
|
{
|
|
global $config;
|
|
|
|
if ($vip['mode'] != 'ipalias') {
|
|
return;
|
|
}
|
|
|
|
if ($vip['interface'] != 'lo0' && !isset($config['interfaces'][$vip['interface']]['enable'])) {
|
|
return;
|
|
}
|
|
|
|
if (is_ipaddrv6($vip['subnet'])) {
|
|
$if = get_real_interface($vip['interface'], 'inet6');
|
|
$af = 'inet6';
|
|
} else {
|
|
$if = get_real_interface($vip['interface']);
|
|
$af = 'inet';
|
|
}
|
|
|
|
$vhid = !empty($vip['vhid']) ? 'vhid ' . escapeshellarg($vip['vhid']) : '';
|
|
$gateway = !empty($vip['gateway']) ? escapeshellarg($vip['gateway']) . ' ' : '';
|
|
|
|
/* XXX use legacy_interface_setaddress */
|
|
mwexec("/sbin/ifconfig " . escapeshellarg($if) . " {$af} " . escapeshellarg($vip['subnet']) . "/" . escapeshellarg($vip['subnet_bits']) . " alias " . $gateway . $vhid);
|
|
}
|
|
|
|
function interface_carp_configure($vip)
|
|
{
|
|
if ($vip['mode'] != 'carp') {
|
|
return;
|
|
}
|
|
|
|
/* when CARP is temporary disabled do not try to configure on any interface up events */
|
|
if (get_single_sysctl('net.inet.carp.allow') == '0') {
|
|
return;
|
|
}
|
|
|
|
$realif = get_real_interface($vip['interface']);
|
|
|
|
$vip_password = escapeshellarg(addslashes(str_replace(" ", "", $vip['password'])));
|
|
$password = '';
|
|
if ($vip['password'] != "") {
|
|
$password = " pass {$vip_password}";
|
|
}
|
|
|
|
$advbase = "";
|
|
if (!empty($vip['advbase'])) {
|
|
$advbase = "advbase " . escapeshellarg($vip['advbase']);
|
|
}
|
|
|
|
$advskew = "advskew " . escapeshellarg($vip['advskew']);
|
|
if (empty($vip['peer']) || $vip['peer'] == '224.0.0.18') {
|
|
$peercfg = ' mcast ';
|
|
} else {
|
|
$peercfg = ' peer ' . escapeshellarg($vip['peer']) . ' ';
|
|
}
|
|
if (empty($vip['peer6']) || $vip['peer6'] == 'ff02::12') {
|
|
$peercfg .= ' mcast6 ';
|
|
} else {
|
|
$peercfg .= ' peer6 ' . escapeshellarg($vip['peer6']) . ' ';
|
|
}
|
|
|
|
mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) . " {$advskew} {$advbase} {$password}");
|
|
|
|
/* XXX use legacy_interface_setaddress */
|
|
if (is_ipaddrv4($vip['subnet'])) {
|
|
mwexec("/sbin/ifconfig {$realif} " . escapeshellarg($vip['subnet']) . "/" . escapeshellarg($vip['subnet_bits']) . " alias vhid " . escapeshellarg($vip['vhid']));
|
|
} elseif (is_ipaddrv6($vip['subnet'])) {
|
|
mwexec("/sbin/ifconfig {$realif} inet6 " . escapeshellarg($vip['subnet']) . " prefixlen " . escapeshellarg($vip['subnet_bits']) . " alias vhid " . escapeshellarg($vip['vhid']));
|
|
}
|
|
/**
|
|
* XXX this is pretty flaky.
|
|
* If we configure peer[6] during setup, values won't stick, they appear to be flushed when
|
|
* the initial address is set.
|
|
*/
|
|
mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) . " {$peercfg}");
|
|
}
|
|
|
|
function _interfaces_wlan_clone($realif, $wlcfg)
|
|
{
|
|
/*
|
|
* Check to see if interface has been cloned as of yet.
|
|
* If it has not been cloned then go ahead and clone it.
|
|
*/
|
|
$needs_clone = false;
|
|
|
|
if (!empty($wlcfg['wireless']['mode'])) {
|
|
/* XXX this is interfaces wireless config */
|
|
$wlcfg_mode = $wlcfg['wireless']['mode'];
|
|
} else {
|
|
/* XXX this is wireless clone config or fallback */
|
|
$wlcfg_mode = $wlcfg['mode'] ?? 'bss';
|
|
}
|
|
|
|
switch ($wlcfg_mode) {
|
|
case "hostap":
|
|
$mode = 'wlanmode hostap';
|
|
break;
|
|
case "adhoc":
|
|
$mode = 'wlanmode adhoc';
|
|
break;
|
|
default:
|
|
$mode = '';
|
|
break;
|
|
}
|
|
|
|
if (does_interface_exist($realif)) {
|
|
exec("/sbin/ifconfig " . escapeshellarg($realif), $output, $ret);
|
|
$ifconfig_str = implode(PHP_EOL, $output);
|
|
if (($wlcfg_mode == 'hostap') && !preg_match('/hostap/si', $ifconfig_str)) {
|
|
log_msg("Interface {$realif} changed to hostap mode");
|
|
$needs_clone = true;
|
|
}
|
|
if (($wlcfg_mode == 'adhoc') && !preg_match('/adhoc/si', $ifconfig_str)) {
|
|
log_msg("Interface {$realif} changed to adhoc mode");
|
|
$needs_clone = true;
|
|
}
|
|
if (($wlcfg_mode == 'bss') && preg_match('/hostap|adhoc/si', $ifconfig_str)) {
|
|
log_msg("Interface {$realif} changed to infrastructure mode");
|
|
$needs_clone = true;
|
|
}
|
|
} else {
|
|
$needs_clone = true;
|
|
}
|
|
|
|
if ($needs_clone) {
|
|
$baseif = interface_get_wireless_base($wlcfg['if']);
|
|
|
|
legacy_interface_destroy($realif);
|
|
|
|
exec("/sbin/ifconfig wlan create wlandev {$baseif} {$mode} bssid name {$realif} 2>&1", $out, $ret);
|
|
if ($ret != 0) {
|
|
log_msg("Failed to clone interface {$baseif} with error code {$ret}, output {$out[0]}", LOG_ERR);
|
|
return null;
|
|
}
|
|
|
|
file_put_contents("/tmp/{$realif}_oldmac", get_interface_mac($realif));
|
|
}
|
|
|
|
return $realif;
|
|
}
|
|
|
|
function interface_sync_wireless_clones(&$ifcfg, $sync_changes = false) /* XXX kill side effect */
|
|
{
|
|
global $config;
|
|
|
|
$shared_settings = array(
|
|
'channel',
|
|
'diversity',
|
|
'protmode',
|
|
'regcountry',
|
|
'regdomain',
|
|
'reglocation',
|
|
'rxantenna',
|
|
'standard',
|
|
'turbo',
|
|
'txantenna',
|
|
'txpower',
|
|
);
|
|
|
|
$baseif = interface_get_wireless_base($ifcfg['if']);
|
|
|
|
foreach (array_keys(legacy_config_get_interfaces(['virtual' => false])) as $if) {
|
|
if ($baseif == interface_get_wireless_base($config['interfaces'][$if]['if']) && $ifcfg['if'] != $config['interfaces'][$if]['if']) {
|
|
if (isset($config['interfaces'][$if]['wireless']['standard']) || $sync_changes) {
|
|
foreach ($shared_settings as $setting) {
|
|
if ($sync_changes) {
|
|
if (isset($ifcfg['wireless'][$setting])) {
|
|
$config['interfaces'][$if]['wireless'][$setting] = $ifcfg['wireless'][$setting];
|
|
} elseif (isset($config['interfaces'][$if]['wireless'][$setting])) {
|
|
unset($config['interfaces'][$if]['wireless'][$setting]);
|
|
}
|
|
} else {
|
|
if (isset($config['interfaces'][$if]['wireless'][$setting])) {
|
|
$ifcfg['wireless'][$setting] = $config['interfaces'][$if]['wireless'][$setting];
|
|
} elseif (isset($ifcfg['wireless'][$setting])) {
|
|
unset($ifcfg['wireless'][$setting]);
|
|
}
|
|
}
|
|
}
|
|
if (!$sync_changes) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read or write settings at shared area
|
|
if (!empty($config['wireless']['interfaces'][$baseif])) {
|
|
foreach ($shared_settings as $setting) {
|
|
if ($sync_changes) {
|
|
if (isset($ifcfg['wireless'][$setting])) {
|
|
$config['wireless']['interfaces'][$baseif][$setting] = $ifcfg['wireless'][$setting];
|
|
} elseif (isset($config['wireless']['interfaces'][$baseif][$setting])) {
|
|
unset($config['wireless']['interfaces'][$baseif][$setting]);
|
|
}
|
|
} elseif (isset($config['wireless']['interfaces'][$baseif][$setting])) {
|
|
if (isset($config['wireless']['interfaces'][$baseif][$setting])) {
|
|
$ifcfg['wireless'][$setting] = $config['wireless']['interfaces'][$baseif][$setting];
|
|
} elseif (isset($ifcfg['wireless'][$setting])) {
|
|
unset($ifcfg['wireless'][$setting]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sync the mode on the clone creation page with the configured mode on the interface
|
|
if (strstr($ifcfg['if'], '_wlan') && !empty($config['wireless']['clone'])) {
|
|
foreach ($config['wireless']['clone'] as &$clone) {
|
|
if ($clone['cloneif'] == $ifcfg['if']) {
|
|
if ($sync_changes) {
|
|
$clone['mode'] = $ifcfg['wireless']['mode'];
|
|
} else {
|
|
$ifcfg['wireless']['mode'] = $clone['mode'];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
unset($clone);
|
|
}
|
|
}
|
|
|
|
function interface_wireless_configure($if, &$wancfg)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($wancfg['wireless'])) {
|
|
return;
|
|
}
|
|
|
|
if (empty($wancfg['wireless'])) {
|
|
/* if an empty node reset to empty array to avoid PHP 8+ issues */
|
|
$wancfg['wireless'] = [];
|
|
}
|
|
|
|
/* XXX _interfaces_wlan_clone() and interface_sync_wireless_clones() need work */
|
|
|
|
// Clone wireless nic if needed.
|
|
_interfaces_wlan_clone($if, $wancfg);
|
|
|
|
// Reject inadvertent changes to shared settings in case the interface hasn't been configured.
|
|
interface_sync_wireless_clones($wancfg, false);
|
|
|
|
$wlcfg = &$wancfg['wireless'];
|
|
|
|
/*
|
|
* Open up a shell script that will be used to output the commands.
|
|
* since wireless is changing a lot, these series of commands are fragile
|
|
* and will sometimes need to be verified by a operator by executing the command
|
|
* and returning the output of the command to the developers for inspection. Please
|
|
* do not change this routine from a shell script to individual exec commands. -sullrich
|
|
*/
|
|
|
|
$fd_set = fopen("/tmp/{$if}_setup.sh", "w");
|
|
fwrite($fd_set, "#!/bin/sh\n");
|
|
fwrite($fd_set, "# wireless configuration script.\n\n");
|
|
|
|
/* set values for /path/program */
|
|
$wpa_supplicant = '/usr/local/sbin/wpa_supplicant';
|
|
$hostapd = '/usr/local/sbin/hostapd';
|
|
$ifconfig = '/sbin/ifconfig';
|
|
$sysctl = '/sbin/sysctl';
|
|
|
|
/* Set all wireless ifconfig variables (split up to get rid of needed checking) */
|
|
|
|
$wlcmd = [];
|
|
$wl_sysctl = [];
|
|
|
|
/* Make sure it's up */
|
|
$wlcmd[] = "up";
|
|
|
|
if (isset($wlcfg['standard'])) {
|
|
/* Set a/b/g standard */
|
|
$standard = str_replace(" Turbo", "", $wlcfg['standard']);
|
|
$wlcmd[] = "mode " . escapeshellarg($standard);
|
|
|
|
/*
|
|
* XXX: Disable ampdu for now on mwl when running in 11n mode
|
|
* to prevent massive packet loss under certain conditions.
|
|
*/
|
|
if (preg_match("/^mwl/i", $if) && ($standard == "11ng" || $standard == "11na")) {
|
|
$wlcmd[] = "-ampdu";
|
|
}
|
|
}
|
|
|
|
if (isset($wlcfg['ssid'])) {
|
|
/* Set ssid */
|
|
$wlcmd[] = "ssid " . escapeshellarg($wlcfg['ssid']);
|
|
}
|
|
|
|
if (isset($wlcfg['protmode'])) {
|
|
/* Set 802.11g protection mode */
|
|
$wlcmd[] = "protmode " . escapeshellarg($wlcfg['protmode']);
|
|
}
|
|
|
|
/* set wireless channel value */
|
|
if (isset($wlcfg['channel'])) {
|
|
if ($wlcfg['channel'] == "0") {
|
|
$wlcmd[] = "channel any";
|
|
} else {
|
|
$wlcmd[] = "channel " . escapeshellarg($wlcfg['channel']);
|
|
}
|
|
}
|
|
|
|
/* Set antenna diversity value */
|
|
if (isset($wlcfg['diversity'])) {
|
|
$wl_sysctl[] = "diversity=" . escapeshellarg($wlcfg['diversity']);
|
|
}
|
|
|
|
/* Set txantenna value */
|
|
if (isset($wlcfg['txantenna'])) {
|
|
$wl_sysctl[] = "txantenna=" . escapeshellarg($wlcfg['txantenna']);
|
|
}
|
|
|
|
/* Set rxantenna value */
|
|
if (isset($wlcfg['rxantenna'])) {
|
|
$wl_sysctl[] = "rxantenna=" . escapeshellarg($wlcfg['rxantenna']);
|
|
}
|
|
|
|
/* Set wireless hostap mode */
|
|
if (isset($wlcfg['mode']) && $wlcfg['mode'] == 'hostap') {
|
|
$wlcmd[] = "mediaopt hostap";
|
|
} else {
|
|
$wlcmd[] = "-mediaopt hostap";
|
|
}
|
|
|
|
/* Set wireless adhoc mode */
|
|
if (isset($wlcfg['mode']) && $wlcfg['mode'] == 'adhoc') {
|
|
$wlcmd[] = "mediaopt adhoc";
|
|
} else {
|
|
$wlcmd[] = "-mediaopt adhoc";
|
|
}
|
|
|
|
/* Not necessary to set BSS mode as this is default if adhoc and/or hostap is NOT set */
|
|
|
|
/* handle hide ssid option */
|
|
if (isset($wlcfg['hidessid']['enable'])) {
|
|
$wlcmd[] = "hidessid";
|
|
} else {
|
|
$wlcmd[] = "-hidessid";
|
|
}
|
|
|
|
/* handle pureg (802.11g) only option */
|
|
if (isset($wlcfg['pureg']['enable'])) {
|
|
$wlcmd[] = "mode 11g pureg";
|
|
} else {
|
|
$wlcmd[] = "-pureg";
|
|
}
|
|
|
|
/* handle puren (802.11n) only option */
|
|
if (isset($wlcfg['puren']['enable'])) {
|
|
$wlcmd[] = "puren";
|
|
} else {
|
|
$wlcmd[] = "-puren";
|
|
}
|
|
|
|
/* enable apbridge option */
|
|
if (isset($wlcfg['apbridge']['enable'])) {
|
|
$wlcmd[] = "apbridge";
|
|
} else {
|
|
$wlcmd[] = "-apbridge";
|
|
}
|
|
|
|
/* handle turbo option */
|
|
if (isset($wlcfg['turbo']['enable'])) {
|
|
$wlcmd[] = "mediaopt turbo";
|
|
} else {
|
|
$wlcmd[] = "-mediaopt turbo";
|
|
}
|
|
|
|
/* handle wme option */
|
|
if (isset($wlcfg['wme']['enable'])) {
|
|
$wlcmd[] = "wme";
|
|
} else {
|
|
$wlcmd[] = "-wme";
|
|
}
|
|
|
|
/* set up wep if enabled */
|
|
if (isset($wlcfg['wep']['enable']) && is_array($wlcfg['wep']['key'])) {
|
|
$wepset = '';
|
|
switch ($wlcfg['wpa']['auth_algs']) {
|
|
case "1":
|
|
$wepset .= "authmode open wepmode on ";
|
|
break;
|
|
case "2":
|
|
$wepset .= "authmode shared wepmode on ";
|
|
break;
|
|
case "3":
|
|
$wepset .= "authmode mixed wepmode on ";
|
|
}
|
|
$i = 1;
|
|
foreach ($wlcfg['wep']['key'] as $wepkey) {
|
|
$wepset .= "wepkey " . escapeshellarg("{$i}:{$wepkey['value']}") . " ";
|
|
if (isset($wepkey['txkey'])) {
|
|
$wlcmd[] = "weptxkey {$i} ";
|
|
}
|
|
$i++;
|
|
}
|
|
$wlcmd[] = $wepset;
|
|
} else {
|
|
$wlcmd[] = 'authmode open wepmode off';
|
|
}
|
|
|
|
kill_wpasupplicant($if);
|
|
kill_hostapd($if);
|
|
|
|
/* generate wpa_supplicant/hostap config if wpa is enabled */
|
|
switch ($wlcfg['mode'] ?? 'bss') {
|
|
case 'bss':
|
|
$authentication = array();
|
|
if (isset($wlcfg['wpa']['enable'])) {
|
|
if ($wlcfg['wpa']['wpa_key_mgmt'] == 'WPA-EAP' && isset($wlcfg['wpa']['wpa_eap_method'])) {
|
|
switch ($wlcfg['wpa']['wpa_eap_method']) {
|
|
case 'PEAP':
|
|
case 'TTLS':
|
|
$authentication[] = "password=\"{$wlcfg['wpa']['passphrase']}\"";
|
|
switch ($wlcfg['wpa']['wpa_eap_p2_auth']) {
|
|
case 'MD5':
|
|
$authentication[] = "phase2=\"auth=MD5\"";
|
|
break;
|
|
case 'MSCHAPv2':
|
|
$authentication[] = "phase1=\"peaplabel=0\"";
|
|
$authentication[] = "phase2=\"auth=MSCHAPV2\"";
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
case 'TLS':
|
|
$authentication[] = "identity=\"{$wlcfg['wpa']['identity']}\"";
|
|
$authentication[] = "eap={$wlcfg['wpa']['wpa_eap_method']}";
|
|
|
|
$all_certs = [];
|
|
// Generate CA cert and client cert to file
|
|
foreach ($config['ca'] as $ca) {
|
|
if ($wlcfg['wpa']['wpa_eap_cacertref'] == $ca['refid']) {
|
|
$all_certs["/var/etc/wpa_supplicant_{$if}_ca.crt"] =
|
|
base64_decode($ca['crt']);
|
|
$authentication[] = "ca_cert=\"/var/etc/wpa_supplicant_{$if}_ca.crt\"";
|
|
}
|
|
}
|
|
foreach ($config['cert'] as $cert) {
|
|
if ($wlcfg['wpa']['wpa_eap_cltcertref'] == $cert['refid']) {
|
|
$all_certs["/var/etc/wpa_supplicant_{$if}_clt.crt"] =
|
|
base64_decode($cert['crt']);
|
|
$all_certs["/var/etc/wpa_supplicant_{$if}_clt.key"] =
|
|
base64_decode($cert['prv']);
|
|
$authentication[] = "client_cert=\"/var/etc/wpa_supplicant_{$if}_clt.crt\"";
|
|
$authentication[] = "private_key=\"/var/etc/wpa_supplicant_{$if}_clt.key\"";
|
|
}
|
|
}
|
|
foreach ($all_certs as $filename => $content) {
|
|
@touch($filename);
|
|
@chmod($filename, 0600);
|
|
@file_put_contents($filename, $content);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
$authentication[] = "psk=\"{$wlcfg['wpa']['passphrase']}\"";
|
|
}
|
|
$authentication = implode("\n", $authentication);
|
|
$wpa = <<<EOD
|
|
ctrl_interface=/var/run/wpa_supplicant
|
|
ctrl_interface_group=0
|
|
ap_scan=1
|
|
#fast_reauth=1
|
|
network={
|
|
ssid="{$wlcfg['ssid']}"
|
|
scan_ssid=1
|
|
priority=5
|
|
key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']}
|
|
$authentication
|
|
pairwise={$wlcfg['wpa']['wpa_pairwise']}
|
|
group={$wlcfg['wpa']['wpa_pairwise']}
|
|
}
|
|
|
|
EOD;
|
|
|
|
@file_put_contents("/var/etc/wpa_supplicant_{$if}.conf", $wpa);
|
|
unset($wpa);
|
|
}
|
|
break;
|
|
case 'hostap':
|
|
if (!empty($wlcfg['wpa']['passphrase'])) {
|
|
$wpa_passphrase = "wpa_passphrase={$wlcfg['wpa']['passphrase']}\n";
|
|
} else {
|
|
$wpa_passphrase = "";
|
|
}
|
|
if (isset($wlcfg['wpa']['enable'])) {
|
|
$wpa = <<<EOD
|
|
interface={$if}
|
|
driver=bsd
|
|
logger_syslog=-1
|
|
logger_syslog_level=0
|
|
logger_stdout=-1
|
|
logger_stdout_level=0
|
|
dump_file=/tmp/hostapd_{$if}.dump
|
|
ctrl_interface=/var/run/hostapd
|
|
ctrl_interface_group=wheel
|
|
#accept_mac_file=/tmp/hostapd_{$if}.accept
|
|
#deny_mac_file=/tmp/hostapd_{$if}.deny
|
|
#macaddr_acl={$wlcfg['wpa']['macaddr_acl']}
|
|
ssid={$wlcfg['ssid']}
|
|
debug={$wlcfg['wpa']['debug_mode']}
|
|
auth_algs={$wlcfg['wpa']['auth_algs']}
|
|
wpa={$wlcfg['wpa']['wpa_mode']}
|
|
wpa_key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']}
|
|
wpa_pairwise={$wlcfg['wpa']['wpa_pairwise']}
|
|
wpa_group_rekey={$wlcfg['wpa']['wpa_group_rekey']}
|
|
wpa_gmk_rekey={$wlcfg['wpa']['wpa_gmk_rekey']}
|
|
wpa_strict_rekey={$wlcfg['wpa']['wpa_strict_rekey']}
|
|
{$wpa_passphrase}
|
|
|
|
EOD;
|
|
|
|
if (isset($wlcfg['wpa']['rsn_preauth'])) {
|
|
$wpa .= <<<EOD
|
|
# Enable the next lines for preauth when roaming. Interface = wired or wireless interface talking to the AP you want to roam from/to
|
|
rsn_preauth=1
|
|
rsn_preauth_interfaces={$if}
|
|
|
|
EOD;
|
|
}
|
|
if (is_array($wlcfg['wpa']['ieee8021x']) && isset($wlcfg['wpa']['ieee8021x']['enable'])) {
|
|
$wpa .= "ieee8021x=1\n";
|
|
|
|
if (!empty($wlcfg['auth_server_addr']) && !empty($wlcfg['auth_server_shared_secret'])) {
|
|
$auth_server_port = "1812";
|
|
if (!empty($wlcfg['auth_server_port']) && is_numeric($wlcfg['auth_server_port'])) {
|
|
$auth_server_port = intval($wlcfg['auth_server_port']);
|
|
}
|
|
$wpa .= <<<EOD
|
|
|
|
auth_server_addr={$wlcfg['auth_server_addr']}
|
|
auth_server_port={$auth_server_port}
|
|
auth_server_shared_secret={$wlcfg['auth_server_shared_secret']}
|
|
|
|
EOD;
|
|
if (!empty($wlcfg['auth_server_addr2']) && !empty($wlcfg['auth_server_shared_secret2'])) {
|
|
$auth_server_port2 = "1812";
|
|
if (!empty($wlcfg['auth_server_port2']) && is_numeric($wlcfg['auth_server_port2'])) {
|
|
$auth_server_port2 = intval($wlcfg['auth_server_port2']);
|
|
}
|
|
|
|
$wpa .= <<<EOD
|
|
auth_server_addr={$wlcfg['auth_server_addr2']}
|
|
auth_server_port={$auth_server_port2}
|
|
auth_server_shared_secret={$wlcfg['auth_server_shared_secret2']}
|
|
|
|
EOD;
|
|
}
|
|
}
|
|
}
|
|
|
|
@file_put_contents("/var/etc/hostapd_{$if}.conf", $wpa);
|
|
unset($wpa);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* all variables are set, lets start up everything
|
|
*/
|
|
|
|
$baseif = interface_get_wireless_base($if);
|
|
preg_match("/^(.*?)([0-9]*)$/", $baseif, $baseif_split);
|
|
$wl_sysctl_prefix = 'dev.' . $baseif_split[1] . '.' . $baseif_split[2];
|
|
|
|
/* set sysctls for the wireless interface */
|
|
if (!empty($wl_sysctl)) {
|
|
fwrite($fd_set, "# sysctls for {$baseif}\n");
|
|
foreach ($wl_sysctl as $wl_sysctl_line) {
|
|
fwrite($fd_set, "{$sysctl} {$wl_sysctl_prefix}.{$wl_sysctl_line}\n");
|
|
}
|
|
}
|
|
|
|
if (isset($wlcfg['wpa']['enable'])) {
|
|
if ($wlcfg['mode'] == "bss") {
|
|
fwrite($fd_set, "{$wpa_supplicant} -B -i {$if} -c /var/etc/wpa_supplicant_{$if}.conf\n");
|
|
}
|
|
if ($wlcfg['mode'] == "hostap") {
|
|
fwrite($fd_set, "{$hostapd} -B -P /var/run/hostapd_{$if}.pid /var/etc/hostapd_{$if}.conf\n");
|
|
}
|
|
}
|
|
|
|
fclose($fd_set);
|
|
|
|
/*
|
|
* Making sure regulatory settings have actually changed
|
|
* before applying, because changing them requires bringing
|
|
* down all wireless networks on the interface.
|
|
*/
|
|
exec("{$ifconfig} " . escapeshellarg($if), $output);
|
|
$ifconfig_str = implode(PHP_EOL, $output);
|
|
unset($output);
|
|
$reg_changing = false;
|
|
|
|
/* special case for the debug country code */
|
|
if ($wlcfg['regcountry'] == 'DEBUG' && !preg_match("/\sregdomain\s+DEBUG\s/si", $ifconfig_str)) {
|
|
$reg_changing = true;
|
|
} elseif ($wlcfg['regdomain'] && !preg_match("/\sregdomain\s+{$wlcfg['regdomain']}\s/si", $ifconfig_str)) {
|
|
$reg_changing = true;
|
|
} elseif ($wlcfg['regcountry'] && !preg_match("/\scountry\s+{$wlcfg['regcountry']}\s/si", $ifconfig_str)) {
|
|
$reg_changing = true;
|
|
} elseif ($wlcfg['reglocation'] == 'anywhere' && preg_match("/\s(indoor|outdoor)\s/si", $ifconfig_str)) {
|
|
$reg_changing = true;
|
|
} elseif ($wlcfg['reglocation'] && $wlcfg['reglocation'] != 'anywhere' && !preg_match("/\s{$wlcfg['reglocation']}\s/si", $ifconfig_str)) {
|
|
$reg_changing = true;
|
|
}
|
|
|
|
if ($reg_changing) {
|
|
/* set regulatory domain */
|
|
if ($wlcfg['regdomain']) {
|
|
$wlregcmd[] = "regdomain " . escapeshellarg($wlcfg['regdomain']);
|
|
}
|
|
|
|
/* set country */
|
|
if ($wlcfg['regcountry']) {
|
|
$wlregcmd[] = "country " . escapeshellarg($wlcfg['regcountry']);
|
|
}
|
|
|
|
/* set location */
|
|
if ($wlcfg['reglocation']) {
|
|
$wlregcmd[] = escapeshellarg($wlcfg['reglocation']);
|
|
}
|
|
|
|
$wlregcmd_args = implode(" ", $wlregcmd);
|
|
|
|
/* build a complete list of the wireless clones for this interface */
|
|
$clone_list = [];
|
|
if (does_interface_exist("{$baseif}_wlan0")) {
|
|
$clone_list[] = "{$baseif}_wlan0";
|
|
}
|
|
if (isset($config['wireless']['clone'])) {
|
|
foreach ($config['wireless']['clone'] as $clone) {
|
|
if ($clone['if'] == $baseif) {
|
|
$clone_list[] = $clone['cloneif'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* find which clones are up and bring them down */
|
|
$ifup = legacy_interface_listget('up');
|
|
$clones_up = [];
|
|
foreach ($clone_list as $clone_if) {
|
|
if (in_array($clone_if, $ifup)) {
|
|
$clones_up[] = $clone_if;
|
|
mwexec("{$ifconfig} " . escapeshellarg($clone_if) . " down");
|
|
}
|
|
}
|
|
|
|
/* apply the regulatory settings */
|
|
mwexec("{$ifconfig} " . escapeshellarg($if) . " {$wlregcmd_args}");
|
|
|
|
/* bring the clones back up that were previously up */
|
|
foreach ($clones_up as $clone_if) {
|
|
mwexec("{$ifconfig} " . escapeshellarg($clone_if) . " up");
|
|
|
|
/*
|
|
* Rerun the setup script for the interface if it isn't this interface, the interface
|
|
* is in infrastructure mode, and WPA is enabled.
|
|
* This can be removed if wpa_supplicant stops dying when you bring the interface down.
|
|
*/
|
|
if ($clone_if != $if) {
|
|
$friendly_if = convert_real_interface_to_friendly_interface_name($clone_if);
|
|
if (
|
|
!empty($friendly_if)
|
|
&& isset($config['interfaces'][$friendly_if]['wireless']['wpa']['enable'])
|
|
&& $config['interfaces'][$friendly_if]['wireless']['mode'] == 'bss'
|
|
) {
|
|
mwexec('/bin/sh /tmp/' . escapeshellarg($clone_if) . '_setup.sh');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($standard)) {
|
|
/*
|
|
* The mode must be specified in a separate command before ifconfig
|
|
* will allow the mode and channel at the same time in the next.
|
|
*
|
|
* XXX but we do not have the standard when wireless was not configured...
|
|
*/
|
|
mwexec("/sbin/ifconfig " . escapeshellarg($if) . " mode " . escapeshellarg($standard));
|
|
}
|
|
|
|
/* configure wireless */
|
|
$wlcmd_args = implode(" ", $wlcmd);
|
|
mwexec("/sbin/ifconfig " . escapeshellarg($if) . " " . $wlcmd_args, false);
|
|
unset($wlcmd_args, $wlcmd);
|
|
|
|
/* configure txpower setting (it has been known to fail so run it separately) */
|
|
if (!empty($wlcfg['txpower'])) {
|
|
mwexecf('/sbin/ifconfig %s txpower %s', array($if, $wlcfg['txpower']));
|
|
}
|
|
|
|
sleep(1);
|
|
/* execute hostapd and wpa_supplicant if required in shell */
|
|
mwexec('/bin/sh /tmp/' . escapeshellarg($if) . '_setup.sh');
|
|
|
|
return 0;
|
|
}
|
|
|
|
function kill_hostapd($interface)
|
|
{
|
|
killbypid("/var/run/hostapd_{$interface}.pid");
|
|
}
|
|
|
|
function kill_wpasupplicant($interface)
|
|
{
|
|
mwexec("/bin/pkill -f \"wpa_supplicant .*{$interface}\\.conf\"\n");
|
|
}
|
|
|
|
function interface_static_configure($interface, $wancfg)
|
|
{
|
|
if (empty($wancfg['ipaddr']) || !is_ipaddrv4($wancfg['ipaddr']) || $wancfg['subnet'] == '') {
|
|
return;
|
|
}
|
|
|
|
$realif = get_real_interface($interface);
|
|
|
|
mwexecf('/sbin/ifconfig %s inet %s/%s', array($realif, $wancfg['ipaddr'], $wancfg['subnet']));
|
|
}
|
|
|
|
function interface_static6_configure($interface, $wancfg)
|
|
{
|
|
if (empty($wancfg['ipaddrv6']) || !is_ipaddrv6($wancfg['ipaddrv6']) || $wancfg['subnetv6'] == '') {
|
|
return;
|
|
}
|
|
|
|
$realif = get_real_interface($interface, 'inet6');
|
|
|
|
mwexecf('/sbin/ifconfig %s inet6 %s prefixlen %s no_dad', [$realif, $wancfg['ipaddrv6'], $wancfg['subnetv6']]);
|
|
}
|
|
|
|
function interfaces_addresses_flush($realif, $family = 4, $ifconfig_details = null)
|
|
{
|
|
$family = $family === 6 ? 6 : 4;
|
|
|
|
foreach (array_keys(interfaces_addresses($realif, true, $ifconfig_details)) as $tmpiface) {
|
|
$tmpip = $tmpiface;
|
|
|
|
if (is_linklocal($tmpip)) {
|
|
/* never delete link-local */
|
|
continue;
|
|
} elseif (is_ipaddrv6($tmpip) || is_subnetv6($tmpip)) {
|
|
if ($family != 6) {
|
|
continue;
|
|
}
|
|
} elseif (is_subnetv4($tmpiface)) {
|
|
if ($family != 4) {
|
|
continue;
|
|
}
|
|
$tmpip = explode('/', $tmpiface)[0];
|
|
}
|
|
|
|
legacy_interface_deladdress($realif, $tmpip, $family);
|
|
}
|
|
}
|
|
|
|
function interface_configure_mtu($device, $mtu, $ifconfig_details)
|
|
{
|
|
global $config;
|
|
|
|
if (strstr($device, 'vlan') || strstr($device, 'qinq')) {
|
|
$parent_device = interface_parent_devices($device)[0];
|
|
$parent_mtu = $mtu + 4;
|
|
$force = false;
|
|
|
|
$parent_cfg = $config['interfaces'][convert_real_interface_to_friendly_interface_name($parent_device)] ?? [];
|
|
if (isset($parent_cfg['enable']) && !empty($parent_cfg['mtu'])) {
|
|
$parent_mtu = $parent_cfg['mtu'];
|
|
$force = true;
|
|
}
|
|
|
|
if ($force || $mtu > $ifconfig_details[$parent_device]['mtu']) {
|
|
/* configure parent MTU recursively to avoid a fail for current device MTU requirement */
|
|
interface_configure_mtu($parent_device, $parent_mtu, $ifconfig_details);
|
|
}
|
|
}
|
|
|
|
if ($mtu != $ifconfig_details[$device]['mtu']) {
|
|
legacy_interface_mtu($device, $mtu);
|
|
}
|
|
}
|
|
|
|
function interface_configure($verbose = false, $interface = 'wan', $reload = false, $linkup = false)
|
|
{
|
|
global $config;
|
|
|
|
$wancfg = $config['interfaces'][$interface];
|
|
$loaded = [];
|
|
|
|
if (!isset($wancfg['enable'])) {
|
|
return $loaded;
|
|
}
|
|
|
|
$wandescr = !empty($wancfg['descr']) ? $wancfg['descr'] : strtoupper($interface);
|
|
|
|
$realif = get_real_interface($interface);
|
|
$realifv6 = get_real_interface($interface, 'inet6');
|
|
$devices = plugins_devices();
|
|
|
|
/* get the correct hardware interface if PPP is involved or return the one we have */
|
|
$realhwif = interface_ppps_hardware($realif)[0]; /* XXX support all MLPPP devices */
|
|
|
|
if ($reload) {
|
|
foreach ($devices as $device) {
|
|
if (empty($device['function']) || empty($device['names'])) {
|
|
continue;
|
|
}
|
|
|
|
if (in_array($realhwif, array_keys($device['names']))) {
|
|
log_msg("Device $realhwif requires reload for $interface, configuring now");
|
|
call_user_func_array($device['function'], [$realhwif]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$ifconfig_details = legacy_interfaces_details();
|
|
if (
|
|
(strpos($realhwif, '/') === false && empty($ifconfig_details[$realhwif])) ||
|
|
(strpos($realhwif, '/') === 0 && !file_exists($realhwif))
|
|
) {
|
|
log_msg(sprintf('Unable to configure nonexistent interface %s (%s)', $interface, $realhwif), LOG_ERR);
|
|
return $loaded;
|
|
}
|
|
|
|
/* XXX mpd5 $realhwif may be a device node path */
|
|
|
|
service_log(sprintf('Configuring %s interface...', $wandescr), $verbose);
|
|
|
|
if (!empty($wancfg['ipaddr'])) {
|
|
interfaces_addresses_flush($realif, 4, $ifconfig_details);
|
|
}
|
|
|
|
if (!empty($wancfg['ipaddrv6'])) {
|
|
interfaces_addresses_flush($realifv6, 6, $ifconfig_details);
|
|
}
|
|
|
|
if (!$linkup) {
|
|
/* XXX wireless configuration: shouldn't live in interface config */
|
|
interface_wireless_configure($realif, $wancfg);
|
|
}
|
|
|
|
/* get expected and current MAC address from the configuration and system */
|
|
$mac_prev = $ifconfig_details[$realhwif]['macaddr'] ?? '';
|
|
$mac_next = !empty($wancfg['spoofmac']) ? $wancfg['spoofmac'] : ($ifconfig_details[$realhwif]['macaddr_hw'] ?? '');
|
|
|
|
/*
|
|
* Disable MAC spoofing if the device type does not support it,
|
|
* its use is prohibited or when it is otherwise reserved.
|
|
*/
|
|
foreach ($devices as $device) {
|
|
if (preg_match('/' . $device['pattern'] . '/', $realhwif)) {
|
|
if ($device['spoofmac'] == false) {
|
|
$mac_next = '';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* in case of LAGG the empty MAC may be used which we need to ignore */
|
|
if ($mac_next == '00:00:00:00:00:00') {
|
|
$mac_next = '';
|
|
}
|
|
|
|
/*
|
|
* Do not try to reapply the spoofed MAC if it's already applied.
|
|
* When ifconfig link is used, it cycles the interface down/up,
|
|
* which triggers the interface config again, which attempts to
|
|
* spoof the MAC again which cycles the link again.
|
|
*/
|
|
if (!empty($mac_prev) && !empty($mac_next) && strcasecmp($mac_prev, $mac_next)) {
|
|
/*
|
|
* When the hardware interface matches the IPv6 interface where we set
|
|
* the MAC address we can assist in providing the accompanying link-local
|
|
* address (EUI-64 embedded MAC address) by nudging the kernel to reapply
|
|
* this address due to the auto_linklocal field which we do not operate
|
|
* unless for bridges, but that one is a reversed use case.
|
|
*
|
|
* Delete all link-local addresses and send the "down" event to bring
|
|
* the kernel into the transition where it will auto-add the right
|
|
* address on the following "up" event.
|
|
*/
|
|
$mac_cmd[] = exec_safe('/sbin/ifconfig %s link %s', [$realhwif, $mac_next]);
|
|
if ($realhwif == $realifv6) {
|
|
$ll_found = false;
|
|
foreach (array_keys(interfaces_addresses($realifv6, true, $ifconfig_details)) as $tmpiface) {
|
|
if (is_linklocal($tmpiface)) {
|
|
$tmpip = explode('%', $tmpiface)[0];
|
|
legacy_interface_deladdress($realifv6, $tmpip, 6);
|
|
$ll_found = true;
|
|
}
|
|
}
|
|
if ($ll_found) {
|
|
$mac_cmd[] = 'down';
|
|
}
|
|
}
|
|
mwexec(join(' ', $mac_cmd));
|
|
}
|
|
|
|
/* only try to set media properties when requested */
|
|
if (!empty($wancfg['media']) || !empty($wancfg['mediaopt'])) {
|
|
$intf_details = $ifconfig_details[$realhwif];
|
|
$media_changed = stripos($intf_details['media_raw'], $wancfg['media']) == false;
|
|
if (!empty($wancfg['mediaopt'])) {
|
|
$media_changed |= stripos($intf_details['media_raw'], $wancfg['mediaopt']) == false;
|
|
}
|
|
if ($media_changed) {
|
|
$cmd = "/sbin/ifconfig " . escapeshellarg($realhwif);
|
|
if (!empty($wancfg['media'])) {
|
|
$cmd .= " media " . escapeshellarg($wancfg['media']);
|
|
}
|
|
if (!empty($wancfg['mediaopt'])) {
|
|
$cmd .= " mediaopt " . escapeshellarg($wancfg['mediaopt']);
|
|
}
|
|
mwexec($cmd);
|
|
}
|
|
}
|
|
|
|
/* set p(ermanent)-promiscuous mode required for e.g. VLAN MAC spoofing */
|
|
if (in_array('ppromisc', $ifconfig_details[$realhwif]['flags'] ?? []) !== !empty($wancfg['promisc'])) {
|
|
mwexecf('/sbin/ifconfig %s %spromisc', [$realhwif, empty($wancfg['promisc']) ? '-' : '']);
|
|
}
|
|
|
|
/* apply interface hardware settings (tso, lro, ..) */
|
|
configure_interface_hardware($realhwif, $ifconfig_details);
|
|
|
|
if (!empty($wancfg['mtu']) && strpos($realhwif, '/') === false) {
|
|
interface_configure_mtu($realhwif, $wancfg['mtu'], $ifconfig_details);
|
|
}
|
|
|
|
/* since the connectivity can include IPv4 and/or IPv6 let the function decide */
|
|
interface_ppps_configure($interface);
|
|
|
|
switch ($wancfg['ipaddr'] ?? 'none') {
|
|
case 'dhcp':
|
|
interface_dhcp_configure($interface);
|
|
break;
|
|
default:
|
|
interface_static_configure($interface, $wancfg);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Unconditional actions on interface include:
|
|
*
|
|
* 1. Disable accepting router advertisements (SLAAC) on main device
|
|
* 2. Enable duplicate address detection (DAD) on main device
|
|
* 3. Set interface description to get more useful ifconfig output
|
|
* 4. Set "up" flag for UP/RUNNING requirement, adding an address
|
|
* already does that so at this point try to be more consistent.
|
|
*/
|
|
$interface_descr = sprintf('%s (%s)', !empty($wancfg['descr']) ? $wancfg['descr'] : strtoupper($interface), $interface);
|
|
/* XXX we should maybe set "ifdisabled" but it could be dangerous for assigned tunnel devices */
|
|
mwexecf('/sbin/ifconfig %s inet6 -accept_rtadv -no_dad description %s up', [$realif, $interface_descr]);
|
|
|
|
switch (isset($config['system']['ipv6allow']) ? ($wancfg['ipaddrv6'] ?? 'none') : 'none') {
|
|
case 'slaac':
|
|
case 'dhcp6':
|
|
mwexecf('/sbin/ifconfig %s inet6 accept_rtadv -ifdisabled up', $realifv6);
|
|
|
|
if (!interface_ppps_bound($interface)) {
|
|
interface_dhcpv6_prepare($interface, $wancfg);
|
|
interface_dhcpv6_configure($interface, $wancfg);
|
|
}
|
|
break;
|
|
case '6rd':
|
|
interface_6rd_configure($interface, $wancfg, $reload || $linkup);
|
|
break;
|
|
case '6to4':
|
|
interface_6to4_configure($interface, $wancfg, $reload || $linkup);
|
|
break;
|
|
case 'track6':
|
|
interface_track6_configure($interface, $wancfg, $reload || $linkup);
|
|
break;
|
|
default:
|
|
if (!interface_ppps_bound($interface)) {
|
|
interface_static6_configure($interface, $wancfg);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* In most cases the IPv6 device is the same as IPv4.
|
|
* If not the safe spot to add a description/up is below
|
|
* when the interface was most likely created.
|
|
*/
|
|
if ($realif != $realifv6) {
|
|
mwexecf('/sbin/ifconfig %s inet6 description %s up', [$realifv6, $interface_descr]);
|
|
}
|
|
|
|
interfaces_vips_configure($interface);
|
|
|
|
/* XXX device member plugin hook */
|
|
link_interface_to_bridge($interface, $realif, $ifconfig_details);
|
|
|
|
/* XXX may be called twice since also tied to dhcp configure */
|
|
interfaces_staticarp_configure($interface, $ifconfig_details);
|
|
|
|
service_log("done.\n", $verbose);
|
|
|
|
if ($reload) {
|
|
system_routing_configure($verbose, $interface);
|
|
plugins_configure('ipsec', $verbose, [$interface]);
|
|
plugins_configure('dhcp', $verbose);
|
|
plugins_configure('dns', $verbose);
|
|
/* XXX not ideal but avoids "errors" in the log */
|
|
plugins_configure('newwanip:rfc2136', $verbose, [[$interface]]);
|
|
}
|
|
|
|
/* XXX device dependency plugin hook */
|
|
$loaded = array_merge($loaded, link_interface_to_gre($interface, true));
|
|
$loaded = array_merge($loaded, link_interface_to_gif($interface, true));
|
|
|
|
/* XXX reload routes for linked devices -- not great but also not avoidable at the moment */
|
|
interfaces_restart_by_device($verbose, $loaded, false);
|
|
|
|
return $loaded;
|
|
}
|
|
|
|
function interface_track6_configure($interface, $lancfg, $reload = false)
|
|
{
|
|
global $config;
|
|
|
|
if (!is_array($lancfg) || empty($lancfg['track6-interface'])) {
|
|
return;
|
|
}
|
|
|
|
$trackcfg = $config['interfaces'][$lancfg['track6-interface']];
|
|
if (!isset($trackcfg['enable'])) {
|
|
log_msg("Interface {$interface} tracking nonexistent interface {$lancfg['track6-interface']}", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
$realifv6 = get_real_interface($interface, 'inet6');
|
|
mwexecf('/sbin/ifconfig %s inet6 %sifdisabled', [$realifv6, isset($lancfg['enable']) ? '-' : '']);
|
|
|
|
switch ($trackcfg['ipaddrv6']) {
|
|
case '6to4':
|
|
interface_track6_6to4_configure($interface, $lancfg);
|
|
break;
|
|
case '6rd':
|
|
interface_track6_6rd_configure($interface, $lancfg);
|
|
break;
|
|
case 'slaac':
|
|
mwexecf('/sbin/ifconfig %s inet6 accept_rtadv', [$realifv6]);
|
|
/* FALLTHROUGH */
|
|
case 'dhcp6':
|
|
if ($reload || !isset($lancfg['enable'])) {
|
|
interface_dhcpv6_prepare($lancfg['track6-interface'], $trackcfg);
|
|
killbypid('/var/run/dhcp6c.pid', 'HUP');
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
function interface_track6_6rd_configure($interface, $lancfg)
|
|
{
|
|
global $config;
|
|
|
|
if (!isset($lancfg['enable'])) {
|
|
/* deconfiguring is done elsewhere as it simply removes addresses */
|
|
return;
|
|
}
|
|
|
|
$wancfg = $config['interfaces'][$lancfg['track6-interface']];
|
|
|
|
if (empty($wancfg)) {
|
|
log_msg("Interface {$interface} tracking nonexistent interface {$lancfg['track6-interface']}", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
$ip4address = get_interface_ip($lancfg['track6-interface']);
|
|
|
|
if (!is_ipaddrv4($ip4address)) {
|
|
log_msg("The interface IPv4 address '{$ip4address}' on interface '{$lancfg['track6-interface']}' is invalid, not configuring 6RD tracking", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
$hexwanv4 = return_hex_ipv4($ip4address);
|
|
|
|
/* create the long prefix notation for math, save the prefix length */
|
|
$rd6prefix = explode("/", $wancfg['prefix-6rd']);
|
|
$rd6prefixlen = $rd6prefix[1];
|
|
$rd6prefix = Net_IPv6::uncompress($rd6prefix[0]);
|
|
|
|
/* binary presentation of the prefix for all 128 bits. */
|
|
$rd6lanbin = convert_ipv6_to_128bit($rd6prefix);
|
|
|
|
/* just save the left prefix length bits */
|
|
$rd6lanbin = substr($rd6lanbin, 0, $rd6prefixlen);
|
|
/* add the v4 address, offset n bits from the left */
|
|
$rd6lanbin .= substr(sprintf("%032b", hexdec($hexwanv4)), (0 + $wancfg['prefix-6rd-v4plen']), 32);
|
|
|
|
/* add the custom prefix id, max 32bits long? (64 bits - (prefixlen + (32 - v4plen)) */
|
|
/* 64 - (37 + (32 - 17)) = 8 == /52 */
|
|
$restbits = 64 - ($rd6prefixlen + (32 - $wancfg['prefix-6rd-v4plen']));
|
|
// echo "64 - (prefixlen {$rd6prefixlen} + v4len (32 - {$wancfg['prefix-6rd-v4plen']})) = {$restbits} \n";
|
|
$rd6lanbin .= substr(sprintf("%032b", str_pad($lancfg['track6-prefix-id'], 32, "0", STR_PAD_LEFT)), (32 - $restbits), 32);
|
|
/* fill the rest out with zeros */
|
|
$rd6lanbin = str_pad($rd6lanbin, 128, "0", STR_PAD_RIGHT);
|
|
|
|
/* convert the 128 bits for the lan address back into a valid IPv6 address */
|
|
$rd6lan = convert_128bit_to_ipv6($rd6lanbin) . (1 + get_interface_number_track6($lancfg['track6-interface'], $interface));
|
|
|
|
$lanif = get_real_interface($interface, 'inet6');
|
|
list ($oip) = interfaces_primary_address6($interface);
|
|
if (!empty($oip)) {
|
|
mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete");
|
|
}
|
|
|
|
log_msg("rd6 {$interface} with ipv6 address {$rd6lan} based on {$lancfg['track6-interface']} ipv4 {$ip4address}");
|
|
mwexec("/sbin/ifconfig {$lanif} inet6 {$rd6lan} prefixlen 64");
|
|
}
|
|
|
|
function interface_track6_6to4_configure($interface, $lancfg)
|
|
{
|
|
if (!isset($lancfg['enable'])) {
|
|
/* deconfiguring is done elsewhere as it simply removes addresses */
|
|
return;
|
|
}
|
|
|
|
$ip4address = get_interface_ip($lancfg['track6-interface']);
|
|
|
|
if (!is_ipaddrv4($ip4address) || is_private_ip($ip4address)) {
|
|
log_msg("The interface IPv4 address '{$ip4address}' on interface '{$lancfg['track6-interface']}' is not public, not configuring 6to4 tracking", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
$hexwanv4 = return_hex_ipv4($ip4address);
|
|
|
|
/* create the long prefix notation for math, save the prefix length */
|
|
$sixto4prefix = "2002::";
|
|
$sixto4prefixlen = 16;
|
|
$sixto4prefix = Net_IPv6::uncompress($sixto4prefix);
|
|
|
|
/* binary presentation of the prefix for all 128 bits. */
|
|
$sixto4lanbin = convert_ipv6_to_128bit($sixto4prefix);
|
|
|
|
/* just save the left prefix length bits */
|
|
$sixto4lanbin = substr($sixto4lanbin, 0, $sixto4prefixlen);
|
|
/* add the v4 address */
|
|
$sixto4lanbin .= sprintf("%032b", hexdec($hexwanv4));
|
|
/* add the custom prefix id */
|
|
$sixto4lanbin .= sprintf("%016b", $lancfg['track6-prefix-id']);
|
|
/* fill the rest out with zeros */
|
|
$sixto4lanbin = str_pad($sixto4lanbin, 128, "0", STR_PAD_RIGHT);
|
|
|
|
/* convert the 128 bits for the lan address back into a valid IPv6 address */
|
|
$sixto4lan = convert_128bit_to_ipv6($sixto4lanbin) . (1 + get_interface_number_track6($lancfg['track6-interface'], $interface));
|
|
|
|
$lanif = get_real_interface($interface, 'inet6');
|
|
list ($oip) = interfaces_primary_address6($interface);
|
|
if (!empty($oip)) {
|
|
mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete");
|
|
}
|
|
|
|
log_msg("6to4 {$interface} with ipv6 address {$sixto4lan} based on {$lancfg['track6-interface']} ipv4 {$ip4address}");
|
|
mwexec("/sbin/ifconfig {$lanif} inet6 {$sixto4lan} prefixlen 64");
|
|
}
|
|
|
|
function interface_6rd_configure($interface, $wancfg, $update = false)
|
|
{
|
|
if (!is_array($wancfg)) {
|
|
return;
|
|
}
|
|
|
|
$ip4address = get_interface_ip($interface);
|
|
|
|
if (!is_ipaddrv4($ip4address)) {
|
|
log_msg("The interface IPv4 address '{$ip4address}' on interface '{$interface}' is invalid, not configuring 6RD tunnel", LOG_ERR);
|
|
return false;
|
|
}
|
|
|
|
$hexwanv4 = return_hex_ipv4(!empty($wancfg['prefix-6rd-v4addr']) ? $wancfg['prefix-6rd-v4addr'] : $ip4address);
|
|
|
|
if (!is_numeric($wancfg['prefix-6rd-v4plen']) || $wancfg['prefix-6rd-v4plen'] < 0 || $wancfg['prefix-6rd-v4plen'] > 32) {
|
|
log_msg("The interface IPv4 prefix '{$wancfg['prefix-6rd-v4plen']}' on interface '{$interface}' is invalid, assuming zero", LOG_WARNING);
|
|
$wancfg['prefix-6rd-v4plen'] = 0;
|
|
}
|
|
|
|
/* create the long prefix notation for math, save the prefix length */
|
|
$rd6prefix = explode("/", $wancfg['prefix-6rd']);
|
|
$rd6prefix_isp = $rd6prefix[0];
|
|
$rd6prefixlen = $rd6prefix[1];
|
|
$rd6prefix = Net_IPv6::uncompress($rd6prefix[0]);
|
|
|
|
/* binary presentation of the prefix for all 128 bits. */
|
|
$rd6prefixbin = convert_ipv6_to_128bit($rd6prefix);
|
|
|
|
/* just save the left prefix length bits */
|
|
$rd6prefixbin = substr($rd6prefixbin, 0, $rd6prefixlen);
|
|
/* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */
|
|
$rd6prefixbin .= substr(sprintf("%032b", hexdec($hexwanv4)), $wancfg['prefix-6rd-v4plen'], 32);
|
|
/* fill out the rest with 0's */
|
|
$rd6prefixbin = str_pad($rd6prefixbin, 128, "0", STR_PAD_RIGHT);
|
|
|
|
/* convert the 128 bits for the broker address back into a valid IPv6 address */
|
|
$rd6prefix = convert_128bit_to_ipv6($rd6prefixbin);
|
|
|
|
/* use gateway inside original prefix to avoid routing issues */
|
|
$rd6brgw = "{$rd6prefix_isp}{$wancfg['gateway-6rd']}";
|
|
|
|
$stfiface = "{$interface}_stf";
|
|
legacy_interface_destroy($stfiface);
|
|
legacy_interface_create('stf', $stfiface);
|
|
legacy_interface_flags($stfiface, 'link2');
|
|
|
|
$mtu = !empty($wancfg['mtu']) ? $wancfg['mtu'] - 20 : 1280;
|
|
if ($mtu > 1480) {
|
|
$mtu = 1480;
|
|
}
|
|
legacy_interface_mtu($stfiface, $mtu);
|
|
|
|
/* use original prefix length for network address to avoid setting the same subnet as on the LAN side (/64 prefix) */
|
|
mwexecf('/sbin/ifconfig %s inet6 %s/%s', array($stfiface, $rd6prefix, $rd6prefixlen));
|
|
mwexecf('/sbin/ifconfig %s stfv4br %s', array($stfiface, $wancfg['gateway-6rd']));
|
|
mwexecf('/sbin/ifconfig %s stfv4net %s/%s', array($stfiface, $ip4address, $wancfg['prefix-6rd-v4plen']));
|
|
mwexecf('/usr/local/sbin/ifctl -i %s -6rd -a %s', [$stfiface, $rd6brgw]);
|
|
|
|
$gateways = new \OPNsense\Routing\Gateways();
|
|
$ip4gateway = $gateways->getInterfaceGateway($interface, 'inet');
|
|
system_host_route($wancfg['gateway-6rd'], $ip4gateway);
|
|
|
|
link_interface_to_track6($interface, $update);
|
|
}
|
|
|
|
function interface_6to4_configure($interface, $wancfg, $update = false)
|
|
{
|
|
if (!is_array($wancfg)) {
|
|
return;
|
|
}
|
|
|
|
$ip4address = get_interface_ip($interface);
|
|
|
|
if (!is_ipaddrv4($ip4address) || is_private_ip($ip4address)) {
|
|
log_msg("The interface IPv4 address '{$ip4address}' on interface '{$interface}' is not public, not configuring 6to4 tunnel", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
/* create the long prefix notation for math, save the prefix length */
|
|
$stfprefixlen = 16;
|
|
$stfprefix = Net_IPv6::uncompress("2002::");
|
|
$stfarr = explode(":", $stfprefix);
|
|
$v4prefixlen = "0";
|
|
|
|
/* we need the hex form of the interface IPv4 address */
|
|
$ip4arr = explode(".", $ip4address);
|
|
$hexwanv4 = "";
|
|
foreach ($ip4arr as $octet) {
|
|
$hexwanv4 .= sprintf("%02x", $octet);
|
|
}
|
|
|
|
/* we need the hex form of the broker IPv4 address */
|
|
$ip4arr = explode(".", "192.88.99.1");
|
|
$hexbrv4 = "";
|
|
foreach ($ip4arr as $octet) {
|
|
$hexbrv4 .= sprintf("%02x", $octet);
|
|
}
|
|
|
|
/* binary presentation of the prefix for all 128 bits. */
|
|
$stfprefixbin = "";
|
|
foreach ($stfarr as $element) {
|
|
$stfprefixbin .= sprintf("%016b", hexdec($element));
|
|
}
|
|
/* just save the left prefix length bits */
|
|
$stfprefixstartbin = substr($stfprefixbin, 0, $stfprefixlen);
|
|
|
|
/* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */
|
|
$stfbrokerbin = substr(sprintf("%032b", hexdec($hexbrv4)), $v4prefixlen, 32);
|
|
$stfbrokerbin = str_pad($stfprefixstartbin . $stfbrokerbin, 128, "0", STR_PAD_RIGHT);
|
|
|
|
/* for the local subnet too. */
|
|
$stflanbin = substr(sprintf("%032b", hexdec($hexwanv4)), $v4prefixlen, 32);
|
|
$stflanbin = str_pad($stfprefixstartbin . $stflanbin, 128, "0", STR_PAD_RIGHT);
|
|
|
|
/* convert the 128 bits for the broker address back into a valid IPv6 address */
|
|
$stfbrarr = array();
|
|
$stfbrbinarr = str_split($stfbrokerbin, 16);
|
|
foreach ($stfbrbinarr as $bin) {
|
|
$stfbrarr[] = dechex(bindec($bin));
|
|
}
|
|
$stfbrgw = Net_IPv6::compress(implode(":", $stfbrarr));
|
|
|
|
/* convert the 128 bits for the broker address back into a valid IPv6 address */
|
|
$stflanarr = array();
|
|
$stflanbinarr = str_split($stflanbin, 16);
|
|
foreach ($stflanbinarr as $bin) {
|
|
$stflanarr[] = dechex(bindec($bin));
|
|
}
|
|
$stflan = Net_IPv6::compress(implode(":", $stflanarr));
|
|
|
|
$stfiface = "{$interface}_stf";
|
|
legacy_interface_destroy($stfiface);
|
|
legacy_interface_create('stf', $stfiface);
|
|
legacy_interface_flags($stfiface, 'link2');
|
|
|
|
$mtu = !empty($wancfg['mtu']) ? $wancfg['mtu'] - 20 : 1280;
|
|
if ($mtu > 1480) {
|
|
$mtu = 1480;
|
|
}
|
|
legacy_interface_mtu($stfiface, $mtu);
|
|
|
|
mwexecf('/sbin/ifconfig %s inet6 %s prefixlen 16', array($stfiface, $stflan));
|
|
mwexecf('/usr/local/sbin/ifctl -i %s -6rd -a %s', [$stfiface, $stfbrgw]);
|
|
|
|
$gateways = new \OPNsense\Routing\Gateways();
|
|
$ip4gateway = $gateways->getInterfaceGateway($interface, 'inet');
|
|
system_host_route('192.88.99.1', $ip4gateway);
|
|
|
|
link_interface_to_track6($interface, $update);
|
|
}
|
|
|
|
function interface_dhcpv6_configure($interface, $wancfg)
|
|
{
|
|
$syscfg = config_read_array('system');
|
|
|
|
/* write DUID if override was set */
|
|
if (!empty($syscfg['ipv6duid'])) {
|
|
dhcp6c_duid_write($syscfg['ipv6duid']);
|
|
/* clear DUID if it is faulty */
|
|
} elseif (empty(dhcp6c_duid_read())) {
|
|
dhcp6c_duid_clear();
|
|
}
|
|
|
|
if (!is_array($wancfg)) {
|
|
return;
|
|
}
|
|
|
|
$realifv6 = get_real_interface($interface, 'inet6');
|
|
|
|
/* always kill rtsold in case of reconfigure */
|
|
killbypid('/var/run/rtsold.pid');
|
|
|
|
$rtsoldcommand = exec_safe('/usr/sbin/rtsold -aiu -p %s -A %s -R %s', [
|
|
'/var/run/rtsold.pid',
|
|
'/var/etc/rtsold_script.sh',
|
|
'/usr/local/opnsense/scripts/interfaces/rtsold_resolvconf.sh',
|
|
]);
|
|
|
|
if (!empty($syscfg['dhcp6_debug'])) {
|
|
$mode = [ '1' => ' -d', '2' => ' -D' ];
|
|
$rtsoldcommand .= $mode[$syscfg['dhcp6_debug']];
|
|
}
|
|
|
|
/* fire up rtsold for IPv6 RAs first */
|
|
mwexec($rtsoldcommand);
|
|
|
|
/* unconditional trigger for hybrid approach, reloads without advertisements */
|
|
mwexecf('/var/etc/rtsold_script.sh %s', array($realifv6));
|
|
}
|
|
|
|
function interface_dhcpv6_id($interface)
|
|
{
|
|
global $config;
|
|
|
|
/* configuration default */
|
|
$id = 0;
|
|
|
|
if (empty($config['interfaces'])) {
|
|
return $id;
|
|
}
|
|
|
|
/* detect unique index */
|
|
foreach (array_keys($config['interfaces']) as $key) {
|
|
if ($key == $interface) {
|
|
break;
|
|
}
|
|
$id += 1;
|
|
}
|
|
|
|
return $id;
|
|
}
|
|
|
|
function interface_dhcpv6_prepare($interface, $wancfg, $cleanup = false)
|
|
{
|
|
if (!is_array($wancfg)) {
|
|
return;
|
|
}
|
|
|
|
$wanif = get_real_interface($interface, 'inet6');
|
|
$id = interface_dhcpv6_id($interface);
|
|
$syscfg = config_read_array('system');
|
|
|
|
if (!empty($wancfg['adv_dhcp6_config_file_override'])) {
|
|
$dhcp6cfile = $wancfg['adv_dhcp6_config_file_override_path'];
|
|
if (file_exists($dhcp6cfile)) {
|
|
$dhcp6cconf = file_get_contents($dhcp6cfile);
|
|
$dhcp6cconf = DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);
|
|
} else {
|
|
log_msg("DHCP6 config file override does not exist: '{$dhcp6cfile}'", LOG_ERR);
|
|
}
|
|
} elseif (!empty($wancfg['adv_dhcp6_config_advanced'])) {
|
|
$dhcp6cconf = DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $id);
|
|
} else {
|
|
$dhcp6cconf = DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id);
|
|
}
|
|
|
|
if (!$cleanup) {
|
|
@file_put_contents("/var/etc/dhcp6c_{$interface}.conf", $dhcp6cconf);
|
|
} else {
|
|
@unlink("/var/etc/dhcp6c_{$interface}.conf");
|
|
}
|
|
|
|
$dhcp6cscript = <<<EOF
|
|
#!/bin/sh
|
|
|
|
case \$REASON in
|
|
INFOREQ|REBIND|RENEW|REQUEST)
|
|
/usr/bin/logger -t dhcp6c "dhcp6c_script: \$REASON on {$wanif} executing"
|
|
|
|
ARGS=
|
|
for NAMESERVER in \${new_domain_name_servers}; do
|
|
ARGS="\${ARGS} -a \${NAMESERVER}"
|
|
done
|
|
/usr/local/sbin/ifctl -i {$wanif} -6nd \${ARGS}
|
|
|
|
ARGS=
|
|
for DOMAIN in \${new_domain_name}; do
|
|
ARGS="\${ARGS} -a \${DOMAIN}"
|
|
done
|
|
/usr/local/sbin/ifctl -i {$wanif} -6sd \${ARGS}
|
|
|
|
ARGS=
|
|
for PD in \${PDINFO}; do
|
|
ARGS="\${ARGS} -a \${PD}"
|
|
done
|
|
if [ \${REASON} != "RENEW" -a \${REASON} != "REBIND" ]; then
|
|
# cannot update since PDINFO may be incomplete in these cases
|
|
# as each PD is being handled separately via the client side
|
|
/usr/local/sbin/ifctl -i {$wanif} -6pd \${ARGS}
|
|
fi
|
|
|
|
FORCE=
|
|
if [ \${REASON} = "REQUEST" ]; then
|
|
/usr/bin/logger -t dhcp6c "dhcp6c_script: \$REASON on {$wanif} renewal"
|
|
FORCE=force
|
|
fi
|
|
|
|
/usr/local/sbin/configctl -d interface newipv6 {$wanif} \${FORCE}
|
|
;;
|
|
EXIT|RELEASE)
|
|
/usr/bin/logger -t dhcp6c "dhcp6c_script: \$REASON on {$wanif} executing"
|
|
|
|
/usr/local/sbin/ifctl -i {$wanif} -6nd
|
|
/usr/local/sbin/ifctl -i {$wanif} -6sd
|
|
/usr/local/sbin/ifctl -i {$wanif} -6pd
|
|
|
|
/usr/local/sbin/configctl -d interface newipv6 {$wanif}
|
|
;;
|
|
*)
|
|
/usr/bin/logger -t dhcp6c "dhcp6c_script: \$REASON on {$wanif} ignored"
|
|
;;
|
|
esac
|
|
|
|
EOF;
|
|
|
|
@file_put_contents("/var/etc/dhcp6c_{$interface}_script.sh", $dhcp6cscript);
|
|
@chmod("/var/etc/dhcp6c_{$interface}_script.sh", 0755);
|
|
|
|
$dhcp6ccommand = exec_safe('/usr/local/sbin/dhcp6c -c %s -p %s', [
|
|
'/var/etc/dhcp6c.conf',
|
|
'/var/run/dhcp6c.pid',
|
|
]);
|
|
|
|
if (!empty($syscfg['dhcp6_debug'])) {
|
|
$mode = [ '1' => ' -d', '2' => ' -D' ];
|
|
$dhcp6ccommand .= $mode[$syscfg['dhcp6_debug']];
|
|
}
|
|
|
|
if (!empty($syscfg['dhcp6_norelease'])) {
|
|
$dhcp6ccommand .= ' -n';
|
|
}
|
|
|
|
$dhcp6cconf = '';
|
|
|
|
/* merge configs and prepare single instance of dhcp6c for startup */
|
|
foreach (legacy_config_get_interfaces(['enable' => true, 'virtual' => false]) as $_interface => $_wancfg) {
|
|
if (empty($_wancfg['ipaddrv6']) || ($_wancfg['ipaddrv6'] != 'dhcp6' && $_wancfg['ipaddrv6'] != 'slaac')) {
|
|
continue;
|
|
}
|
|
|
|
if (!file_exists("/var/etc/dhcp6c_{$_interface}.conf")) {
|
|
/* config file not yet rendered, defer for later */
|
|
continue;
|
|
}
|
|
|
|
$dhcp6cconf .= file_get_contents("/var/etc/dhcp6c_{$_interface}.conf");
|
|
}
|
|
|
|
@file_put_contents('/var/etc/dhcp6c.conf', $dhcp6cconf);
|
|
|
|
$rtsold_script = <<<EOD
|
|
#!/bin/sh
|
|
# this file was auto-generated, do not edit
|
|
if [ -z "\${1}" ]; then
|
|
echo "Nothing to do."
|
|
exit 0
|
|
fi
|
|
if grep -q "^interface \${1} " /var/etc/radvd.conf; then
|
|
echo "Rejecting own configuration."
|
|
exit 0
|
|
fi
|
|
if [ -n "\${2}" ]; then
|
|
# Note that the router file can be written by ppp-linkup.sh or
|
|
# this script so do not clear the file as it may already exist.
|
|
/usr/local/sbin/ifctl -i \${1} -6rd -a \${2}
|
|
fi
|
|
if [ -f /var/run/dhcp6c.pid ]; then
|
|
if ! /bin/pkill -0 -F /var/run/dhcp6c.pid; then
|
|
rm -f /var/run/dhcp6c.pid
|
|
fi
|
|
fi
|
|
if [ -f /var/run/dhcp6c.pid ]; then
|
|
/usr/bin/logger -t dhcp6c "RTSOLD script - Sending SIGHUP to dhcp6c"
|
|
/bin/pkill -HUP -F /var/run/dhcp6c.pid
|
|
else
|
|
/usr/bin/logger -t dhcp6c "RTSOLD script - Starting dhcp6c daemon"
|
|
{$dhcp6ccommand}
|
|
fi
|
|
|
|
EOD;
|
|
|
|
@file_put_contents('/var/etc/rtsold_script.sh', $rtsold_script);
|
|
@chmod('/var/etc/rtsold_script.sh', 0755);
|
|
}
|
|
|
|
function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0)
|
|
{
|
|
$dhcp6cconf = "interface {$wanif} {\n";
|
|
|
|
if ($wancfg['ipaddrv6'] == 'slaac') {
|
|
/* for SLAAC interfaces we do fire off a dhcp6 client for just our name servers */
|
|
$dhcp6cconf .= " information-only;\n";
|
|
$dhcp6cconf .= " request domain-name-servers;\n";
|
|
$dhcp6cconf .= " request domain-name;\n";
|
|
$dhcp6cconf .= " script \"/var/etc/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n";
|
|
$dhcp6cconf .= "};\n";
|
|
} else {
|
|
if (!isset($wancfg['dhcp6prefixonly'])) {
|
|
$dhcp6cconf .= " send ia-na {$id}; # request stateful address\n";
|
|
}
|
|
if (is_numeric($wancfg['dhcp6-ia-pd-len'])) {
|
|
$dhcp6cconf .= " send ia-pd {$id}; # request prefix delegation\n";
|
|
}
|
|
|
|
$dhcp6cconf .= " request domain-name-servers;\n";
|
|
$dhcp6cconf .= " request domain-name;\n";
|
|
$dhcp6cconf .= " script \"/var/etc/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n";
|
|
$dhcp6cconf .= "};\n";
|
|
|
|
if (!isset($wancfg['dhcp6prefixonly'])) {
|
|
$dhcp6cconf .= "id-assoc na {$id} { };\n";
|
|
}
|
|
|
|
if (is_numeric($wancfg['dhcp6-ia-pd-len'])) {
|
|
$dhcp6cconf .= "id-assoc pd {$id} {\n";
|
|
if (isset($wancfg['dhcp6-ia-pd-send-hint'])) {
|
|
$preflen = 64 - $wancfg['dhcp6-ia-pd-len'];
|
|
$dhcp6cconf .= " prefix ::/{$preflen} infinity;\n";
|
|
}
|
|
if (isset($wancfg['dhcp6-prefix-id']) && is_numeric($wancfg['dhcp6-prefix-id'])) {
|
|
$dhcp6cconf .= " prefix-interface {$wanif} {\n";
|
|
$dhcp6cconf .= " sla-id {$wancfg['dhcp6-prefix-id']};\n";
|
|
$dhcp6cconf .= " sla-len {$wancfg['dhcp6-ia-pd-len']};\n";
|
|
if (isset($wancfg['dhcp6_ifid'])) {
|
|
$dhcp6cconf .= " ifid {$wancfg['dhcp6_ifid']};\n";
|
|
}
|
|
$dhcp6cconf .= " };\n";
|
|
}
|
|
foreach (link_interface_to_track6($interface) as $friendly => $lancfg) {
|
|
if (is_numeric($lancfg['track6-prefix-id'])) {
|
|
$trackifv6 = get_real_interface($friendly, 'inet6');
|
|
$dhcp6cconf .= " prefix-interface {$trackifv6} {\n";
|
|
$dhcp6cconf .= " sla-id {$lancfg['track6-prefix-id']};\n";
|
|
$dhcp6cconf .= " sla-len {$wancfg['dhcp6-ia-pd-len']};\n";
|
|
if (isset($lancfg['track6_ifid'])) {
|
|
$dhcp6cconf .= " ifid {$lancfg['track6_ifid']};\n";
|
|
}
|
|
$dhcp6cconf .= " };\n";
|
|
}
|
|
}
|
|
$dhcp6cconf .= "};\n";
|
|
}
|
|
}
|
|
|
|
return $dhcp6cconf;
|
|
}
|
|
|
|
function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $id = 0)
|
|
{
|
|
$send_options = "";
|
|
|
|
if ($wancfg['adv_dhcp6_interface_statement_send_options'] != '') {
|
|
$options = preg_split('/\s*,\s*(?=(?:[^"]*"[^"]*")*[^"]*$)/', $wancfg['adv_dhcp6_interface_statement_send_options']);
|
|
foreach ($options as $option) {
|
|
$send_options .= " send {$option};\n";
|
|
}
|
|
}
|
|
|
|
$request_options = "";
|
|
if ($wancfg['adv_dhcp6_interface_statement_request_options'] != '') {
|
|
$options = preg_split('/\s*,\s*(?=(?:[^"]*"[^"]*")*[^"]*$)/', $wancfg['adv_dhcp6_interface_statement_request_options']);
|
|
foreach ($options as $option) {
|
|
$request_options .= " request {$option};\n";
|
|
}
|
|
}
|
|
|
|
$information_only = "";
|
|
if ($wancfg['adv_dhcp6_interface_statement_information_only_enable'] != '') {
|
|
$information_only = " information-only;\n";
|
|
}
|
|
|
|
$script = " script \"/var/etc/dhcp6c_{$interface}_script.sh\";\n";
|
|
if ($wancfg['adv_dhcp6_interface_statement_script'] != '') {
|
|
$script = " script \"{$wancfg['adv_dhcp6_interface_statement_script']}\";\n";
|
|
}
|
|
|
|
$interface_statement = "interface {$wanif} {\n";
|
|
$interface_statement .= $send_options;
|
|
$interface_statement .= $request_options;
|
|
$interface_statement .= $information_only;
|
|
$interface_statement .= $script;
|
|
$interface_statement .= "};\n";
|
|
|
|
$id_assoc_statement_address = "";
|
|
if (!empty($wancfg['adv_dhcp6_id_assoc_statement_address_enable'])) {
|
|
$id_assoc_statement_address .= "id-assoc na ";
|
|
if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_id'])) {
|
|
$id_assoc_statement_address .= "{$wancfg['adv_dhcp6_id_assoc_statement_address_id']}";
|
|
} else {
|
|
$id_assoc_statement_address .= $id;
|
|
}
|
|
$id_assoc_statement_address .= " {\n";
|
|
|
|
if (
|
|
($wancfg['adv_dhcp6_id_assoc_statement_address'] != '') &&
|
|
(is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_pltime']) ||
|
|
$wancfg['adv_dhcp6_id_assoc_statement_address_pltime'] == 'infinity')
|
|
) {
|
|
$id_assoc_statement_address .= " address";
|
|
$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address']}";
|
|
$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_pltime']}";
|
|
if (
|
|
is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_vltime']) ||
|
|
$wancfg['adv_dhcp6_id_assoc_statement_address_vltime'] == 'infinity'
|
|
) {
|
|
$id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_vltime']}";
|
|
}
|
|
$id_assoc_statement_address .= ";\n";
|
|
}
|
|
|
|
$id_assoc_statement_address .= "};\n";
|
|
}
|
|
|
|
$id_assoc_statement_prefix = "";
|
|
if ($wancfg['adv_dhcp6_id_assoc_statement_prefix_enable'] != '') {
|
|
$id_assoc_statement_prefix .= "id-assoc pd ";
|
|
if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_id'])) {
|
|
$id_assoc_statement_prefix .= "{$wancfg['adv_dhcp6_id_assoc_statement_prefix_id']}";
|
|
} else {
|
|
$id_assoc_statement_prefix .= $id;
|
|
}
|
|
$id_assoc_statement_prefix .= " {\n";
|
|
|
|
if (
|
|
($wancfg['adv_dhcp6_id_assoc_statement_prefix'] != '') &&
|
|
(is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']) ||
|
|
$wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime'] == 'infinity')
|
|
) {
|
|
$id_assoc_statement_prefix .= " prefix";
|
|
$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix']}";
|
|
$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']}";
|
|
if (
|
|
(is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'])) ||
|
|
($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'] == 'infinity')
|
|
) {
|
|
$id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime']}";
|
|
}
|
|
$id_assoc_statement_prefix .= ";\n";
|
|
}
|
|
|
|
if (isset($wancfg['dhcp6-prefix-id']) && is_numeric($wancfg['dhcp6-prefix-id'])) {
|
|
$id_assoc_statement_prefix .= " prefix-interface {$wanif} {\n";
|
|
$id_assoc_statement_prefix .= " sla-id {$wancfg['dhcp6-prefix-id']};\n";
|
|
if (
|
|
($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] >= 0) &&
|
|
($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] <= 128)
|
|
) {
|
|
$id_assoc_statement_prefix .= " sla-len {$wancfg['adv_dhcp6_prefix_interface_statement_sla_len']};\n";
|
|
}
|
|
if (isset($wancfg['dhcp6_ifid'])) {
|
|
$id_assoc_statement_prefix .= " ifid {$wancfg['dhcp6_ifid']};\n";
|
|
}
|
|
$id_assoc_statement_prefix .= " };\n";
|
|
}
|
|
|
|
foreach (link_interface_to_track6($interface) as $friendly => $lancfg) {
|
|
if (is_numeric($lancfg['track6-prefix-id'])) {
|
|
$trackifv6 = get_real_interface($friendly, 'inet6');
|
|
$id_assoc_statement_prefix .= " prefix-interface {$trackifv6} {\n";
|
|
$id_assoc_statement_prefix .= " sla-id {$lancfg['track6-prefix-id']};\n";
|
|
if (
|
|
($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] >= 0) &&
|
|
($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] <= 128)
|
|
) {
|
|
$id_assoc_statement_prefix .= " sla-len {$wancfg['adv_dhcp6_prefix_interface_statement_sla_len']};\n";
|
|
}
|
|
if (isset($lancfg['track6_ifid'])) {
|
|
$id_assoc_statement_prefix .= " ifid {$lancfg['track6_ifid']};\n";
|
|
}
|
|
$id_assoc_statement_prefix .= " };\n";
|
|
}
|
|
}
|
|
|
|
$id_assoc_statement_prefix .= "};\n";
|
|
}
|
|
|
|
$authentication_statement = "";
|
|
if (
|
|
($wancfg['adv_dhcp6_authentication_statement_authname'] != '') &&
|
|
($wancfg['adv_dhcp6_authentication_statement_protocol'] == 'delayed')
|
|
) {
|
|
$authentication_statement .= "authentication {$wancfg['adv_dhcp6_authentication_statement_authname']} {\n";
|
|
$authentication_statement .= " protocol {$wancfg['adv_dhcp6_authentication_statement_protocol']};\n";
|
|
if (preg_match("/(hmac(-)?md5)||(HMAC(-)?MD5)/", $wancfg['adv_dhcp6_authentication_statement_algorithm'])) {
|
|
$authentication_statement .= " algorithm {$wancfg['adv_dhcp6_authentication_statement_algorithm']};\n";
|
|
}
|
|
if ($wancfg['adv_dhcp6_authentication_statement_rdm'] == 'monocounter') {
|
|
$authentication_statement .= " rdm {$wancfg['adv_dhcp6_authentication_statement_rdm']};\n";
|
|
}
|
|
$authentication_statement .= "};\n";
|
|
}
|
|
|
|
$key_info_statement = "";
|
|
if (
|
|
($wancfg['adv_dhcp6_key_info_statement_keyname'] != '') &&
|
|
($wancfg['adv_dhcp6_key_info_statement_realm'] != '') &&
|
|
(is_numeric($wancfg['adv_dhcp6_key_info_statement_keyid'])) &&
|
|
($wancfg['adv_dhcp6_key_info_statement_secret'] != '')
|
|
) {
|
|
$key_info_statement .= "keyinfo {$wancfg['adv_dhcp6_key_info_statement_keyname']} {\n";
|
|
$key_info_statement .= " realm \"{$wancfg['adv_dhcp6_key_info_statement_realm']}\";\n";
|
|
$key_info_statement .= " keyid {$wancfg['adv_dhcp6_key_info_statement_keyid']};\n";
|
|
$key_info_statement .= " secret \"{$wancfg['adv_dhcp6_key_info_statement_secret']}\";\n";
|
|
if (preg_match("/((([0-9]{4}-)?[0-9]{2}-[0-9]{2} )?[0-9]{2}:[0-9]{2})|(forever)/", $wancfg['adv_dhcp6_key_info_statement_expire'])) {
|
|
$key_info_statement .= " expire \"{$wancfg['adv_dhcp6_key_info_statement_expire']}\";\n";
|
|
}
|
|
$key_info_statement .= "};\n";
|
|
}
|
|
|
|
$dhcp6cconf = $interface_statement;
|
|
$dhcp6cconf .= $id_assoc_statement_address;
|
|
$dhcp6cconf .= $id_assoc_statement_prefix;
|
|
$dhcp6cconf .= $authentication_statement;
|
|
$dhcp6cconf .= $key_info_statement;
|
|
|
|
$dhcp6cconf = DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);
|
|
|
|
return $dhcp6cconf;
|
|
}
|
|
|
|
function DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf)
|
|
{
|
|
return DHCP_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);
|
|
}
|
|
|
|
function interface_dhcp_configure($interface = 'wan')
|
|
{
|
|
global $config;
|
|
|
|
$wancfg = $config['interfaces'][$interface];
|
|
if (empty($wancfg)) {
|
|
log_msg("Interface '{$interface}' does not have a DHCP configuration.", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
$wanif = get_real_interface($interface);
|
|
if (empty($wanif)) {
|
|
log_msg("Invalid interface '{$interface}' in interface_dhcp_configure()", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
killbypid("/var/run/dhclient.{$wanif}.pid");
|
|
|
|
$fd = fopen("/var/etc/dhclient_{$interface}.conf", "w");
|
|
if (!$fd) {
|
|
log_msg("Error: cannot open dhclient_{$interface}.conf in interface_dhcp_configure() for writing.", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
if ($wancfg['dhcphostname']) {
|
|
$dhclientconf_hostname = "send dhcp-client-identifier \"{$wancfg['dhcphostname']}\";\n";
|
|
$dhclientconf_hostname .= "\tsend host-name \"{$wancfg['dhcphostname']}\";\n";
|
|
} else {
|
|
$dhclientconf_hostname = "";
|
|
}
|
|
|
|
$dhclientconf = <<<EOD
|
|
interface "{$wanif}" {
|
|
timeout 60;
|
|
retry 15;
|
|
select-timeout 0;
|
|
initial-interval 1;
|
|
{$dhclientconf_hostname}
|
|
script "/usr/local/opnsense/scripts/interfaces/dhclient-script";
|
|
|
|
EOD;
|
|
|
|
if (empty($wancfg['dhcphonourmtu'])) {
|
|
$dhclientconf .= " supersede interface-mtu 0;\n";
|
|
}
|
|
|
|
if (!empty($wancfg['dhcprejectfrom'])) {
|
|
$dhclientconf .= " reject {$wancfg['dhcprejectfrom']};\n";
|
|
}
|
|
|
|
if (isset($wancfg['dhcpvlanprio'])) {
|
|
$dhclientconf .= " vlan-pcp {$wancfg['dhcpvlanprio']};\n";
|
|
}
|
|
|
|
$dhclientconf .= "}\n";
|
|
|
|
// DHCP Config File Advanced
|
|
if ($wancfg['adv_dhcp_config_advanced']) {
|
|
$dhclientconf = DHCP_Config_File_Advanced($interface, $wancfg, $wanif);
|
|
}
|
|
|
|
if (is_ipaddr($wancfg['alias-address'])) {
|
|
$subnetmask = gen_subnet_mask($wancfg['alias-subnet']);
|
|
$dhclientconf .= <<<EOD
|
|
alias {
|
|
interface "{$wanif}";
|
|
fixed-address {$wancfg['alias-address']};
|
|
option subnet-mask {$subnetmask};
|
|
}
|
|
|
|
EOD;
|
|
}
|
|
|
|
// DHCP Config File Override
|
|
if (!empty($wancfg['adv_dhcp_config_file_override'])) {
|
|
$dhclientfile = $wancfg['adv_dhcp_config_file_override_path'];
|
|
if (file_exists($dhclientfile)) {
|
|
$dhclientconf = file_get_contents($dhclientfile);
|
|
$dhclientconf = DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf);
|
|
} else {
|
|
log_msg("DHCP config file override does not exist: '{$dhclientfile}'", LOG_ERR);
|
|
}
|
|
}
|
|
|
|
fwrite($fd, $dhclientconf);
|
|
fclose($fd);
|
|
|
|
legacy_interface_flags($wanif, 'up');
|
|
|
|
mwexecf('/sbin/dhclient -c %s -p %s %s', array(
|
|
"/var/etc/dhclient_{$interface}.conf",
|
|
"/var/run/dhclient.{$wanif}.pid",
|
|
$wanif
|
|
));
|
|
}
|
|
|
|
function DHCP_Config_File_Advanced($interface, $wancfg, $wanif)
|
|
{
|
|
$hostname = "";
|
|
if ($wancfg['dhcphostname'] != '') {
|
|
$hostname = "\tsend host-name \"{$wancfg['dhcphostname']}\";\n";
|
|
}
|
|
|
|
/* DHCP Protocol Timings */
|
|
$protocol_timings = array ('adv_dhcp_pt_timeout' => "timeout", 'adv_dhcp_pt_retry' => "retry", 'adv_dhcp_pt_select_timeout' => "select-timeout", 'adv_dhcp_pt_reboot' => "reboot", 'adv_dhcp_pt_backoff_cutoff' => "backoff-cutoff", 'adv_dhcp_pt_initial_interval' => "initial-interval");
|
|
foreach ($protocol_timings as $Protocol_Timing => $PT_Name) {
|
|
$pt_variable = "{$Protocol_Timing}";
|
|
${$pt_variable} = "";
|
|
if ($wancfg[$Protocol_Timing] != "") {
|
|
${$pt_variable} = "\t{$PT_Name} {$wancfg[$Protocol_Timing]};\n";
|
|
}
|
|
}
|
|
|
|
$send_options = "";
|
|
if ($wancfg['adv_dhcp_send_options'] != '') {
|
|
$options = preg_split('/\s*,\s*(?=(?:[^"]*"[^"]*")*[^"]*$)/', $wancfg['adv_dhcp_send_options']);
|
|
foreach ($options as $option) {
|
|
$send_options .= "\tsend " . $option . ";\n";
|
|
}
|
|
}
|
|
|
|
$request_options = "";
|
|
if ($wancfg['adv_dhcp_request_options'] != '') {
|
|
$request_options = "\trequest {$wancfg['adv_dhcp_request_options']};\n";
|
|
}
|
|
|
|
$required_options = "";
|
|
if ($wancfg['adv_dhcp_required_options'] != '') {
|
|
$required_options = "\trequire {$wancfg['adv_dhcp_required_options']};\n";
|
|
}
|
|
|
|
$option_modifiers = "";
|
|
if ($wancfg['adv_dhcp_option_modifiers'] != '') {
|
|
$modifiers = preg_split('/\s*,\s*(?=(?:[^"]*"[^"]*")*[^"]*$)/', $wancfg['adv_dhcp_option_modifiers']);
|
|
foreach ($modifiers as $modifier) {
|
|
$option_modifiers .= "\t" . $modifier . ";\n";
|
|
}
|
|
}
|
|
|
|
$dhclientconf = "interface \"{$wanif}\" {\n";
|
|
$dhclientconf .= "\t# DHCP Protocol Timing Values\n";
|
|
$dhclientconf .= "{$adv_dhcp_pt_timeout}";
|
|
$dhclientconf .= "{$adv_dhcp_pt_retry}";
|
|
$dhclientconf .= "{$adv_dhcp_pt_select_timeout}";
|
|
$dhclientconf .= "{$adv_dhcp_pt_reboot}";
|
|
$dhclientconf .= "{$adv_dhcp_pt_backoff_cutoff}";
|
|
$dhclientconf .= "{$adv_dhcp_pt_initial_interval}";
|
|
$dhclientconf .= "\n\t# DHCP Protocol Options\n";
|
|
$dhclientconf .= "{$hostname}";
|
|
$dhclientconf .= "{$send_options}";
|
|
$dhclientconf .= "{$request_options}";
|
|
$dhclientconf .= "{$required_options}";
|
|
$dhclientconf .= "{$option_modifiers}";
|
|
$dhclientconf .= "\n\tscript \"/usr/local/opnsense/scripts/interfaces/dhclient-script\";\n";
|
|
if (empty($wancfg['dhcphonourmtu'])) {
|
|
$dhclientconf .= "\tsupersede interface-mtu 0;\n";
|
|
}
|
|
if (!empty($wancfg['dhcprejectfrom'])) {
|
|
$dhclientconf .= "\treject {$wancfg['dhcprejectfrom']};\n";
|
|
}
|
|
if (isset($wancfg['dhcpvlanprio'])) {
|
|
$dhclientconf .= "\tvlan-pcp {$wancfg['dhcpvlanprio']};\n";
|
|
}
|
|
$dhclientconf .= "}\n";
|
|
|
|
$dhclientconf = DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf);
|
|
|
|
return $dhclientconf;
|
|
}
|
|
|
|
function DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf)
|
|
{
|
|
/* Apply Interface Substitutions */
|
|
$dhclientconf = str_replace("{interface}", "{$wanif}", $dhclientconf);
|
|
|
|
/* Apply Hostname Substitutions */
|
|
$dhclientconf = str_replace("{hostname}", $wancfg['dhcphostname'], $dhclientconf);
|
|
|
|
/* Arrays of MAC Address Types, Cases, Delimiters */
|
|
/* ASCII or HEX, Upper or Lower Case, Various Delimiters (none, space, colon, hyphen, period) */
|
|
$various_mac_types = array("mac_addr_ascii", "mac_addr_hex");
|
|
$various_mac_cases = array("U", "L");
|
|
$various_mac_delimiters = array("", " ", ":", "-", ".");
|
|
|
|
/* Apply MAC Address Substitutions */
|
|
foreach ($various_mac_types as $various_mac_type) {
|
|
foreach ($various_mac_cases as $various_mac_case) {
|
|
foreach ($various_mac_delimiters as $various_mac_delimiter) {
|
|
$res = stripos($dhclientconf, $various_mac_type . $various_mac_case . $various_mac_delimiter);
|
|
if ($res !== false) {
|
|
/* Get MAC Address as ASCII String With Colon (:) Celimiters */
|
|
if ("$various_mac_case" == "U") {
|
|
$dhcpclientconf_mac = strtoupper(get_interface_mac($wanif));
|
|
}
|
|
if ("$various_mac_case" == "L") {
|
|
$dhcpclientconf_mac = strtolower(get_interface_mac($wanif));
|
|
}
|
|
|
|
if ("$various_mac_type" == "mac_addr_hex") {
|
|
/* Convert MAC ascii string to HEX with colon (:) delimiters. */
|
|
$dhcpclientconf_mac = str_replace(":", "", $dhcpclientconf_mac);
|
|
$dhcpclientconf_mac_hex = "";
|
|
$delimiter = "";
|
|
for ($i = 0; $i < strlen($dhcpclientconf_mac); $i++) {
|
|
$dhcpclientconf_mac_hex .= $delimiter . bin2hex($dhcpclientconf_mac[$i]);
|
|
$delimiter = ":";
|
|
}
|
|
$dhcpclientconf_mac = $dhcpclientconf_mac_hex;
|
|
}
|
|
|
|
/* MAC Address Delimiter Substitutions */
|
|
$dhcpclientconf_mac = str_replace(":", $various_mac_delimiter, $dhcpclientconf_mac);
|
|
|
|
/* Apply MAC Address Substitutions */
|
|
$dhclientconf = str_replace("{" . $various_mac_type . $various_mac_case . $various_mac_delimiter . "}", $dhcpclientconf_mac, $dhclientconf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $dhclientconf;
|
|
}
|
|
|
|
|
|
/* convert fxp0 -> wan, etc. */
|
|
function convert_real_interface_to_friendly_interface_name($interface = 'wan')
|
|
{
|
|
// search direct
|
|
$all_interfaces = legacy_config_get_interfaces();
|
|
foreach ($all_interfaces as $ifname => $ifcfg) {
|
|
if ($ifname == $interface || $ifcfg['if'] == $interface) {
|
|
return $ifname;
|
|
}
|
|
}
|
|
|
|
// search related
|
|
foreach (array_keys($all_interfaces) as $ifname) {
|
|
if (get_real_interface($ifname) == $interface) {
|
|
return $ifname;
|
|
}
|
|
}
|
|
|
|
if ($interface == 'enc0') {
|
|
return 'IPsec';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function convert_friendly_interface_to_friendly_descr($interface)
|
|
{
|
|
global $config;
|
|
|
|
$ifdesc = $interface;
|
|
|
|
switch ($interface) {
|
|
case 'l2tp':
|
|
$ifdesc = 'L2TP';
|
|
break;
|
|
case 'pptp':
|
|
$ifdesc = 'PPTP';
|
|
break;
|
|
case 'pppoe':
|
|
$ifdesc = 'PPPoE';
|
|
break;
|
|
case 'openvpn':
|
|
/* XXX practically unneeded as we are rendering virtual interfaces to the config */
|
|
$ifdesc = 'OpenVPN';
|
|
break;
|
|
case 'enc0':
|
|
case 'ipsec':
|
|
case 'IPsec':
|
|
/* XXX practically unneeded as we are rendering virtual interfaces to the config */
|
|
/* XXX it should also be noted that 'enc0' is the only proper way for this lookup */
|
|
$ifdesc = 'IPsec';
|
|
break;
|
|
default:
|
|
if (isset($config['interfaces'][$interface])) {
|
|
return !empty($config['interfaces'][$interface]['descr']) ?
|
|
$config['interfaces'][$interface]['descr'] : strtoupper($interface);
|
|
} elseif (strstr($interface, '_vip')) {
|
|
if (isset($config['virtualip']['vip'])) {
|
|
foreach ($config['virtualip']['vip'] as $counter => $vip) {
|
|
if ($vip['mode'] == 'carp') {
|
|
if ($interface == "{$vip['interface']}_vip{$vip['vhid']}") {
|
|
return "{$vip['descr']} ({$vip['subnet']})";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
foreach (legacy_config_get_interfaces(array('virtual' => false)) as $if => $ifcfg) {
|
|
if ($if == $interface || $ifcfg['descr'] == $interface) {
|
|
return $ifcfg['ifdescr'];
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return $ifdesc;
|
|
}
|
|
|
|
/* collect hardware device parents for VLAN, LAGG and bridges */
|
|
function interface_parent_devices($device, $as_interface = false)
|
|
{
|
|
$parents = [];
|
|
|
|
if (strstr($device, 'vlan') || strstr($device, 'qinq')) {
|
|
/* XXX maybe if we have a qinq type return both parents? */
|
|
foreach (config_read_array('vlans', 'vlan') as $vlan) {
|
|
if ($device == $vlan['vlanif']) {
|
|
$parents[] = $vlan['if'];
|
|
break;
|
|
}
|
|
}
|
|
} elseif (strstr($device, 'bridge')) {
|
|
foreach (config_read_array('bridges', 'bridged') as $bridge) {
|
|
if ($device == $bridge['bridgeif']) {
|
|
foreach (explode(',', $bridge['members'] ?? '') as $member) {
|
|
/* bridge stores members as configured interfaces */
|
|
$parents[] = $as_interface ? $member : get_real_interface($member);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} elseif (strstr($device, 'lagg')) {
|
|
foreach (config_read_array('laggs', 'lagg') as $lagg) {
|
|
if ($device == $lagg['laggif']) {
|
|
foreach (explode(',', $lagg['members']) as $member) {
|
|
$parents[] = $member;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $parents;
|
|
}
|
|
|
|
function interface_get_wireless_base($wlif)
|
|
{
|
|
if (!strstr($wlif, '_wlan')) {
|
|
return $wlif;
|
|
} else {
|
|
return substr($wlif, 0, stripos($wlif, '_wlan'));
|
|
}
|
|
}
|
|
|
|
function get_real_interface($interface = 'wan', $family = 'all')
|
|
{
|
|
global $config;
|
|
|
|
$realif = $interface;
|
|
|
|
switch ($interface) {
|
|
/* XXX legacy cruft starts here */
|
|
case 'enc0':
|
|
case 'openvpn':
|
|
case 'ppp':
|
|
break;
|
|
case 'ipsec':
|
|
$realif = 'enc0';
|
|
break;
|
|
/* XXX legacy cruft ends here */
|
|
default:
|
|
if (empty($config['interfaces'][$interface])) {
|
|
/* assume the interface exists to force an error elsewhere */
|
|
break;
|
|
}
|
|
|
|
$cfg = &config_read_array('interfaces', $interface);
|
|
|
|
/* set default for IPv4 and IPv6 lookups */
|
|
$realif = $cfg['if'];
|
|
|
|
if (isset($cfg['wireless']) && !strstr($realif, '_wlan')) {
|
|
$realif .= '_wlan0';
|
|
}
|
|
|
|
if ($family == 'inet6') {
|
|
switch ($cfg['ipaddrv6'] ?? 'none') {
|
|
case '6rd':
|
|
case '6to4':
|
|
$realif = "{$interface}_stf";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return $realif;
|
|
}
|
|
|
|
function guess_interface_from_ip($ipaddress)
|
|
{
|
|
if (is_ipaddrv4($ipaddress)) {
|
|
$family = "inet";
|
|
} elseif (is_ipaddrv6($ipaddress)) {
|
|
$family = "inet6";
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
/* create a route table we can search */
|
|
exec("/usr/bin/netstat -rnWf " . $family, $output, $ret);
|
|
|
|
/* search for the route with the largest subnet mask */
|
|
$largest_mask = 0;
|
|
$best_if = null;
|
|
|
|
foreach ($output as $line) {
|
|
$fields = preg_split("/\s+/", $line);
|
|
if (is_subnet($fields[0])) {
|
|
if (ip_in_subnet($ipaddress, $fields[0])) {
|
|
list($ip, $mask) = explode('/', $fields[0]);
|
|
if ($mask > $largest_mask) {
|
|
$best_if = $fields[5];
|
|
$largest_mask = $mask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($best_if)) {
|
|
return $best_if;
|
|
}
|
|
|
|
$ret = shell_safe('/sbin/route -n get %s | /usr/bin/awk \'/interface/ { print $2; };\'', $ipaddress);
|
|
if (empty($ret)) {
|
|
return false;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
function get_interface_number_track6($wanif, $targetif)
|
|
{
|
|
$list = link_interface_to_track6($wanif);
|
|
$number = 0;
|
|
|
|
foreach (array_keys($list) as $lanif) {
|
|
if ($lanif == $targetif) {
|
|
return $number;
|
|
}
|
|
$number += 1;
|
|
}
|
|
|
|
/* if we fail give backwards-compat */
|
|
return 0;
|
|
}
|
|
|
|
function link_interface_to_track6($wanif, $update = false)
|
|
{
|
|
$list = [];
|
|
|
|
if (empty($wanif)) {
|
|
return $list;
|
|
}
|
|
|
|
$wancfg = config_read_array('interfaces', $wanif);
|
|
|
|
if (!isset($wancfg['enable']) || empty($wancfg['ipaddrv6'])) {
|
|
return $list;
|
|
}
|
|
|
|
foreach (legacy_config_get_interfaces(['virtual' => false]) as $lanif => $lancfg) {
|
|
if (!isset($lancfg['enable']) || empty($lancfg['ipaddrv6'])) {
|
|
continue;
|
|
}
|
|
|
|
if ($lancfg['ipaddrv6'] == 'track6' && $lancfg['track6-interface'] == $wanif) {
|
|
$list[$lanif] = $lancfg;
|
|
}
|
|
}
|
|
|
|
if ($update) {
|
|
foreach ($list as $lanif => $lancfg) {
|
|
interface_track6_configure($lanif, $lancfg);
|
|
}
|
|
|
|
if (count($list)) {
|
|
plugins_configure('dhcp', false, ['inet6']);
|
|
}
|
|
}
|
|
|
|
return $list;
|
|
}
|
|
|
|
function interfaces_restart_by_device($verbose, $devices, $reconfigure = true)
|
|
{
|
|
$restartifs = [];
|
|
|
|
foreach (legacy_config_get_interfaces(['enable' => true, 'virtual' => false]) as $ifname => $ifparent) {
|
|
foreach ($devices as $device) {
|
|
if ($ifparent['if'] == $device) {
|
|
$restartifs[$ifname] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($reconfigure) {
|
|
foreach (array_keys($restartifs) as $ifname) {
|
|
interface_configure($verbose, $ifname);
|
|
}
|
|
}
|
|
|
|
system_routing_configure($verbose, array_keys($restartifs));
|
|
}
|
|
|
|
function link_interface_to_bridge($interface, $attach_device = null, $ifconfig_details = [/* must be set for attach to work */])
|
|
{
|
|
foreach (config_read_array('bridges', 'bridged') as $bridge) {
|
|
if (!in_array($interface, explode(',', $bridge['members'] ?? ''))) {
|
|
continue;
|
|
}
|
|
|
|
if ($attach_device) {
|
|
if (empty($ifconfig_details[$bridge['bridgeif']])) {
|
|
log_msg("Device {$attach_device} cannot be added to non-existent {$bridge['bridgeif']}, skipping now.");
|
|
break;
|
|
} elseif (empty($ifconfig_details[$attach_device])) {
|
|
log_msg("Device {$attach_device} was not found so it cannot be added to {$bridge['bridgeif']}, skipping now.");
|
|
break;
|
|
}
|
|
|
|
configure_interface_hardware($attach_device);
|
|
|
|
/*
|
|
* The MTU of the first member interface to be added is used as the bridge
|
|
* MTU. All additional members are required to have exactly the same MTU
|
|
* value.
|
|
*/
|
|
if ($ifconfig_details[$bridge['bridgeif']]['mtu'] != $ifconfig_details[$attach_device]['mtu']) {
|
|
legacy_interface_mtu($attach_device, $ifconfig_details[$bridge['bridgeif']]['mtu']);
|
|
}
|
|
|
|
legacy_interface_flags($attach_device, 'up');
|
|
legacy_bridge_member($bridge['bridgeif'], $attach_device);
|
|
}
|
|
|
|
return $bridge['bridgeif'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function link_interface_to_gre($interface, $update = false, $family = null)
|
|
{
|
|
global $config;
|
|
|
|
$aliaslist = get_configured_ip_aliases_list();
|
|
$result = [];
|
|
|
|
if (isset($config['gres']['gre'])) {
|
|
foreach ($config['gres']['gre'] as $gre) {
|
|
$parent = explode('_vip', $gre['if'])[0];
|
|
if (is_ipaddr($parent)) {
|
|
foreach ($aliaslist as $ip => $int) {
|
|
if ($ip == $parent) {
|
|
$parent = $int;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($parent != $interface) {
|
|
continue;
|
|
} elseif ($family == 4 && !is_ipaddrv4($gre['remote-addr'])) {
|
|
continue;
|
|
} elseif ($family == 6 && !is_ipaddrv6($gre['remote-addr'])) {
|
|
continue;
|
|
}
|
|
|
|
if ($update && empty(_interfaces_gre_configure($gre))) {
|
|
/* only return the ones that did configure correctly */
|
|
continue;
|
|
}
|
|
|
|
/* callers are only concerned with the resulting device names */
|
|
$result[] = $gre['greif'];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function link_interface_to_gif($interface, $update = false, $family = null)
|
|
{
|
|
global $config;
|
|
|
|
$result = [];
|
|
|
|
if (isset($config['gifs']['gif'])) {
|
|
foreach ($config['gifs']['gif'] as $gif) {
|
|
if (explode('_vip', $gif['if'])[0] != $interface) {
|
|
continue;
|
|
} elseif ($family == 4 && !is_ipaddrv4($gif['remote-addr'])) {
|
|
continue;
|
|
} elseif ($family == 6 && !is_ipaddrv6($gif['remote-addr'])) {
|
|
continue;
|
|
}
|
|
|
|
if ($update && empty(_interfaces_gif_configure($gif))) {
|
|
/* only return the ones that did configure correctly */
|
|
continue;
|
|
}
|
|
|
|
/* callers are only concerned with the resulting device names */
|
|
$result[] = $gif['gifif'];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function ip_in_interface_alias_subnet($interface, $ipalias)
|
|
{
|
|
global $config;
|
|
|
|
if (empty($interface) || !is_ipaddr($ipalias)) {
|
|
return false;
|
|
}
|
|
if (isset($config['virtualip']['vip'])) {
|
|
foreach ($config['virtualip']['vip'] as $vip) {
|
|
switch ($vip['mode']) {
|
|
case "ipalias":
|
|
if ($vip['interface'] != $interface) {
|
|
break;
|
|
}
|
|
$subnet = is_ipaddrv6($ipalias) ? gen_subnetv6($vip['subnet'], $vip['subnet_bits']) : gen_subnet($vip['subnet'], $vip['subnet_bits']);
|
|
if (ip_in_subnet($ipalias, $subnet . "/" . $vip['subnet_bits'])) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function get_interface_ip($interface, $ifconfig_details = null)
|
|
{
|
|
if (is_ipaddrv4($interface)) {
|
|
return $interface;
|
|
}
|
|
|
|
if (strstr($interface, '_vip')) {
|
|
foreach (config_read_array('virtualip', 'vip') as $vip) {
|
|
if ($vip['mode'] == 'carp') {
|
|
if ($interface == "{$vip['interface']}_vip{$vip['vhid']}" && is_ipaddrv4($vip['subnet'])) {
|
|
return $vip['subnet'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
list ($ip) = interfaces_primary_address($interface, $ifconfig_details);
|
|
|
|
return $ip;
|
|
}
|
|
|
|
function get_interface_ipv6($interface, $ifconfig_details = null, $mode = 'primary')
|
|
{
|
|
if (is_ipaddrv6($interface)) {
|
|
return $interface;
|
|
}
|
|
|
|
if (strstr($interface, '_vip')) {
|
|
foreach (config_read_array('virtualip', 'vip') as $vip) {
|
|
if ($vip['mode'] == 'carp') {
|
|
if ($interface == "{$vip['interface']}_vip{$vip['vhid']}" && is_ipaddrv6($vip['subnet'])) {
|
|
return $vip['subnet'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch ($mode) {
|
|
case 'routed':
|
|
list ($ipv6) = interfaces_routed_address6($interface, $ifconfig_details);
|
|
break;
|
|
case 'scoped':
|
|
list ($ipv6) = interfaces_scoped_address6($interface, $ifconfig_details);
|
|
break;
|
|
case 'primary':
|
|
default:
|
|
list ($ipv6) = interfaces_primary_address6($interface, $ifconfig_details);
|
|
break;
|
|
}
|
|
|
|
return $ipv6;
|
|
}
|
|
|
|
function get_interface_mac($interface, $ifconfig_details = null)
|
|
{
|
|
$intf_details = [];
|
|
|
|
if (empty($ifconfig_details)) {
|
|
$intf_details = legacy_interface_details($interface);
|
|
} elseif (!empty($ifconfig_details[$interface])) {
|
|
$intf_details = $ifconfig_details[$interface];
|
|
}
|
|
|
|
return $intf_details['macaddr'];
|
|
}
|
|
|
|
function get_vip_descr($ipaddress)
|
|
{
|
|
global $config;
|
|
|
|
foreach ($config['virtualip']['vip'] as $vip) {
|
|
if ($vip['subnet'] == $ipaddress) {
|
|
return ($vip['descr'] ?? '');
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function interfaces_staticarp_configure($if, $ifconfig_details = null)
|
|
{
|
|
global $config;
|
|
|
|
$ifcfg = $config['interfaces'][$if];
|
|
if (empty($ifcfg['if']) || !isset($ifcfg['enable'])) {
|
|
return;
|
|
}
|
|
|
|
if (empty($ifconfig_details)) {
|
|
$ifconfig_details = legacy_interfaces_details($ifcfg['if']);
|
|
}
|
|
|
|
if (empty($ifconfig_details[$ifcfg['if']])) {
|
|
return;
|
|
}
|
|
|
|
$have = in_array('staticarp', $ifconfig_details[$ifcfg['if']]['flags']);
|
|
$want = isset($config['dhcpd'][$if]['staticarp']);
|
|
|
|
if ($have !== $want) {
|
|
mwexecf('/sbin/ifconfig %s %sstaticarp', [$ifcfg['if'], $want ? '' : '-']);
|
|
mwexecf('/usr/sbin/arp -d -i %s -a', [$ifcfg['if']]);
|
|
}
|
|
|
|
interfaces_neighbors_configure($ifcfg['if'], $ifconfig_details);
|
|
}
|
|
|
|
function interfaces_neighbors_configure($device = null, $ifconfig_details = null)
|
|
{
|
|
$subnets = [];
|
|
|
|
if (!empty($device)) {
|
|
if (empty($ifconfig_details) || empty($ifconfig_details[$device])) {
|
|
/* when called with an interface, require $ifconfig_details being passed */
|
|
return;
|
|
}
|
|
foreach (['ipv4', 'ipv6'] as $proto) {
|
|
if (!empty($ifconfig_details[$device])) {
|
|
foreach ($ifconfig_details[$device][$proto] as $item) {
|
|
$subnets[] = $item['ipaddr'] . '/' . $item['subnetbits'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$current_neightbors = [];
|
|
foreach ((new \OPNsense\Interfaces\Neighbor())->neighbor->iterateItems() as $key => $node) {
|
|
$found = empty($if); /* unfiltered when no $if provided */
|
|
foreach ($subnets as $subnet) {
|
|
$found = ip_in_subnet((string)$node->ipaddress, $subnet);
|
|
if ($found) {
|
|
break;
|
|
}
|
|
}
|
|
if ($found) {
|
|
// IPv4 [arp] or IPv6 [ndp]
|
|
if (strpos($node->ipaddress, ":") === false) {
|
|
mwexecf('/usr/sbin/arp -s %s %s', [$node->ipaddress, $node->etheraddr]);
|
|
} else {
|
|
mwexecf('/usr/sbin/ndp -s %s %s', [$node->ipaddress, $node->etheraddr]);
|
|
}
|
|
}
|
|
$current_neightbors[] = (string)$node->ipaddress;
|
|
}
|
|
|
|
/* persist accounted addresses, without a cleanup that would be all seen since last cleanup */
|
|
$fobj = new \OPNsense\Core\FileObject('/tmp/interfaces_neighbors.json', 'a+');
|
|
$current = $fobj->readJson() ?? [];
|
|
$fobj->truncate(0)->writeJson(
|
|
!empty($device) ? array_unique(array_merge($current_neightbors, $current)) : $current_neightbors
|
|
);
|
|
unset($fobj);
|
|
/* only cleanup when applying all interfaces */
|
|
if (empty($device) && is_array($current)) {
|
|
foreach ($current as $item) {
|
|
if (is_string($item) && is_ipaddr($item) && !in_array($item, $current_neightbors)) {
|
|
if (strpos($item, ":") === false) {
|
|
mwexecf('/usr/sbin/arp -d %s', [$item]);
|
|
} else {
|
|
mwexecf('/usr/sbin/ndp -d %s', [$item]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function convert_seconds_to_hms($sec)
|
|
{
|
|
$min = $hrs = 0;
|
|
if ($sec != 0) {
|
|
$min = floor($sec / 60);
|
|
$sec %= 60;
|
|
}
|
|
if ($min != 0) {
|
|
$hrs = floor($min / 60);
|
|
$min %= 60;
|
|
}
|
|
if ($sec < 10) {
|
|
$sec = "0" . $sec;
|
|
}
|
|
if ($min < 10) {
|
|
$min = "0" . $min;
|
|
}
|
|
if ($hrs < 10) {
|
|
$hrs = "0" . $hrs;
|
|
}
|
|
$result = $hrs . ":" . $min . ":" . $sec;
|
|
return $result;
|
|
}
|
|
|
|
/****f* legacy/is_ipaddr_configured
|
|
* NAME
|
|
* is_ipaddr_configured
|
|
* INPUTS
|
|
* IP Address to check.
|
|
* RESULT
|
|
* returns true if the IP Address is
|
|
* configured and present on this device.
|
|
*/
|
|
function is_ipaddr_configured($ipaddr, $ignore_if = '')
|
|
{
|
|
$if = get_real_interface($ignore_if);
|
|
$interface_list_ips = get_configured_ip_addresses();
|
|
|
|
if (empty($interface_list_ips[$ipaddr]) || $interface_list_ips[$ipaddr] == $if) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function interfaces_addresses($interfaces, $as_subnet = false, $ifconfig_details = null)
|
|
{
|
|
global $config;
|
|
|
|
$ifcache = [];
|
|
$realifs = [];
|
|
$result = [];
|
|
|
|
if (!is_array($interfaces)) {
|
|
$interfaces = [$interfaces];
|
|
}
|
|
|
|
foreach ($interfaces as $interface) {
|
|
if (isset($config['interfaces'][$interface])) {
|
|
foreach (['all', 'inet6'] as $family) {
|
|
$tmpif = get_real_interface($interface, $family);
|
|
if (empty($config['interfaces'][$interface]['virtual'])) {
|
|
$ifcache[$tmpif] = $interface;
|
|
}
|
|
$realifs[] = $tmpif;
|
|
}
|
|
} else {
|
|
/* take interfaces as is */
|
|
$realifs[] = $interface;
|
|
}
|
|
}
|
|
|
|
if (!count($realifs)) {
|
|
return $result;
|
|
}
|
|
|
|
$realifs = array_unique($realifs);
|
|
if (!empty($ifconfig_details)) {
|
|
$intf_details = $ifconfig_details;
|
|
} else {
|
|
$intf_details = count($realifs) > 1 ? legacy_interfaces_details() : legacy_interfaces_details($realifs[0]);
|
|
}
|
|
|
|
foreach ($realifs as $realif) {
|
|
foreach (['ipv4', 'ipv6'] as $proto) {
|
|
if (empty($intf_details[$realif][$proto])) {
|
|
continue;
|
|
}
|
|
foreach ($intf_details[$realif][$proto] as $address) {
|
|
if (empty($address['ipaddr'])) {
|
|
continue;
|
|
}
|
|
$scope = '';
|
|
if (!empty($address['link-local'])) {
|
|
$scope = "%{$realif}";
|
|
}
|
|
$suffix = '';
|
|
if ($as_subnet) {
|
|
if (empty($address['subnetbits'])) {
|
|
continue;
|
|
}
|
|
$suffix = "/{$address['subnetbits']}";
|
|
$scope = '';
|
|
}
|
|
$key = "{$address['ipaddr']}{$scope}{$suffix}";
|
|
$result[$key] = [
|
|
'address' => $address['ipaddr'],
|
|
'alias' => false,
|
|
'autoconf' => !empty($address['autoconf']),
|
|
'bind' => true,
|
|
'bits' => $address['subnetbits'],
|
|
'deprecated' => !empty($address['deprecated']),
|
|
'detached' => !empty($address['detached']),
|
|
'tentative' => !empty($address['tentative']),
|
|
'family' => $proto == 'ipv4' ? 'inet' : 'inet6',
|
|
'interface' => !empty($ifcache[$realif]) ? $ifcache[$realif] : null,
|
|
'key' => $key,
|
|
'name' => $realif,
|
|
'scope' => !empty($address['link-local']),
|
|
'unique' => ($proto == 'ipv4' || !empty($address['link-local'])) ? false : is_uniquelocal($address['ipaddr']),
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($result as &$info) {
|
|
foreach (config_read_array('virtualip', 'vip') as $vip) {
|
|
if (empty($info['interface']) || $info['interface'] != $vip['interface']) {
|
|
continue;
|
|
}
|
|
|
|
if ($vip['mode'] != 'ipalias' && $vip['mode'] != 'carp') {
|
|
continue;
|
|
}
|
|
|
|
$match = false;
|
|
|
|
if ($info['family'] == 'inet' && strpos($vip['subnet'], ':') === false) {
|
|
$match = $vip['subnet'] == $info['address'];
|
|
} elseif ($info['family'] == 'inet6' && strpos($vip['subnet'], ':') !== false) {
|
|
/*
|
|
* Since we do not know what subnet value was given by user
|
|
* uncompress/compress to match correctly compressed system
|
|
* value.
|
|
*/
|
|
$match = Net_IPv6::compress(Net_IPv6::uncompress($vip['subnet'])) == $info['address'];
|
|
}
|
|
|
|
if (!$match) {
|
|
continue;
|
|
}
|
|
|
|
$info['alias'] = true;
|
|
|
|
if (!empty($vip['nobind'])) {
|
|
$info['bind'] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* move ULAs to the bottom to prefer GUA addresses */
|
|
uasort($result, function ($a, $b) {
|
|
return $a['unique'] - $b['unique'];
|
|
});
|
|
|
|
return $result;
|
|
}
|
|
|
|
function interfaces_has_prefix_only($interface)
|
|
{
|
|
$interfaces_a = config_read_array('interfaces');
|
|
$ret = false;
|
|
|
|
switch ($interfaces_a[$interface]['ipaddrv6'] ?? 'none') {
|
|
case 'dhcp6':
|
|
$ret = empty($interfaces_a[$interface]['adv_dhcp6_config_file_override']) &&
|
|
((!empty($interfaces_a[$interface]['adv_dhcp6_config_advanced']) &&
|
|
empty($interfaces_a[$interface]['adv_dhcp6_id_assoc_statement_address_enable']) &&
|
|
!isset($interfaces_a[$interface]['dhcp6-prefix-id'])) ||
|
|
(isset($interfaces_a[$interface]['dhcp6prefixonly']) &&
|
|
!isset($interfaces_a[$interface]['dhcp6-prefix-id'])));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
function interfaces_primary_address($interface, $ifconfig_details = null)
|
|
{
|
|
/* primary returns preferred local address according to configuration */
|
|
$ifcfgip = $network = $subnetbits = $device = null;
|
|
|
|
foreach (interfaces_addresses($interface, false, $ifconfig_details) as $addr) {
|
|
if ($addr['family'] != 'inet') {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* In IPv4 the strict ordering for addresses is ensured so that
|
|
* we do include IP alias or CARP addresses. If the need arises
|
|
* to get to a "dynamic" primary address we could add another
|
|
* argument for callers to enforce alias exclusion.
|
|
*/
|
|
|
|
$network = gen_subnet($addr['address'], $addr['bits']) . "/{$addr['bits']}";
|
|
$subnetbits = $addr['bits'];
|
|
$ifcfgip = $addr['address'];
|
|
$device = $addr['name'];
|
|
break; /* all done */
|
|
}
|
|
|
|
return [ $ifcfgip, $network, $subnetbits, $device ];
|
|
}
|
|
|
|
function _interfaces_primary_address6($interface, $ifconfig_details = null, $allow_track = true, $link_local = false)
|
|
{
|
|
$ifcfgipv6 = $networkv6 = $subnetbitsv6 = $devicev6 = null;
|
|
|
|
if ($allow_track && !$link_local && interfaces_has_prefix_only($interface)) {
|
|
/* extend the search scope for a non-NA mode to tracking interfaces */
|
|
$interface = array_merge([$interface], array_keys(link_interface_to_track6($interface)));
|
|
}
|
|
|
|
foreach (interfaces_addresses($interface, false, $ifconfig_details) as $addr) {
|
|
/* XXX consider excluding 'autoconf', but only when it's not in SLAAC mode */
|
|
if ($addr['family'] != 'inet6' || $addr['deprecated'] || $addr['detached'] || $addr['tentative'] || $addr['alias']) {
|
|
continue;
|
|
}
|
|
|
|
if ($link_local && !$addr['scope']) {
|
|
continue;
|
|
} elseif (!$link_local && $addr['scope']) {
|
|
continue;
|
|
}
|
|
|
|
$networkv6 = gen_subnetv6($addr['address'], $addr['bits']) . "/{$addr['bits']}";
|
|
$subnetbitsv6 = $addr['bits'];
|
|
$ifcfgipv6 = $addr['address'];
|
|
if ($link_local) {
|
|
$ifcfgipv6 .= "%{$addr['name']}";
|
|
}
|
|
$devicev6 = $addr['name'];
|
|
break; /* all done */
|
|
}
|
|
|
|
return [ $ifcfgipv6, $networkv6, $subnetbitsv6, $devicev6 ];
|
|
}
|
|
|
|
function interfaces_routed_address6($interface, $ifconfig_details = null)
|
|
{
|
|
/* "routed" returns a non-link-local address only, possiby derived from tracking interfaces */
|
|
return _interfaces_primary_address6($interface, $ifconfig_details, true, false);
|
|
}
|
|
|
|
function interfaces_scoped_address6($interface, $ifconfig_details = null)
|
|
{
|
|
/* "scoped" returns own link-local address only */
|
|
return _interfaces_primary_address6($interface, $ifconfig_details, false, true);
|
|
}
|
|
|
|
function interfaces_primary_address6($interface, $ifconfig_details = null)
|
|
{
|
|
/* primary returns preferred local address according to configuration */
|
|
$ifcfgipv6 = $networkv6 = $subnetbitsv6 = null;
|
|
|
|
if (interfaces_has_prefix_only($interface)) {
|
|
return _interfaces_primary_address6($interface, $ifconfig_details, false, true);
|
|
}
|
|
|
|
return _interfaces_primary_address6($interface, $ifconfig_details, false, false);
|
|
}
|