xref: /linux/tools/testing/selftests/drivers/net/hw/vlan.py (revision 42a9fb3382fc2573e92f41d203b095d9a372cfc9)
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