VXLAN support (#3787)

* VXLAN: hook vxlan_configure_interface to newwanip event, move interface (address) configuration responisibilty there as well. This might be a better spot for other device setups as well.
This commit is contained in:
Ad Schellevis 2019-11-01 21:25:27 +01:00 committed by GitHub
parent c20b0083ef
commit 6628ae2e40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 478 additions and 0 deletions

9
plist
View File

@ -45,6 +45,7 @@
/usr/local/etc/inc/plugins.inc.d/squid.inc
/usr/local/etc/inc/plugins.inc.d/suricata.inc
/usr/local/etc/inc/plugins.inc.d/unbound.inc
/usr/local/etc/inc/plugins.inc.d/vxlan.inc
/usr/local/etc/inc/plugins.inc.d/webgui.inc
/usr/local/etc/inc/rrd.inc
/usr/local/etc/inc/services.inc
@ -254,6 +255,9 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/Api/LegacySubsystemController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/KeyPairsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/IPsec/forms/dialogKeyPair.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VxlanSettingsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/VxlanController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVxlan.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
@ -419,6 +423,10 @@
/usr/local/opnsense/mvc/app/models/OPNsense/IPsec/IPsec.php
/usr/local/opnsense/mvc/app/models/OPNsense/IPsec/IPsec.xml
/usr/local/opnsense/mvc/app/models/OPNsense/IPsec/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/VxLan.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/VxLan.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_0.php
@ -469,6 +477,7 @@
/usr/local/opnsense/mvc/app/views/OPNsense/Firewall/alias_util.volt
/usr/local/opnsense/mvc/app/views/OPNsense/IDS/index.volt
/usr/local/opnsense/mvc/app/views/OPNsense/IPsec/key_pairs.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vxlan.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt
/usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/export.volt

View File

@ -904,6 +904,9 @@ function interfaces_configure($verbose = false)
/* special interfaces on top of newly generated devices */
$special[] = $if;
continue;
} elseif (strstr($ifcfg['if'], 'vxlan.')) {
// XXX device configuration is responsible for interface setup too when trying to init all.
continue;
}
$is_track6 = !empty($ifcfg['ipaddrv6']) && $ifcfg['ipaddrv6'] == 'track6';
@ -958,6 +961,7 @@ function interfaces_configure($verbose = false)
/* XXX temporary plugin spot */
plugins_configure('ipsec_prepare', $verbose);
plugins_configure('vxlan_prepare', $verbose);
interfaces_group_setup();
}

View File

@ -337,6 +337,17 @@ function legacy_interfaces_details($intf = null)
"advbase" => $line_parts[5],
"advskew" => $line_parts[7]
);
} elseif (strpos($line, "\tvxlan") !== false) {
if (empty($result[$current_interface]["vxlan"])) {
$result[$current_interface]["vxlan"] = array();
}
$result[$current_interface]["vxlan"]["vni"] = $line_parts[2];
$result[$current_interface]["vxlan"]["local"] = $line_parts[4];
if ($line_parts[5] == "group") {
$result[$current_interface]["vxlan"]["group"] = $line_parts[6];
} else {
$result[$current_interface]["vxlan"]["remote"] = $line_parts[6];
}
}
}

View File

@ -129,6 +129,7 @@ function core_devices()
$devices[] = array('pattern' => '_stf', 'volatile' => true);
$devices[] = array('pattern' => '_wlan', 'volatile' => true);
$devices[] = array('pattern' => 'vlan', 'volatile' => true);
$devices[] = array('pattern' => 'vxlan', 'volatile' => true);
return $devices;
}

View File

