Configuring VLANs

This document describes the device data model parameters one should consider when creating a VLAN configuration template. For a wider picture, please see contributing new devices document.

This document assumes you’re using an Ansible task list that is able to deploy device configuration from a template. If you plan to use Ansible modules to build device configuration, you’ll find some guidance in Using Ansible Configuration Modules section of OSPF implementation guide.

VLAN node- and interface data model contains enough information to implement VLANs on a wide variety of platforms, including:

  • Switch-like platforms that use a VLAN database, VLAN trunks on physical interfaces, and VLAN/SVI interfaces (example: Dell OS10). Set vlan.model attribute to switch.

  • Router-like platforms that use bridge groups, VLAN subinterfaces on physical interfaces, and BVI/IRB interfaces (example: Cisco IOSv, Nokia SR Linux, Mikrotik RouterOS). Set vlan.model attribute to router.

  • Switch-like platforms that support both routed and switched ports, or a hybrid approach where some ports can be routed using VLAN subinterfaces, while other ports could be connected to the internal L2/L3 switch (example: Arista EOS, VyOS). Set vlan.model attribute to l3-switch.

The following blog posts might help if the above bullets managed to totally confuse you:

Finally, it’s highly recommended to run all relevant VLAN integration test cases in tests/integration/vlan directory to test your implementation.

Notes:

  • The device configuration template (in Jinja2 format) should be stored in netsim/templates/vlan/<nos>.j2 with nos being the value of netlab_device_type or ansible_network_os variable (see Using Your Devices with Ansible Playbooks for more details).

  • Most of the data model attributes are optional. Use if sth is defined, sth|default(value) or if 'sth' in ifdata in your Jinja2 templates to check for presence of optional attributes. Try to be consistent ;)

VLAN Principles

Data model:

  • VLANs are defined in node-level vlans dictionary. Use this dictionary to get VLAN ID (802.1q tag), bridge group ID, and VNI.

  • Interface VLAN parameters are specified in interface vlan dictionary. Do not use any other parameter (for example, vlan_name).

  • VLAN interfaces or routed VLAN subinterface are created as needed on platforms that support them. These interfaces have virtual_interface set to True; you can further classify them with the type attribute:

    • VLAN/SVI/BVI interface have type set to svi

    • Routed VLAN subinterfaces have type set to ‌vlan_member

VLAN-related interfaces are included in the node.interfaces list and are thus best configured in initial configuration template[1]. The other VLAN-related parameters should be configured in netsim/templates/vlan/<nos>.j2 template (or corresponding Ansible task list).

Device Features

You have to specify VLAN-related capabilities of your device in devices.<device>.features.vlan dictionary in topology-defaults.yml. You can set the following parameters:

  • model – describes the way the device implements VLAN interfaces. Valid values are router, switch or l3-switch. See the introductory section of this document for more details.

  • svi_interface_name – a template for the VLAN/SVI/BVI interface name. You can use {vlan} or {bvi} within this string to set the interface name based on VLAN ID or bridge group.

  • subif_name – name of VLAN subinterfaces for router platforms or routed VLAN subinterface name for l3-switch platforms. Use {ifname} to get the parent interface name, {subif_index} to get subinterface ID[2], and {vlan.access_id} to get the VLAN tag[3].

  • first_subif_id – subinterface ID of the first subinterface in case your platform uses unusual subinterface names. Defaults to 1.

  • mixed_trunk – set to True when a platform supports a mix of bridged and routed VLANs on a trunk interface, regardless of the platform type – applicable to router and l3-switch platforms. Set this flag only if your platform passes the vlan-mixed-trunk.yml integration test case.

  • native_routed – set to True when a platform supports routed native VLAN on a routed interface – applicable to router and l3-switch platforms. Set this flag only if your platform passes the vlan-routed-native.yml integration test case.

The following VLAN features have been defined for Cisco IOSv, Arista EOS, VyOS, Mikrotik RouterOS and Dell OS10:

