Enabling Secondary IPs and/or VLANs on the WAN interface

MetalSoft supports configuring secondary IPs and VLANs on the WAN interface. This enables complex setups such as the one pictured below (as an example) where the primary IP is a private (non-routable IP) and a secondary IP is configured on a VLAN interface that is bridged to the primary interface.

Note that this is only supported on certain switch vendors (such as Cumulus 4.x+) and certain provisioners (such as the VLAN provisioner). The untagged traffic is handled by the default VLAN (automatically allocated by MetalSoft in this case) whereas the secondary VLAN is allowed by the switch port configuration.

To enable this scenario follow the following:

  1. From the Admin panel, in the Subnets section click Add subnet pool button. Register a Subnet Pool that will be used to allocate IPs on primary or secondary interfaces. Make sure to set the Infrastructure Allocation option to Manual.

  1. From the Infrastructure Editor, select the InstanceArray, click the Advanced tab and configure the desired primary and secondary IP configuration.

  • A setting (on either the primary IP or secondary IPs) of Subnet Pool: Automatic will instruct the system to use IPs from Subnets created from Subnet Pools that are marked as Infrastructure Allocation: Automatic (the default)”

  • A setting of Use custom VLAN: disabled will instruct the system to use the automatically allocated VLAN from the range configured in the Datacenter’s ToRWANVLANRange option (defaults to 100-399 for VLAN provisioner).

  • A setting other than Default on the VLAN for the primary IPs will also instruct the system to configure the switch port to map all UNTAGGED traffic to the respective switch-side VLAN.

  • A setting other than Default on the VLAN for the secondary IPs will also instruct the system to configure the switch port to allow traffic TAGGED with the respective VLAN.

Note that this does not automatically configure the server side interface. The template must be configured to perform this setup. The {{wan_interface_ipv4_vlan_id_INDEX}} variable can be used to in the template to retrieve the VLAN id of the respective interface.

Configuring the secondary interface in the kickstart file of the template

The last step is to configure the template to create a vlan interface and assign the ip. Edit the template’s kickstart file to include something like this:

This example is for VMWare ESXI:

if [ {{wan_interface_0_ipv4_ip_count}} -ge 2 ]; then

    esxcli network vswitch standard portgroup add --portgroup-name="Internet Network" --vswitch-name=vSwitch0

    esxcli network vswitch standard portgroup set --portgroup-name "Internet Network" --vlan-id {{wan_interface_0_ipv4_ip_1_vlan_id}}
    esxcli network ip interface add --interface-name=vmk1 --portgroup-name="Internet Network"

    esxcli network ip interface ipv4 set --interface-name "vmk1" --ipv4={{wan_interface_0_ipv4_ip_1_address}} --netmask={{wan_interface_0_ipv4_ip_1_subnet_pool_mask}} --gateway={{wan_interface_1_ipv4_ip_0_gateway}} --type=static
    esxcli network ip route ipv4 add -n 0.0.0.0/0 -g {{wan_interface_0_ipv4_ip_1_gateway}}

else 

    esxcli network ip route ipv4 add -n 0.0.0.0/0 -g {{wan_interface_0_ipv4_ip_0_gateway}}

fi

This is for ubuntu 20.04:


getInterfaceNameFromMAC() {
    interfaceName=$(ip -o link | awk '$(NF-2) == "'$1'" {print $2}')
    if [ -z "$interfaceName" ]
        then
            echo "Did not find interface name for given MAC: $1."
        exit 1
    fi
    echo ${interfaceName::-1}
}

# Helper Network Device ConfigFile
bondNetworkDeviceConfigFile() {
cat <<EOT >> /etc/systemd/network/10-bond0.link
# Specify the Mac address for bond0
[Match]
OriginalName=bond0

[Link]
MACAddressPolicy=none
MACAddress={{wan_interface_0_mac_address}}
EOT
}

export wanInterfaceCount={{wan_interface_count}}

# Generate Netplan Configiguration for bonded setups
if [ {{wan_interface_count}} -gt 1 ]; then
case $wanInterfaceCount in
	2)
