Zzzz code digger

SONiC host VLAN fundamentals

2021-05-25
Longxiang Lyu

  • reading notes of SONiC host VLAN related topics

vlan basics

  • vlan: virtual LAN
  • a native bridge/switch is one broadcast domain, VLAN is used to separate one broadcast domain into multiple broadcast domains.
  • VID: a bridge/switch port can have one or more VIDs assigned to it. a VID(VLAN ID) is an integer with default value as 1.
    • only packets that has tag number equal to one of the VIDs of the port is allowed to pass(both ingress/egress)
    • a port can have multiple VIDs: a trunk port
  • PVID: PVID(Port VLAN ID)
    • ingress packets(untagged) will be tagged with PVID
    • egress packets with VLAN tag same as PVID of the port will be untagged
  • trunk port: have multiple VIDs, allows traffic from various VLANs traveling across switches.

SONiC host vlan

  • a typical VLAN setup in T0 topology
root@vlab-06:~# show vlan config
Name        VID  Member      Mode
--------  -----  ----------  --------
Vlan1000   1000  Ethernet4   untagged
Vlan1000   1000  Ethernet8   untagged
Vlan1000   1000  Ethernet12  untagged
Vlan1000   1000  Ethernet16  untagged
Vlan1000   1000  Ethernet20  untagged
Vlan1000   1000  Ethernet24  untagged
Vlan1000   1000  Ethernet28  untagged
Vlan1000   1000  Ethernet32  untagged
Vlan1000   1000  Ethernet36  untagged
Vlan1000   1000  Ethernet40  untagged
Vlan1000   1000  Ethernet44  untagged
Vlan1000   1000  Ethernet48  untagged
Vlan1000   1000  Ethernet52  untagged
Vlan1000   1000  Ethernet56  untagged
Vlan1000   1000  Ethernet60  untagged
Vlan1000   1000  Ethernet64  untagged
Vlan1000   1000  Ethernet68  untagged
Vlan1000   1000  Ethernet72  untagged
Vlan1000   1000  Ethernet76  untagged
Vlan1000   1000  Ethernet80  untagged
Vlan1000   1000  Ethernet84  untagged
Vlan1000   1000  Ethernet88  untagged
Vlan1000   1000  Ethernet92  untagged
Vlan1000   1000  Ethernet96  untagged
  • the host VLAN setup
    • the host VLAN is configured with Linux command bridge by the vlanmgr: https://github.com/Azure/sonic-swss/blob/master/cfgmgr/vlanmgr.h
root@vlab-06:~# bridge vlan show
port    vlan ids
docker0  1 PVID Egress Untagged
Ethernet24       1000 PVID Egress Untagged
Ethernet28       1000 PVID Egress Untagged
Ethernet36       1000 PVID Egress Untagged
Ethernet32       1000 PVID Egress Untagged
Ethernet40       1000 PVID Egress Untagged
Ethernet44       1000 PVID Egress Untagged
Ethernet4        1000 PVID Egress Untagged
Ethernet8        1000 PVID Egress Untagged
Ethernet12       1000 PVID Egress Untagged
Ethernet20       1000 PVID Egress Untagged
Ethernet16       1000 PVID Egress Untagged
Ethernet52       1000 PVID Egress Untagged
Ethernet48       1000 PVID Egress Untagged
Ethernet56       1000 PVID Egress Untagged
Ethernet60       1000 PVID Egress Untagged
Ethernet68       1000 PVID Egress Untagged
Ethernet64       1000 PVID Egress Untagged
Ethernet72       1000 PVID Egress Untagged
Ethernet76       1000 PVID Egress Untagged
Ethernet84       1000 PVID Egress Untagged
Ethernet80       1000 PVID Egress Untagged
Ethernet88       1000 PVID Egress Untagged
Ethernet92       1000 PVID Egress Untagged
Ethernet96       1000 PVID Egress Untagged
Bridge   1000
dummy    1 PVID Egress Untagged

add vlan bridge

  • VLAN bridge BRIDGE is configured in the constructor of VlanMgr
VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames) :
        Orch(cfgDb, tableNames),
        m_cfgVlanTable(cfgDb, CFG_VLAN_TABLE_NAME),
        m_cfgVlanMemberTable(cfgDb, CFG_VLAN_MEMBER_TABLE_NAME),
        m_statePortTable(stateDb, STATE_PORT_TABLE_NAME),
        m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME),
        m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME),
        m_stateVlanMemberTable(stateDb, STATE_VLAN_MEMBER_TABLE_NAME),
        m_appVlanTableProducer(appDb, APP_VLAN_TABLE_NAME),
        m_appVlanMemberTableProducer(appDb, APP_VLAN_MEMBER_TABLE_NAME),
        replayDone(false)
{
    ...
 
    const std::string cmds = std::string("")
      + BASH_CMD + " -c \""
      + IP_CMD + " link del " + DOT1Q_BRIDGE_NAME + " 2>/dev/null; "
      + IP_CMD + " link add " + DOT1Q_BRIDGE_NAME + " up type bridge && "
      + IP_CMD + " link set " + DOT1Q_BRIDGE_NAME + " mtu " + DEFAULT_MTU_STR + " && "
      + IP_CMD + " link set " + DOT1Q_BRIDGE_NAME + " address " + gMacAddress.to_string() + " && "
      + BRIDGE_CMD + " vlan del vid " + DEFAULT_VLAN_ID + " dev " + DOT1Q_BRIDGE_NAME + " self; "
      + IP_CMD + " link del dev dummy 2>/dev/null; "
      + IP_CMD + " link add dummy type dummy && "
      + IP_CMD + " link set dummy master " + DOT1Q_BRIDGE_NAME + "\"";

    std::string res;
    EXEC_WITH_ERROR_THROW(cmds, res);

    // The generated command is:
    // /bin/echo 1 > /sys/class/net/Bridge/bridge/vlan_filtering
    const std::string echo_cmd = std::string("")
      + ECHO_CMD + " 1 > /sys/class/net/" + DOT1Q_BRIDGE_NAME + "/bridge/vlan_filtering";

    int ret = swss::exec(echo_cmd, res);
    /* echo will fail in virtual switch since /sys directory is read-only.
     * need to use ip command to setup the vlan_filtering which is not available in debian 8.
     * Once we move sonic to debian 9, we can use IP command by default
     * ip command available in Debian 9 to create a bridge with a vlan filtering:
     * /sbin/ip link add Bridge up type bridge vlan_filtering 1 */
    if (ret != 0)
    {
        const std::string echo_cmd_backup = std::string("")
          + IP_CMD + " link set " + DOT1Q_BRIDGE_NAME + " type bridge vlan_filtering 1";

        EXEC_WITH_ERROR_THROW(echo_cmd_backup, res);
    }
}

  • the bridge setting has two modes:
    • self: link setting is configured on specified physical device
    • master: link setting is configured on the bridge(enslaved master)
  • the bridge BRIDGE has vlan_filtering == 1
    • when disabled, the bridge will not consider the VLAN tag when handling packets
  • a dummy interface is created and enslaved to bridge BRIDGE
    • FIXME: why create a dummy here?

add host vlan interface

bool VlanMgr::addHostVlan(int vlan_id)
{
    SWSS_LOG_ENTER();

    // The command should be generated as:
    // /bin/bash -c "/sbin/bridge vlan add vid  dev Bridge self &&
    //               /sbin/ip link add link Bridge up name Vlan address  type vlan id "
    const std::string cmds = std::string("")
      + BASH_CMD + " -c \""
      + BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + DOT1Q_BRIDGE_NAME + " self && "
      + IP_CMD + " link add link " + DOT1Q_BRIDGE_NAME
               + " up"
               + " name " + VLAN_PREFIX + std::to_string(vlan_id)
               + " address " + gMacAddress.to_string()
               + " type vlan id " + std::to_string(vlan_id) + "\"";

    std::string res;
    EXEC_WITH_ERROR_THROW(cmds, res);

    return true;
}
  • Assume vlan_id is 1000
  • addHostVlan
    • adds vlan 1000 to the bridge Bridge
    • create a vlan id 1000 sub interface called Vlan1000 of bridge Bridge

add host vlan member