devices:
  iosv:
    features:
      vlan:
        model: router
        svi_interface_name: BVI{bvi}
        subif_name: "{ifname}.{subif_index}"
        mixed_trunk: True
        native_routed: True        
  eos:
    features:
      vlan:
        model: l3-switch
        svi_interface_name: Vlan{vlan}
        subif_name: "{ifname}.{subif_index}"
  srlinux:
    features:
      vlan:
        model: router
        svi_interface_name: "vlan{vlan}"
        subif_name: "{ifname}.{subif_index}"
        mixed_trunk: True
  vyos:
    features:
      vlan:
        model: l3-switch
        svi_interface_name: "br0.{vlan}"
        subif_name: "{ifname}.{vlan.access_id}"
  dellos10:
    features:
      vlan:
        model: switch
        svi_interface_name: vlan{vlan}
  routeros:
    features:
      vlan:
        model: router
        svi_interface_name: bridge{vlan}
        subif_name: "{ifname}-{vlan.access_id}"

Notes:

  • Cisco IOSv is a router and uses BVI (bridge group) interface and per-VLAN subinterfaces.

  • Arista EOS is a switch and uses VLAN interfaces. It also supports routed VLAN subinterfaces.

  • VyOS uses a VLAN-aware Linux bridge and creates VLAN interfaces by appending VLAN ID to bridge name - so it behaves like a switch. It also supports routed VLAN subinterfaces.

  • Dell OS10 is a switch and uses VLAN interfaces. As specified above, it does not support routed VLAN subinterfaces.

  • Mikrotik RouterOS is a router and uses bridge interface and per-VLAN subinterfaces.

Interface Configuration

It’s easiest to create VLAN interfaces and subinterfaces in the netsim/templates/initial/<nos>.j2 configuration template. Use virtual_interface attribute to skip configuration parameters that apply only to physical interfaces. For example, you cannot configure switchport or mac-address on an Arista EOS VLAN interface.

Arista EOS interface configuration template:

{% for l in interfaces|default([]) %}
interface {{ l.ifname }}
 no shutdown
{% if l.virtual_interface is not defined %}
 no switchport
 mac-address {{ '52dc.cafe.%02d%02d' % ( id,l.ifindex ) }}
{% endif %}
{% endfor %}

Some operating systems (example: Cisco IOS) don’t want to create VLAN or BVI interface unless you have already configured the corresponding VLAN or bridge-group.

Cisco IOS initial configuration template thus checks for presence of vlans dictionary:

{% if vlans is defined %}
{% include 'ios.vlan.j2' %}
{% endif %}

Cisco IOS initial VLAN configuration creates the necessary bridge groups. VLANs are assigned to bridge groups during the VLAN configuration process.

bridge irb
!
{% for vname,vdata in vlans.items() %}
bridge {{ vdata.bridge_group }} protocol ieee
bridge {{ vdata.bridge_group }} route ip
!
{% endfor +%}

VLAN Database Configuration

Use the vlans node dictionary to create a list of VLANs used by a device. Each VLAN within the vlans dictionary has at least these attributes

  • id – 802.1q tag

  • vni – VXLAN VNI

  • bridge_group – VLAN-specific bridge group within the node.

Switch-like devices usually need a VLAN database. The following template configures it on Arista EOS:

{% if vlans is defined %}
{%   for vname,vdata in vlans.items() %}
vlan {{ vdata.id }}
 name {{ vname }}
!
{%   endfor +%}
{% endif %}

Interface VLAN Parameters

Based on the capabilities of your platform, you might have to configure:

  • VLAN access interfaces

  • VLAN trunks

  • Native VLAN

  • VLAN encapsulation on a VLAN- or routed subinterface[4]