@ -0,0 +1,130 @@
<?php
/*
* Copyright (C) 2019 Deciso B.V.
* 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.
*/
function vxlan_configure()
{
return array(
'vxlan_prepare' => array('vxlan_configure_interface'),
'newwanip' => array('vxlan_configure_interface'),
);
}
function vxlan_configure_interface($verbose=false)
{
$cnf = OPNsense\Core\Config::getInstance()->object();
$interfaces_details = legacy_interfaces_details();
$configured_devices = array();
$changed_devices = array();
$vxlans = iterator_to_array((new \OPNsense\Interfaces\VxLan())->vxlans->vxlan->iterateItems());
if ($verbose && !empty($vxlans)) {
echo 'Configuring VXLAN interfaces...';
flush();
}
$all_addresses = array();
$known_addresses = array();
foreach ($interfaces_details as $intf) {
foreach (['ipv4', 'ipv6'] as $ipproto) {
if (!empty($intf[$ipproto])) {
foreach ($intf[$ipproto] as $net) {
$known_addresses[] = $net['ipaddr'];
}
}
}
}
// (re)configure vxlan devices
foreach ($vxlans as $vxlan) {
if (!in_array((string)$vxlan->vxlanlocal, $known_addresses)) {
// skip when interface address is not assigned (yet)
continue;
}
$device_name = "vxlan.{$vxlan->deviceId}" ;
$configured_devices[] = $device_name;
$current_settings = array(
"vxlanid" => null,
"vxlanlocal" => null,
"vxlanremote" => null,
"vxlangroup" => null
);
if (empty($interfaces_details[$device_name])) {
// new device
mwexecf('/sbin/ifconfig vxlan create name %s', array($device_name));
$isChanged = true;
} else {
$isChanged = false;
$current_settings['vxlanid'] = $interfaces_details[$device_name]['vxlan']['vni'];
$current_settings['vxlanlocal'] = explode(":", $interfaces_details[$device_name]['vxlan']['local'])[0];
$current_settings['vxlanremote'] = explode(":", $interfaces_details[$device_name]['vxlan']['remote'])[0];
$current_settings['vxlangroup'] = explode(":", $interfaces_details[$device_name]['vxlan']['group'])[0];
}
// gather settings, detect changes
$ifcnfcmd = '/sbin/ifconfig %s';
$ifcnfcmdp = array($device_name);
foreach (array('vxlanid', 'vxlanlocal', 'vxlanremote', 'vxlangroup', 'vxlandev') as $param) {
$value = '';
if ($param == 'vxlandev') {
$intfnm = (string)$vxlan->$param;
if (!empty($cnf->interfaces->$intfnm)) {
$value = $cnf->interfaces->$intfnm->if->__toString();
}
} else {
$value = (string)$vxlan->$param;
}
if ($value != '') {
$ifcnfcmd .= " {$param} %s ";
$ifcnfcmdp[] = $value;
}
if (isset($current_settings[$param]) && $current_settings[$param] != $value) {
$isChanged = true;
}
}
if ($isChanged) {
mwexecf($ifcnfcmd, $ifcnfcmdp);
$changed_devices[] = $device_name;
}
}
// destroy non existing interfaces
foreach ($interfaces_details as $intf => $data) {
if (strpos($intf, "vxlan.") === 0) {
if (!in_array($intf, $configured_devices)) {
mwexecf('/sbin/ifconfig %s destroy', array($intf));
}
}
}
if ($verbose && !empty($vxlans)) {
echo "done.\n";
}
// configure interface when device has changed
foreach ($changed_devices as $device_name) {
$friendly_if = convert_real_interface_to_friendly_interface_name($device_name);
if (!empty($friendly_if)) {
interface_configure($verbose, $friendly_if);
}
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* Copyright (C) 2019 Deciso B.V.
* 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.
*/
namespace OPNsense\Interfaces\Api;
use \OPNsense\Core\Backend;
use \OPNsense\Base\ApiMutableModelControllerBase;
class VxlanSettingsController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'vxlan';
protected static $internalModelClass = 'OPNsense\Interfaces\VxLan';
public function searchItemAction()
{
return $this->searchBase("vxlans.vxlan", array(
'enabled', 'deviceId', 'vxlanid', 'vxlanlocal', 'vxlanremote', 'vxlangroup', 'vxlandev'
), "vxlanid");
}
public function setItemAction($uuid)
{
return $this->setBase("vxlan", "vxlans.vxlan", $uuid);
}
public function addItemAction()
{
return $this->addBase("vxlan", "vxlans.vxlan");
}
public function getItemAction($uuid = null)
{
return $this->getBase("vxlan", "vxlans.vxlan", $uuid);
}
public function delItemAction($uuid)
{
return $this->delBase("vxlans.vxlan", $uuid);
}
public function toggleItemAction($uuid, $enabled = null)
{
return $this->toggleBase("vxlans.vxlan", $uuid, $enabled);
}
public function reconfigureAction()
{
$result = array("status" => "failed");
if ($this->request->isPost()) {
$backend = new Backend();
$result['status'] = strtolower(trim($backend->configdRun('interface vxlan configure')));
}
return $result;
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* Copyright (C) 2019 Deciso B.V.
* 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.
*/
namespace OPNsense\Interfaces;
class VxLanController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->pick('OPNsense/Interface/vxlan');
$this->view->formDialogVxlan = $this->getForm("dialogVxlan");
}
}

View File

@ -0,0 +1,49 @@
<form>
<field>
<id>vxlan.deviceId</id>
<label>deviceId</label>
<type>info</type>
</field>
<field>
<id>vxlan.vxlanid</id>
<label>VNI</label>
<type>text</type>
<help>VXLAN Network Identifier (VNI) that identifies the virtual network segment membership of the interface.</help>
</field>
<field>
<id>vxlan.vxlanlocal</id>
<label>Source address</label>
<type>text</type>
<help>
The source address used in the encapsulating IPv4/IPv6 header.
The address should already be assigned to an existing interface.
When the interface is configured in unicast mode, the listening socket is bound to this address.
</help>
</field>
<field>
<id>vxlan.vxlanremote</id>
<label>Remote address</label>
<type>text</type>
<help>
The interface can be configured in a unicast, or point-to-point, mode to create a tunnel between two hosts.
This is the IP address of the remote end of the tunnel.
</help>
</field>
<field>
<id>vxlan.vxlangroup</id>
<label>Multicast group</label>
<type>text</type>
<help>
The interface can be configured in a multicast mode to create a virtual network of hosts.
This is the IP multicast group address the interface will join.
</help>
</field>
<field>
<id>vxlan.vxlandev</id>
<label>Device</label>
<type>dropdown</type>
<help>
When the interface is configured in multicast mode, the dev interface is used to transmit IP multicast packets.
</help>
</field>
</form>

View File

@ -0,0 +1,9 @@
<acl>
<page-interfaces-vxlan>
<name>Interfaces: VXLAN</name>
<patterns>
<pattern>ui/interfaces/vxlan</pattern>
<pattern>api/interfaces/vxlan</pattern>
</patterns>
</page-interfaces-vxlan>
</acl>

View File

@ -0,0 +1,7 @@
<menu>
<Interfaces>
<Types>
<VXLAN url="/ui/interfaces/vxlan"/>
</Types>
</Interfaces>
</menu>

View File

@ -0,0 +1,35 @@
<?php
/*
* Copyright (C) 2019 Deciso B.V.
* 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.
*/
namespace OPNsense\Interfaces;
use OPNsense\Base\BaseModel;
class VxLan extends BaseModel
{
}

View File

@ -0,0 +1,35 @@
<model>
<mount>//OPNsense/Interfaces</mount>
<description>
the OPNsense "GridExample" application
</description>
<items>
<vxlans>
<vxlan type="ArrayField">
<deviceId type="AutoNumberField">
<Required>Y</Required>
</deviceId>
<vxlanid type="IntegerField">
<MinimumValue>0</MinimumValue>
<MaximumValue>16777215</MaximumValue>
<Required>Y</Required>
</vxlanid>
<vxlanlocal type="NetworkField">
<Required>Y</Required>
<NetMaskAllowed>N</NetMaskAllowed>
</vxlanlocal>
<vxlanremote type="NetworkField">
<Required>N</Required>
<NetMaskAllowed>N</NetMaskAllowed>
</vxlanremote>
<vxlangroup type="NetworkField">
<Required>N</Required>
<NetMaskAllowed>N</NetMaskAllowed>
</vxlangroup>
<vxlandev type="InterfaceField">
<Required>N</Required>
</vxlandev>
</vxlan>
</vxlans>
</items>
</model>

View File

@ -0,0 +1,64 @@
<script>
$( document ).ready(function() {
$("#grid-addresses").UIBootgrid(
{ search:'/api/interfaces/vxlan_settings/searchItem/',
get:'/api/interfaces/vxlan_settings/getItem/',
set:'/api/interfaces/vxlan_settings/setItem/',
add:'/api/interfaces/vxlan_settings/addItem/',
del:'/api/interfaces/vxlan_settings/delItem/'
}
);
$("#reconfigureAct").click(function(){
$("#reconfigureAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall("/api/interfaces/vxlan_settings/reconfigure", {}, function(data,status) {
// when done, disable progress animation.
$("#reconfigureAct_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Error reconfiguring vxlan') }}",
message: data['status'],
draggable: true
});
}
});
});
});
</script>
<div class="tab-content content-box">
<table id="grid-addresses" class="table table-condensed table-hover table-striped" data-editDialog="DialogVxlan" data-editAlert="VxLanChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="deviceId" data-type="string">{{ lang._('DeviceId') }}</th>
<th data-column-id="vxlanid" data-type="string">{{ lang._('VNI') }}</th>
<th data-column-id="vxlanlocal" data-type="string">{{ lang._('Source') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div class="col-md-12">
<div id="VxLanChangeMessage" class="alert alert-info" style="display: none" role="alert">
{{ lang._('After changing settings, please remember to apply them with the button below') }}
</div>
<hr/>
<button class="btn btn-primary" id="reconfigureAct" type="button"><b>{{ lang._('Apply') }}</b> <i id="reconfigureAct_progress" class=""></i></button>
<br/><br/>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogVxlan,'id':'DialogVxlan','label':lang._('Edit VxLan')])}}

View File

@ -86,3 +86,8 @@ type: script
command:/usr/local/opnsense/scripts/routes/gateways.php
type:script_output
message:list gateways
[vxlan.configure]
command: /usr/local/sbin/pluginctl -c vxlan_prepare
message: Reconfiguring vxlan
type: script