1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5VLAN tests. 6 7Validates that ping traffic is sent and received correctly over 802.1q 8and 802.1ad VLAN interfaces, with hardware RX VLAN stripping enabled and 9disabled on the local interface. 10 11The "hw" test variants enable the RX VLAN stripping features supported by 12the device, the "sw" test variants disable all of them. A test is xfailed 13if the requested configuration is not possible. 14 15Test cases: 16 - 8021q_hw: Traffic over a single 802.1q VLAN, HW stripping on 17 - 8021q_sw: Traffic over a single 802.1q VLAN, HW stripping off 18 - 8021ad_hw: Traffic over a single 802.1ad VLAN, HW stripping on 19 - 8021ad_sw: Traffic over a single 802.1ad VLAN, HW stripping off 20 - qinq_hw: Traffic over an 802.1q VLAN stacked on an 802.1ad VLAN, 21 HW stripping on 22 - qinq_sw: Traffic over an 802.1q VLAN stacked on an 802.1ad VLAN, 23 HW stripping off 24""" 25 26import os 27 28from lib.py import ( 29 KsftNamedVariant, 30 KsftXfailEx, 31 NetDrvEpEnv, 32 cmd, 33 defer, 34 ethtool, 35 ip, 36 ksft_exit, 37 ksft_run, 38 ksft_variants, 39 set_ethtool_feat, 40) 41 42OUTER_DEV = f"vlout{os.getpid()}" 43INNER_DEV = f"vlin{os.getpid()}" 44 45OUTER_VID = 100 46INNER_VID = 200 47 48LOCAL_IP = os.environ.get("VLAN_LOCAL_V4", "198.51.100.1") 49REMOTE_IP = os.environ.get("VLAN_REMOTE_V4", "198.51.100.2") 50 51 52def _vlan_add(base, name, proto, vid, host=None): 53 """Create a VLAN device on top of base and bring it up.""" 54 55 ip(f"link add link {base} name {name} type vlan proto {proto} id {vid}", 56 host=host) 57 defer(ip, f"link del {name}", host=host) 58 ip(f"link set {name} up", host=host) 59 60 61def _vlan_setup(base, addr, outer_proto, inner_proto, host=None): 62 """Create VLAN interfaces on base and set an IP on the innermost one.""" 63 64 _vlan_add(base, OUTER_DEV, outer_proto, OUTER_VID, host=host) 65 if inner_proto: 66 _vlan_add(OUTER_DEV, INNER_DEV, inner_proto, INNER_VID, host=host) 67 68 dev = INNER_DEV if inner_proto else OUTER_DEV 69 ip(f"addr add {addr}/24 dev {dev}", host=host) 70 71 72def _setup(cfg, outer_proto, inner_proto, hw_strip): 73 """Configure VLAN stripping and create the VLAN interfaces.""" 74 75 names = ["rx-vlan-offload"] 76 if outer_proto == "802.1ad": 77 names.append("rx-vlan-stag-hw-parse") 78 79 feat = ethtool(f"-k {cfg.ifname}", json=True)[0] 80 feats = {} 81 for name in names: 82 # hw: skip the features the device does not support 83 # sw: request all of them off 84 if hw_strip and feat[name]["fixed"] and not feat[name]["active"]: 85 continue 86 feats[name] = hw_strip 87 88 if hw_strip and not feats: 89 raise KsftXfailEx("Device does not support RX VLAN stripping") 90 91 set_ethtool_feat(cfg.ifname, feat, feats) 92 93 _vlan_setup(cfg.ifname, LOCAL_IP, outer_proto, inner_proto) 94 _vlan_setup(cfg.remote_ifname, REMOTE_IP, outer_proto, inner_proto, 95 host=cfg.remote) 96 97 98def _vlan_variants(): 99 """Generator that yields the VLAN protocols and the stripping mode.""" 100 101 yield KsftNamedVariant("8021q_hw", "802.1q", None, True) 102 yield KsftNamedVariant("8021q_sw", "802.1q", None, False) 103 yield KsftNamedVariant("8021ad_hw", "802.1ad", None, True) 104 yield KsftNamedVariant("8021ad_sw", "802.1ad", None, False) 105 yield KsftNamedVariant("qinq_hw", "802.1ad", "802.1q", True) 106 yield KsftNamedVariant("qinq_sw", "802.1ad", "802.1q", False) 107 108 109@ksft_variants(_vlan_variants()) 110def test(cfg, outer_proto, inner_proto, hw_strip): 111 """Run a single VLAN test""" 112 113 _setup(cfg, outer_proto, inner_proto, hw_strip) 114 115 cmd(f"ping -c 1 -W 5 {REMOTE_IP}") 116 117 118def main() -> None: 119 """ Ksft boiler plate main """ 120 121 with NetDrvEpEnv(__file__) as cfg: 122 ksft_run(cases=[test], args=(cfg, )) 123 ksft_exit() 124 125 126if __name__ == "__main__": 127 main() 128