You can use the following interface VLAN parameters to configure VLANs:

  • A (switched) trunk VLAN interface has vlan.trunk_id list that contains the list of VLANs on that trunk. A pure routed physical interface will not have a vlan.trunk_id list – the VLANs terminated on the interface are configured as subinterfaces.

  • A trunk VLAN interface might have a native VLAN. Its 802.1q tag is specified in vlan.access_id attribute, its name in vlan.native attribute. The native VLAN 802.1q tag is also included in vlan.trunk_id. A routed interface will not have vlan.native or vlan.access_id attribute – there is no need for extra configuration when all you want to do is to route untagged IP packets.

  • An access VLAN interface has vlan.access_id attribute that contains the 802.1 VLAN tag. You can also use vlan.access attribute to get VLAN name. A routed interface will not have vlan.access or vlan.access_id attribute because it’s routing untagged IP packets.

  • A routed VLAN subinterface has VLAN 802.1q tag in vlan.access_id attribute. There is no vlan.access or vlan.native attribute.

  • Use type to differentiate between a VLAN access physical interface and a routed VLAN subinterface – routed subinterfaces have type set to vlan_member

Switch-Like Platforms

Use the following logic to configure interface VLANs parameters on platforms that look like switches:

  • Is vlan.trunk_id defined? ➜ trunk interface

    • Set interface mode to trunk

    • Set list of allowed VLANs from vlan.trunk_id

    • If vlan.native is defined, set native VLAN from vlan.access_id

  • Else: Is vlan.access_id defined? ➜ access or routed subinterface

    • Is type equal to vlan_member? ➜ routed VLAN subinterface, set VLAN tag

    • Else: Access VLAN interface:

      • Set interface mode to access

      • Set access VLAN 802.1q tag from vlan.access_id

The following template implements that logic for Arista EOS:

{% for ifdata in interfaces if ifdata.vlan is defined %}
!
interface {{ ifdata.ifname }}
{%   if ifdata.vlan.trunk_id is defined %}
 switchport
 switchport mode trunk
 switchport trunk allowed vlan {{ ifdata.vlan.trunk_id|sort|join(",") }}
{%     if ifdata.vlan.native is defined %}
 switchport trunk native vlan {{ ifdata.vlan.access_id }}
{%     endif %}
{%   elif ifdata.vlan.access_id is defined %}
{%     if ifdata.type == 'vlan_member' %}
 encapsulation dot1q vlan {{ ifdata.vlan.access_id }}
{%     else %}
 switchport
 switchport access vlan {{ ifdata.vlan.access_id }}
{%     endif %}
{%   endif %}
{% endfor +%}

Alternatively, you could check for:

  • vlan.trunk_id ➜ trunk VLAN interface

  • vlan.access ➜ access VLAN interface

  • vlan.access_id but no vlan.access ➜ routed subinterface

  • Anything else ➜ regular IP interface

Router-Like Platforms

Router-like platforms implement VLANs by connecting physical interfaces or VLAN subinterfaces to bridge domains (groups). VLAN subinterfaces are created for every VLAN in a VLAN trunk; interface type for VLAN subinterfaces is set to vlan_member regardless of whether the VLAN is routed or bridged.

Configuring VLAN interface parameters on a router-like platform is even easier than doing it on a switch-like platform:

  • If the VLAN name is specified (vlan.access or vlan.native), find the bridge_group from the VLAN database. Configure bridge group on the interface to connect the VLAN subinterface or access interface to the VLAN-specific bridge group.

  • When configuring a VLAN subinterface (type == vlan_member), configure interface VLAN encapsulation using vlan.access_id parameter.

The following template configures VLAN interface parameters on Cisco IOS:

{% for ifdata in interfaces if ifdata.vlan is defined %}
!
interface {{ ifdata.ifname }}
{% if ifdata.type == 'vlan_member' and ifdata.vlan.access_id is defined %}
 encapsulation dot1Q {{ ifdata.vlan.access_id }}
{% endif %}
{% if ifdata.vlan.access is defined %}
 bridge-group {{ vlans[ifdata.vlan.access].bridge_group }}
{% endif %}
{% if ifdata.vlan.native is defined %}
 bridge-group {{ vlans[ifdata.vlan.native].bridge_group }}
{% endif %}
{% endfor %}