Skip to content

Commit

Permalink
Interfaces: Other Types: VLAN - Add stacked VLAN support (IEEE 802.1a…
Browse files Browse the repository at this point in the history
…d / QinQ) (#5607)

* Interfaces: Other Types: VLAN - Add stacked VLAN support (IEEE 802.1ad / QinQ) for #5560

This commit adds QinQ to the new VLAN MVC implementation.

o Implement new interface nameing, vlan_XXX for vlans and qinq_XXX for QinQ interfaces, to keep the names static its much easier to cope with changes.
o For backwards compatibilty keep existing interface naming for old entries.
o interfaces_vlan_configure() is only called during bootup and should enforce proper ordering in device creation (vlans first stacked later)
o interface_vlan_configure() remove pcp tricker around 0 as the model will enforce zero's anyway
o lock vlan when being used in QinQ interfaces
  • Loading branch information
AdSchellevis committed Mar 2, 2022
1 parent 2164310 commit 99aacf7
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
16 changes: 12 additions & 4 deletions src/etc/inc/interfaces.inc
Expand Up @@ -182,7 +182,16 @@ function interfaces_vlan_configure($verbose = false)
echo 'Configuring VLAN interfaces...';
flush();
}

// 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;
}
});
foreach ($config['vlans']['vlan'] as $vlan) {
interface_vlan_configure($vlan);
}
Expand All @@ -196,12 +205,11 @@ function interface_vlan_configure($vlan)
{
interfaces_bring_up($vlan['if']); /* XXX overreach? */

/* XXX avoid destroy/create */
/* destroy is a safety precaution, when confguring via api or gui this function should only be called on new vlans */
legacy_interface_destroy($vlan['vlanif']);
legacy_interface_create('vlan', $vlan['vlanif']);

$pcp = isset($vlan['pcp']) ? $vlan['pcp'] : 0;
legacy_vlan_tag($vlan['vlanif'], $vlan['if'], $vlan['tag'], $pcp);
legacy_vlan_tag($vlan['vlanif'], $vlan['if'], $vlan['tag'], $vlan['pcp']);

interfaces_bring_up($vlan['vlanif']);
}
Expand Down
Expand Up @@ -38,10 +38,28 @@ class VlanSettingsController extends ApiMutableModelControllerBase
protected static $internalModelName = 'vlan';
protected static $internalModelClass = 'OPNsense\Interfaces\Vlan';

private function generateVlanIfName()
private function generateVlanIfName($current=null)
{
$tmp = $this->request->getPost("vlan");
return "{$tmp['if']}_vlan{$tmp['tag']}";
$prefix = (strpos($tmp['if'], 'vlan') === false ? "vlan" : "qinq");
if ($current != null && (string)$current->vlanif == "{$tmp['if']}_vlan{$tmp['tag']}") {
// keep legacy naming
return "{$tmp['if']}_vlan{$tmp['tag']}";
} elseif ($current != null && strpos((string)$current->vlanif, '_vlan') === false &&
strpos((string)$current->vlanif, $prefix) === 0
) {
// new naming convention and same type, name stays the same
return (string)$current->vlanif;
} else {
// autonumber new
$ifid = 0;
foreach ($this->getModel()->vlan->iterateItems() as $node) {
if (strpos((string)$node->vlanif . "_", $prefix) === 0) {
$ifid = max($ifid, (int)explode("_", (string)$node->vlanif)[1]);
}
}
return $prefix . "_" . ($ifid+1);
}
}

private function interfaceAssigned($if)
Expand All @@ -66,8 +84,23 @@ public function setItemAction($uuid)
{
$node = $this->getModel()->getNodeByReference('vlan.' . $uuid);
$old_vlanif = $node != null ? (string)$node->vlanif : null;
$new_vlanif = $this->generateVlanIfName();
if ($old_vlanif != null && $old_vlanif != $new_vlanif && $this->interfaceAssigned($old_vlanif)) {
$new_vlanif = $this->generateVlanIfName($node);
$children = 0;
foreach ($this->getModel()->vlan->iterateItems() as $node) {
if ((string)$node->if == $old_vlanif) {
$children++;
}
}
if ($old_vlanif != null && $old_vlanif != $new_vlanif && $children > 0) {
$result = [
"result" => "failed",
"validations" => [
"vlan.vlanif" => gettext("This VLAN cannot be deleted because it is used in QinQ interfaces.")
]
];
} elseif ($old_vlanif != null && $old_vlanif != $new_vlanif && $this->interfaceAssigned($old_vlanif)) {
// Reassignment is only an issue when naming changes. These additional validations only apply
// for legacy interface nameing (e.g. <interface>_vlan_<tag>) and type changes vlan verses qinq.
$tmp = $this->request->getPost("vlan");
if ($tmp['tag'] != (string)$node->tag) {
$result = [
Expand Down Expand Up @@ -110,7 +143,15 @@ public function delItemAction($uuid)
{
$node = $this->getModel()->getNodeByReference('vlan.' . $uuid);
$old_vlanif = $node != null ? (string)$node->vlanif : null;
if ($old_vlanif != null && $this->interfaceAssigned($old_vlanif)) {
$children = 0;
foreach ($this->getModel()->vlan->iterateItems() as $node) {
if ((string)$node->if == $old_vlanif) {
$children++;
}
}
if ($children > 0) {
throw new UserException(gettext("This VLAN cannot be deleted because it is used in QinQ interfaces."));
} elseif ($old_vlanif != null && $this->interfaceAssigned($old_vlanif)) {
throw new UserException(gettext("This VLAN cannot be deleted because it is assigned as an interface."));
} else {
$result = $this->delBase("vlan", $uuid);
Expand Down
Expand Up @@ -52,9 +52,7 @@ protected function actionPostLoadingEvent()
$ifconfig = json_decode((new Backend())->configdRun('interface list ifconfig'), true);
if (!empty($ifconfig)) {
foreach ($ifconfig as $ifname => $details) {
// XXX: skip same interface types as legacy, may need to revise later
if (
strpos($ifname, "_vlan") > 1 || strpos($ifname, "lo") === 0 || strpos($ifname, "enc") === 0 ||
if (strpos($ifname, "qinq") === 0 || strpos($ifname, "lo") === 0 || strpos($ifname, "enc") === 0 ||
strpos($ifname, "pflog") === 0 || strpos($ifname, "pfsync") === 0 ||
strpos($ifname, "bridge") === 0 ||
strpos($ifname, "gre") === 0 || strpos($ifname, "gif") === 0 || strpos($ifname, "ipsec") === 0
Expand Down

0 comments on commit 99aacf7

Please sign in to comment.