1 # Copyright (c) 2016-2017 Intel Corporation
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
18 from random import SystemRandom
22 from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar
23 from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom
24 from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar
25 from trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4
26 from trex_stl_lib import api as Pkt
28 from yardstick.common import exceptions as y_exc
29 from yardstick.network_services.traffic_profile import base
41 TYPE_OF_SERVICE = 'tos'
43 LOG = logging.getLogger(__name__)
46 class TrexProfile(base.TrafficProfile):
47 """ This class handles Trex Traffic profile generation and execution """
50 ETHERNET: ('ether_packet', Pkt.Ether),
51 IP: ('ip_packet', Pkt.IP),
52 IPv6: ('ip6_packet', Pkt.IPv6),
53 UDP: ('udp_packet', Pkt.UDP),
56 def _general_single_action_partial(self, protocol):
62 self._set_proto_fields(protocol, **kwargs)
66 def _ethernet_range_action_partial(self, direction, _):
67 def partial(min_value, max_value, count):
68 # pylint: disable=unused-argument
69 stl_vm_flow_var = STLVmFlowVar(name="mac_{}".format(direction),
75 self.vm_flow_vars.append(stl_vm_flow_var)
76 stl_vm_wr_flow_var = STLVmWrFlowVar(
77 fv_name='mac_{}'.format(direction),
78 pkt_offset='Ether.{}'.format(direction))
79 self.vm_flow_vars.append(stl_vm_wr_flow_var)
82 def _ip_range_action_partial(self, direction, count=1):
83 # pylint: disable=unused-argument
84 def partial(min_value, max_value, count):
85 _, _, actual_count = self._count_ip(min_value, max_value)
88 elif actual_count < int(count):
91 stl_vm_flow_var = STLVmFlowVarRepeatableRandom(
92 name="ip4_{}".format(direction),
98 self.vm_flow_vars.append(stl_vm_flow_var)
99 stl_vm_wr_flow_var = STLVmWrFlowVar(
100 fv_name='ip4_{}'.format(direction),
101 pkt_offset='IP.{}'.format(direction))
102 self.vm_flow_vars.append(stl_vm_wr_flow_var)
103 stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP")
104 self.vm_flow_vars.append(stl_vm_fix_ipv4)
107 def _ip6_range_action_partial(self, direction, _):
108 def partial(min_value, max_value, count):
109 # pylint: disable=unused-argument
110 min_value, max_value, _ = self._count_ip(min_value, max_value)
111 stl_vm_flow_var = STLVmFlowVar(name="ip6_{}".format(direction),
117 self.vm_flow_vars.append(stl_vm_flow_var)
118 stl_vm_wr_flow_var = STLVmWrFlowVar(
119 fv_name='ip6_{}'.format(direction),
120 pkt_offset='IPv6.{}'.format(direction),
122 self.vm_flow_vars.append(stl_vm_wr_flow_var)
125 def _dscp_range_action_partial(self, *args):
126 def partial(min_value, max_value, count):
127 # pylint: disable=unused-argument
128 stl_vm_flow_var = STLVmFlowVar(name="dscp",
134 self.vm_flow_vars.append(stl_vm_flow_var)
135 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp',
137 self.vm_flow_vars.append(stl_vm_wr_flow_var)
140 def _udp_range_action_partial(self, field, count=1):
141 # pylint: disable=unused-argument
142 def partial(min_value, max_value, count):
143 actual_count = int(max_value) - int(min_value)
146 elif int(count) > actual_count:
149 stl_vm_flow_var = STLVmFlowVarRepeatableRandom(
150 name="port_{}".format(field),
156 self.vm_flow_vars.append(stl_vm_flow_var)
157 stl_vm_wr_flow_var = STLVmWrFlowVar(
158 fv_name='port_{}'.format(field),
159 pkt_offset=self.udp[field])
160 self.vm_flow_vars.append(stl_vm_wr_flow_var)
163 def __init__(self, yaml_data):
164 super(TrexProfile, self).__init__(yaml_data)
168 self.first_run = True
170 self.profile_data = []
177 self.ether_packet = None
178 self.ip_packet = None
179 self.ip6_packet = None
180 self.udp_packet = None
185 self.qinq_packet = None
187 self.vm_flow_vars = []
190 self._map_proto_actions = {
191 # the tuple is (single value function, range value function, if the values should be
192 # converted to integer).
193 ETHERNET: (self._general_single_action_partial(ETHERNET),
194 self._ethernet_range_action_partial,
197 IP: (self._general_single_action_partial(IP),
198 self._ip_range_action_partial,
201 IPv6: (self._general_single_action_partial(IPv6),
202 self._ip6_range_action_partial,
205 DSCP: (self._general_single_action_partial(IP),
206 self._dscp_range_action_partial,
209 UDP: (self._general_single_action_partial(UDP),
210 self._udp_range_action_partial,
215 def execute_traffic(self, traffic_generator):
216 """ Generate the stream and run traffic on the given ports """
217 raise NotImplementedError()
219 def _call_on_range(self, range, single_action, range_action, count=1, to_int=False):
220 def convert_to_int(val):
221 return int(val) if to_int else val
223 range_iter = iter(str(range).split('-'))
224 min_value = convert_to_int(next(range_iter))
226 max_value = convert_to_int(next(range_iter))
227 except StopIteration:
228 single_action(min_value)
230 range_action(min_value=min_value, max_value=max_value, count=count)
232 def _set_proto_addr(self, protocol, field, address, count=1):
233 single_action, range_action, to_int = self._map_proto_actions[protocol]
234 self._call_on_range(address,
235 single_action(field),
236 range_action(field, count),
241 def _set_proto_fields(self, protocol, **kwargs):
242 _attr_name, _class = self.PROTO_MAP[protocol]
244 if not getattr(self, _attr_name):
245 setattr(self, _attr_name, _class())
247 _attr = getattr(self, _attr_name)
248 for key, value in six.iteritems(kwargs):
249 setattr(_attr, key, value)
251 def set_svlan_cvlan(self, svlan, cvlan):
252 """ set svlan & cvlan """
254 ether_params = {'type': 0x8100}
255 self._set_proto_fields(ETHERNET, **ether_params)
256 svlans = str(svlan['id']).split('-')
257 svlan_min = int(svlans[0])
258 svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0])
260 svlan = self._get_random_value(svlan_min, svlan_max)
263 cvlans = str(cvlan['id']).split('-')
264 cvlan_min = int(cvlans[0])
265 cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0])
267 cvlan = self._get_random_value(cvlan_min, cvlan_max)
270 self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan)
272 def set_qinq(self, qinq):
273 """ set qinq in packet """
274 self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN'])
276 def _set_outer_l2_fields(self, outer_l2):
277 """ setup outer l2 fields from traffic profile """
278 ether_params = {'type': 0x800}
279 self._set_proto_fields(ETHERNET, **ether_params)
280 if 'srcmac' in outer_l2:
281 self._set_proto_addr(ETHERNET, SRC, outer_l2['srcmac'])
282 if 'dstmac' in outer_l2:
283 self._set_proto_addr(ETHERNET, DST, outer_l2['dstmac'])
284 if 'QinQ' in outer_l2:
285 self.set_qinq(outer_l2['QinQ'])
287 def _set_outer_l3v4_fields(self, outer_l3v4):
288 """ setup outer l3v4 fields from traffic profile """
290 if 'proto' in outer_l3v4:
291 ip_params['proto'] = socket.getprotobyname(outer_l3v4['proto'])
292 if outer_l3v4['proto'] == 'tcp':
293 self.udp_packet = Pkt.TCP()
294 self.udp[DST_PORT] = 'TCP.dport'
295 self.udp[SRC_PORT] = 'TCP.sport'
296 tcp_params = {'flags': '', 'window': 0}
297 self._set_proto_fields(UDP, **tcp_params)
298 if 'ttl' in outer_l3v4:
299 ip_params['ttl'] = outer_l3v4['ttl']
300 self._set_proto_fields(IP, **ip_params)
301 if 'dscp' in outer_l3v4:
302 self._set_proto_addr(DSCP, TYPE_OF_SERVICE, outer_l3v4['dscp'])
303 if 'srcip4' in outer_l3v4:
304 self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count'])
305 if 'dstip4' in outer_l3v4:
306 self._set_proto_addr(IP, DST, outer_l3v4['dstip4'], outer_l3v4['count'])
308 def _set_outer_l3v6_fields(self, outer_l3v6):
309 """ setup outer l3v6 fields from traffic profile """
310 ether_params = {'type': 0x86dd}
311 self._set_proto_fields(ETHERNET, **ether_params)
313 if 'proto' in outer_l3v6:
314 ip6_params['proto'] = outer_l3v6['proto']
315 if outer_l3v6['proto'] == 'tcp':
316 self.udp_packet = Pkt.TCP()
317 self.udp[DST_PORT] = 'TCP.dport'
318 self.udp[SRC_PORT] = 'TCP.sport'
319 tcp_params = {'flags': '', 'window': 0}
320 self._set_proto_fields(UDP, **tcp_params)
321 if 'ttl' in outer_l3v6:
322 ip6_params['ttl'] = outer_l3v6['ttl']
323 if 'tc' in outer_l3v6:
324 ip6_params['tc'] = outer_l3v6['tc']
325 if 'hlim' in outer_l3v6:
326 ip6_params['hlim'] = outer_l3v6['hlim']
327 self._set_proto_fields(IPv6, **ip6_params)
328 if 'srcip6' in outer_l3v6:
329 self._set_proto_addr(IPv6, SRC, outer_l3v6['srcip6'])
330 if 'dstip6' in outer_l3v6:
331 self._set_proto_addr(IPv6, DST, outer_l3v6['dstip6'])
333 def _set_outer_l4_fields(self, outer_l4):
334 """ setup outer l4 fields from traffic profile """
335 if 'srcport' in outer_l4:
336 self._set_proto_addr(UDP, SRC_PORT, outer_l4['srcport'], outer_l4['count'])
337 if 'dstport' in outer_l4:
338 self._set_proto_addr(UDP, DST_PORT, outer_l4['dstport'], outer_l4['count'])
341 def _count_ip(cls, start_ip, end_ip):
342 start = ipaddress.ip_address(six.u(start_ip))
343 end = ipaddress.ip_address(six.u(end_ip))
344 if start.version == 4:
345 return start, end, int(end) - int(start)
346 elif start.version == 6:
347 if int(start) > int(end):
348 raise y_exc.IPv6RangeError(start_ip=str(start),
350 _, lo1 = struct.unpack('!QQ', start.packed)
351 _, lo2 = struct.unpack('!QQ', end.packed)
352 return lo1, lo2, lo2 - lo1
355 def _get_random_value(cls, min_port, max_port):
356 cryptogen = SystemRandom()
357 return cryptogen.randrange(min_port, max_port)