1 # Copyright (c) 2021 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Tap utilities library."""
16 from enum import IntEnum
18 from robot.api import logger
20 from resources.libraries.python.Constants import Constants
21 from resources.libraries.python.InterfaceUtil import InterfaceUtil
22 from resources.libraries.python.L2Util import L2Util
23 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
24 from resources.libraries.python.topology import Topology
27 class TapFeaturesFlags(IntEnum):
28 """TAP Features Flags."""
30 TAP_API_FLAG_CSUM_OFFLOAD = 2
31 TAP_API_FLAG_PERSIST = 4
32 TAP_API_FLAG_ATTACH = 8
34 TAP_API_FLAG_GRO_COALESCE = 32
35 TAP_API_FLAG_PACKED = 64
36 TAP_API_FLAG_IN_ORDER = 128
43 def add_tap_interface(
44 node, tap_name, mac=None, host_namespace=None, num_rx_queues=1,
45 rxq_size=0, txq_size=0, tap_feature_mask=0):
46 """Add tap interface with name and optionally with MAC.
48 :param node: Node to add tap on.
49 :param tap_name: Tap interface name for linux tap.
50 :param mac: Optional MAC address for VPP tap.
51 :param host_namespace: Namespace.
52 :param num_rx_queues: Number of RX queues.
53 :param rxq_size: Size of RXQ (0 = Default API; 256 = Default VPP).
54 :param txq_size: Size of TXQ (0 = Default API; 256 = Default VPP).
55 :param tap_feature_mask: Mask of tap features to be enabled.
59 :type host_namespace: str
60 :type num_rx_queues: int
63 :type tap_feature_mask: int
64 :returns: Returns a interface index.
67 cmd = u"tap_create_v2"
69 id=Constants.BITWISE_NON_ZERO,
70 use_random_mac=bool(mac is None),
71 mac_address=L2Util.mac_to_bin(mac) if mac else None,
72 num_rx_queues=int(num_rx_queues),
73 tx_ring_sz=int(txq_size),
74 rx_ring_sz=int(rxq_size),
76 host_mac_addr_set=False,
77 host_ip4_prefix_set=False,
78 host_ip6_prefix_set=False,
79 host_ip4_gw_set=False,
80 host_ip6_gw_set=False,
81 host_namespace_set=bool(host_namespace),
82 host_namespace=host_namespace,
83 host_if_name_set=True,
84 host_if_name=tap_name,
85 host_bridge_set=False,
86 tap_flags=tap_feature_mask
88 err_msg = f"Failed to create tap interface {tap_name} " \
89 f"on host {node[u'host']}"
91 with PapiSocketExecutor(node) as papi_exec:
92 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
94 if_key = Topology.add_new_port(node, u"tap")
95 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
96 Topology.update_interface_name(node, if_key, tap_name)
98 mac = Tap.vpp_get_tap_interface_mac(node, tap_name)
99 Topology.update_interface_mac_address(node, if_key, mac)
100 tap_dev_name = Tap.vpp_get_tap_dev_name(node, tap_name)
101 Topology.update_interface_tap_dev_name(node, if_key, tap_dev_name)
106 def vpp_get_tap_dev_name(node, host_if_name):
107 """Get VPP tap interface name from hardware interfaces dump.
109 :param node: DUT node.
110 :param host_if_name: Tap host interface name.
112 :type host_if_name: str
113 :returns: VPP tap interface dev_name.
116 return Tap.tap_dump(node, host_if_name).get(u"dev_name")
119 def vpp_get_tap_interface_mac(node, interface_name):
120 """Get tap interface MAC address from interfaces dump.
122 :param node: DUT node.
123 :param interface_name: Tap interface name.
125 :type interface_name: str
126 :returns: Tap interface MAC address.
129 return InterfaceUtil.vpp_get_interface_mac(node, interface_name)
132 def tap_dump(node, name=None):
133 """Get all TAP interface data from the given node, or data about
134 a specific TAP interface.
136 :param node: VPP node to get data from.
137 :param name: Optional name of a specific TAP interface.
140 :returns: Dictionary of information about a specific TAP interface, or
141 a List of dictionaries containing all TAP data for the given node.
144 def process_tap_dump(tap_dump):
147 :param tap_dump: Tap interface dump.
149 :returns: Processed tap interface dump.
152 tap_dump[u"host_mac_addr"] = str(tap_dump[u"host_mac_addr"])
153 tap_dump[u"host_ip4_prefix"] = str(tap_dump[u"host_ip4_prefix"])
154 tap_dump[u"host_ip6_prefix"] = str(tap_dump[u"host_ip6_prefix"])
155 tap_dump[u"tap_flags"] = tap_dump[u"tap_flags"].value \
156 if hasattr(tap_dump[u"tap_flags"], u"value") \
157 else int(tap_dump[u"tap_flags"])
158 tap_dump[u"host_namespace"] = None \
159 if tap_dump[u"host_namespace"] == u"(nil)" \
160 else tap_dump[u"host_namespace"]
161 tap_dump[u"host_bridge"] = None \
162 if tap_dump[u"host_bridge"] == u"(nil)" \
163 else tap_dump[u"host_bridge"]
167 cmd = u"sw_interface_tap_v2_dump"
168 err_msg = f"Failed to get TAP dump on host {node[u'host']}"
170 with PapiSocketExecutor(node) as papi_exec:
171 details = papi_exec.add(cmd).get_details(err_msg)
173 data = list() if name is None else dict()
176 data.append(process_tap_dump(dump))
177 elif dump.get(u"host_if_name") == name:
178 data = process_tap_dump(dump)
181 logger.debug(f"TAP data:\n{data}")
185 class TapFeatureMask:
186 """Tap features utilities"""
189 def create_tap_feature_mask(**kwargs):
190 """Create tap feature mask with feature bits set according to kwargs.
191 :param kwargs: Key-value pairs of feature names and it's state
196 if u"all" in kwargs and kwargs[u"all"] is True:
197 for tap_feature_flag in TapFeaturesFlags:
198 tap_feature_mask |= 1 << (tap_feature_flag.value - 1)
200 for feature_name, enabled in kwargs.items():
201 tap_feature_name = u"TAP_API_FLAG_" + feature_name.upper()
202 if tap_feature_name not in TapFeaturesFlags.__members__:
203 raise ValueError(u"Unsupported tap feature flag name")
205 tap_feature_mask |= \
206 1 << (TapFeaturesFlags[tap_feature_name].value - 1)
208 return tap_feature_mask
211 def is_feature_enabled(tap_feature_mask, tap_feature_flag):
212 """Checks if concrete tap feature is enabled within
214 :param tap_feature_mask: Mask of enabled tap features
215 :param tap_feature_flag: Checked tap feature
216 :type tap_feature_mask: int
217 :type tap_feature_flag: TapFeaturesFlags
219 feature_flag_bit = 1 << tap_feature_flag.value
220 return (tap_feature_mask & feature_flag_bit) > 0