bool VlanMgr::addHostVlanMember(int vlan_id, const string &port_alias, const string& tagging_mode)
{
    SWSS_LOG_ENTER();

    std::string tagging_cmd;
    if (tagging_mode == "untagged" || tagging_mode == "priority_tagged")
    {
        tagging_cmd = "pvid untagged";
    }

    // The command should be generated as:
    // /bin/bash -c "/sbin/ip link set  master Bridge &&
    //               /sbin/bridge vlan del vid 1 dev  &&
    //               /sbin/bridge vlan add vid  dev  "
    ostringstream cmds, inner;
    inner << IP_CMD " link set " << shellquote(port_alias) << " master " DOT1Q_BRIDGE_NAME " && "
      BRIDGE_CMD " vlan del vid " DEFAULT_VLAN_ID " dev " << shellquote(port_alias) << " && "
      BRIDGE_CMD " vlan add vid " + std::to_string(vlan_id) + " dev " << shellquote(port_alias) << " " + tagging_cmd;
    cmds << BASH_CMD " -c " << shellquote(inner.str());

    std::string res;
    EXEC_WITH_ERROR_THROW(cmds.str(), res);

    return true;
}
  • addhostVlanMember
    • add the port to the bridge Bridge
    • remove the default vid 1
    • add the vid 1000 to the port
  • bridge vlan add
    • pvid: the vlan specified is to be considered a PVID at ingress, any untagged frames will be assigned to this vlan
    • untagged: the vlan specified is to be considered as untagged on egress

some trials with command bridge

root@lolv-vm-02:~# ip netns add vlan_ns
root@lolv-vm-02:~# ip netns exec vlan_ns /bin/bash
root@lolv-vm-02:~# ip link add vlan_bridge up type bridge
root@lolv-vm-02:~# bridge vlan
port    vlan ids
vlan_bridge      1 PVID Egress Untagged

root@lolv-vm-02:~# ip link add ext_intf1 type veth peer name int_intf1
root@lolv-vm-02:~# ip link add ext_intf0 type veth peer name int_intf0
root@lolv-vm-02:~# ip link add ext_intf2 type veth peer name int_intf2
root@lolv-vm-02:~# bridge vlan add vid 1000 dev vlan_bridge self
root@lolv-vm-02:~# bridge vlan
port    vlan ids
vlan_bridge      1 PVID Egress Untagged
         1000

root@lolv-vm-02:~# bridge vlan del vid 1 dev vlan_bridge self
root@lolv-vm-02:~# bridge vlan
port    vlan ids
vlan_bridge      1000

root@lolv-vm-02:~# ip link set int_intf0 master vlan_bridge
root@lolv-vm-02:~# bridge vlan add vid 1000 dev int_intf0
root@lolv-vm-02:~# ip link set int_intf1 master vlan_bridge
root@lolv-vm-02:~# bridge vlan add vid 1000 dev int_intf1 pvid
root@lolv-vm-02:~# ip link set int_intf2 master vlan_bridge
root@lolv-vm-02:~# bridge vlan add vid 1000 dev int_intf2 pvid untagged
root@lolv-vm-02:~# bridge vlan
port    vlan ids
vlan_bridge      1000

int_intf0        1 PVID Egress Untagged
         1000

int_intf1        1 Egress Untagged
         1000 PVID

int_intf2        1 Egress Untagged
         1000 PVID Egress Untagged

root@lolv-vm-02:~# brctl show
bridge name     bridge id               STP enabled     interfaces
vlan_bridge             8000.36b3066690b0       no              int_intf0
                                                                int_intf1
                                                                int_intf2
                                                                                                 
  • the bridge command is used for VLAN filtering
  • any ether device added by bridge vlan add has a default vid as 1 with both PVID and untagged
    • from the example above, when bridge vlan_bridge is created, it has a default vid as 1 with PVID and untagged
    • this means, without assign user vid to the bridge vlan_bridge, any packets enters the bridge will be tagged with the default vid 1, any packets leaving the bridge will have the vlan tag(1) stripped, and this process is transparent to the user as there is no vlan configured on the bridge.
  • NOTE:
    • PVID is used to ingress packets, if a port has PVID on for a vid, it means any untagged packet entering the port will be passed to the VLAN specified by vid.
    • untagged is used to egress packets, when a packet is leaving a VLAN through a port, if the port has untagged on, the vlan tag of the packet will be stripped.
    • a port can have multiple vids with untagged, but can only have one vid as PVID

references

  • https://github.com/Azure/sonic-mgmt/blob/master/docs/testplan/VLAN-trunk-test-plan.md
  • https://documentation.meraki.com/General_Administration/Tools_and_Troubleshooting/Fundamentals_of_802.1Q_VLAN_Tagging#:~:text=VLAN%20enabled%20ports%20are%20generally,for%20only%20a%20single%20VLAN.
  • http://forums.dlink.com/index.php?topic=41342.0
  • https://man7.org/linux/man-pages/man8/bridge.8.html
  • https://linux-blog.anracom.com/tag/vlan-aware-bridge/

Similar Posts

Comments