wan1Name="$(getInterfaceNameFromMAC "{{wan_interface_0_mac_address}}")"
wan2Name="$(getInterfaceNameFromMAC "{{wan_interface_1_mac_address}}")"
read -r -d '' NETPLANETHERNETS<< EOM
{
  "$wan1Name": {
    "dhcp4": false
  },
  "$wan2Name": {
    "dhcp4": false
  }
}
EOM
read -r -d '' NETPLANBONDINTERFACES<< EOM
[
  "$wan1Name",
  "$wan2Name"
]
EOM
;;
	3)
wan1Name="$(getInterfaceNameFromMAC "{{wan_interface_0_mac_address}}")"
wan2Name="$(getInterfaceNameFromMAC "{{wan_interface_1_mac_address}}")"
wan3Name="$(getInterfaceNameFromMAC "{{wan_interface_2_mac_address}}")"
read -r -d '' NETPLANETHERNETS<< EOM
{
  "$wan1Name": {
    "dhcp4": false
  },
  "$wan2Name": {
    "dhcp4": false
  },
  "$wan3Name": {
    "dhcp4": false
  }
}
EOM
read -r -d '' NETPLANBONDINTERFACES<< EOM
[
  "$wan1Name",
  "$wan2Name",
  "$wan3Name"
]
EOM
;;
	4)
wan1Name="$(getInterfaceNameFromMAC "{{wan_interface_0_mac_address}}")"
wan2Name="$(getInterfaceNameFromMAC "{{wan_interface_1_mac_address}}")"
wan3Name="$(getInterfaceNameFromMAC "{{wan_interface_2_mac_address}}")"
wan4Name="$(getInterfaceNameFromMAC "{{wan_interface_3_mac_address}}")"
read -r -d '' NETPLANETHERNETS<< EOM
{
  "$wan1Name": {
    "dhcp4": false
  },
  "$wan2Name": {
    "dhcp4": false
  },
  "$wan3Name": {
    "dhcp4": false
  },
  "$wan4Name": {
    "dhcp4": false
  }
}
EOM
read -r -d '' NETPLANBONDINTERFACES<< EOM
[
  "$wan1Name",
  "$wan2Name",
  "$wan3Name",
  "$wan4Name"
]
EOM
;;
	*)
exit
;;
esac

bondNetworkDeviceConfigFile


read -r -d '' TEMPNETPLANCFG<< EOM
{
  "network": {
    "version": 2,
    "renderer": "networkd",
    "ethernets": $NETPLANETHERNETS,
    "bonds": {
      "bond0": {
        "dhcp4": false,
        "addresses": [
           "{{wan_interface_0_ipv4_ip_0_cidr}}"
        ],
        "routes": [
          {
            "to": "default",
            "via": "{{wan_interface_0_ipv4_ip_0_gateway}}"
          }
        ],
        "nameservers": {
          "addresses": [
             "{{dns_server_ip_0}}"
          ]
        },
        "interfaces": $NETPLANBONDINTERFACES,
        "parameters": {
          "mode": "802.3ad",
          "lacp-rate": "fast",
          "mii-monitor-interval": 100,
          "transmit-hash-policy": "layer3+4"
        }
      }
    }
  }
}
EOM

echo "$TEMPNETPLANCFG" > /var/log/01-netcfg.json

# Convert to yaml
python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' < /var/log/01-netcfg.json > /etc/netplan/01-netcfg.yaml
fi


# Generate Netplan Configuration for single wan interface setups
if [ {{wan_interface_count}} -eq 1 ]; then

wan1Name="$(getInterfaceNameFromMAC "{{wan_interface_0_mac_address}}")"
read -r -d '' TEMPNETPLANCFG<< EOM
{
  "network": {
    "version": 2,
    "renderer": "networkd",
    "ethernets": {
      "$wan1Name": {
        "dhcp4": false,
        "addresses": [
               "{{wan_interface_0_ipv4_ip_0_cidr}}"
            ],
        "routes": [
              {
                "to": "default",
                "via": "{{wan_interface_0_ipv4_ip_0_gateway}}"
              }
            ],
        "nameservers": {
              "addresses": [
                 "{{dns_server_ip_0}}"
              ]
            }
      }
    }
  }
}
EOM

echo "$TEMPNETPLANCFG" > /var/log/01-netcfg.json

# Convert to yaml
python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' < /var/log/01-netcfg.json > /etc/netplan/01-netcfg.yaml
fi