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.
14 """ Trex Traffic Profile definitions """
16 from __future__ import absolute_import
20 from random import SystemRandom
23 from yardstick.network_services.traffic_profile.base import TrafficProfile
24 from stl.trex_stl_lib.trex_stl_client import STLStream
25 from stl.trex_stl_lib.trex_stl_streams import STLFlowLatencyStats
26 from stl.trex_stl_lib.trex_stl_streams import STLTXCont
27 from stl.trex_stl_lib.trex_stl_streams import STLProfile
28 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmWrFlowVar
29 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVarRepeatableRandom
30 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFlowVar
31 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLPktBuilder
32 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLScVmRaw
33 from stl.trex_stl_lib.trex_stl_packet_builder_scapy import STLVmFixIpv4
34 from stl.trex_stl_lib import api as Pkt
37 class TrexProfile(TrafficProfile):
38 """ This class handles Trex Traffic profile generation and execution """
40 def __init__(self, yaml_data):
41 super(TrexProfile, self).__init__(yaml_data)
47 self.profile_data = []
55 self.ip6_packet = None
56 self.udp_packet = None
59 self.qinq_packet = None
61 self.vm_flow_vars = []
63 self.ether_packet = []
65 def execute(self, traffic_generator):
66 """ Generate the stream and run traffic on the given ports """
69 def _set_ether_fields(self, **kwargs):
70 """ set ethernet protocol fields """
71 if not self.ether_packet:
72 self.ether_packet = Pkt.Ether()
73 for key, value in six.iteritems(kwargs):
74 setattr(self.ether_packet, key, value)
76 def _set_ip_fields(self, **kwargs):
77 """ set l3 ipv4 protocol fields """
79 if not self.ip_packet:
80 self.ip_packet = Pkt.IP()
82 setattr(self.ip_packet, key, kwargs[key])
84 def _set_ip6_fields(self, **kwargs):
85 """ set l3 ipv6 protocol fields """
86 if not self.ip6_packet:
87 self.ip6_packet = Pkt.IPv6()
89 setattr(self.ip6_packet, key, kwargs[key])
91 def _set_udp_fields(self, **kwargs):
92 """ set l4 udp ports fields """
93 if not self.udp_packet:
94 self.udp_packet = Pkt.UDP()
96 setattr(self.udp_packet, key, kwargs[key])
98 def set_src_mac(self, src_mac):
99 """ set source mac address fields """
100 src_macs = src_mac.split('-')
101 min_value = src_macs[0]
102 if len(src_macs) == 1:
104 self._set_ether_fields(src=src_mac)
106 stl_vm_flow_var = STLVmFlowVar(name="mac_src",
112 self.vm_flow_vars.append(stl_vm_flow_var)
113 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='mac_src',
114 pkt_offset='Ether.src')
115 self.vm_flow_vars.append(stl_vm_wr_flow_var)
117 def set_dst_mac(self, dst_mac):
118 """ set destination mac address fields """
119 dst_macs = dst_mac.split('-')
120 min_value = dst_macs[0]
121 if len(dst_macs) == 1:
123 self._set_ether_fields(dst=dst_mac)
125 stl_vm_flow_var = STLVmFlowVar(name="mac_dst",
131 self.vm_flow_vars.append(stl_vm_flow_var)
132 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='mac_dst',
133 pkt_offset='Ether.dst')
134 self.vm_flow_vars.append(stl_vm_wr_flow_var)
136 def set_src_ip4(self, src_ip4, count=1):
137 """ set source ipv4 address fields """
138 src_ips = src_ip4.split('-')
139 min_value = src_ips[0]
140 max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
141 if len(src_ips) == 1:
143 self._set_ip_fields(src=src_ip4)
146 STLVmFlowVarRepeatableRandom(name="ip4_src",
152 self.vm_flow_vars.append(stl_vm_flow_var)
153 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='ip4_src',
155 self.vm_flow_vars.append(stl_vm_wr_flow_var)
156 stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP")
157 self.vm_flow_vars.append(stl_vm_fix_ipv4)
159 def set_dst_ip4(self, dst_ip4, count=1):
160 """ set destination ipv4 address fields """
161 dst_ips = dst_ip4.split('-')
162 min_value = dst_ips[0]
163 max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
164 if len(dst_ips) == 1:
166 self._set_ip_fields(dst=dst_ip4)
169 STLVmFlowVarRepeatableRandom(name="dst_ip4",
175 self.vm_flow_vars.append(stl_vm_flow_var)
176 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dst_ip4',
178 self.vm_flow_vars.append(stl_vm_wr_flow_var)
179 stl_vm_fix_ipv4 = STLVmFixIpv4(offset="IP")
180 self.vm_flow_vars.append(stl_vm_fix_ipv4)
182 def set_src_ip6(self, src_ip6):
183 """ set source ipv6 address fields """
184 src_ips = src_ip6.split('-')
185 min_value = src_ips[0]
186 max_value = src_ips[1] if len(src_ips) == 2 else src_ips[0]
188 self._set_ip6_fields(src=src_ip6)
189 if len(src_ips) == 2:
190 min_value, max_value = \
191 self._get_start_end_ipv6(min_value, max_value)
192 stl_vm_flow_var = STLVmFlowVar(name="ip6_src",
198 self.vm_flow_vars.append(stl_vm_flow_var)
199 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='ip6_src',
200 pkt_offset='IPv6.src',
202 self.vm_flow_vars.append(stl_vm_wr_flow_var)
204 def set_dst_ip6(self, dst_ip6):
205 """ set destination ipv6 address fields """
206 dst_ips = dst_ip6.split('-')
207 min_value = dst_ips[0]
208 max_value = dst_ips[1] if len(dst_ips) == 2 else dst_ips[0]
210 self._set_ip6_fields(dst=dst_ip6)
211 if len(dst_ips) == 2:
212 min_value, max_value = \
213 self._get_start_end_ipv6(min_value, max_value)
214 stl_vm_flow_var = STLVmFlowVar(name="dst_ip6",
220 self.vm_flow_vars.append(stl_vm_flow_var)
221 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dst_ip6',
222 pkt_offset='IPv6.dst',
224 self.vm_flow_vars.append(stl_vm_wr_flow_var)
226 def set_dscp(self, dscp):
227 """ set dscp for trex """
228 dscps = str(dscp).split('-')
229 min_value = int(dscps[0])
230 max_value = int(dscps[1]) if len(dscps) == 2 else int(dscps[0])
233 self._set_ip_fields(tos=dscp)
235 stl_vm_flow_var = STLVmFlowVar(name="dscp",
241 self.vm_flow_vars.append(stl_vm_flow_var)
242 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='dscp',
244 self.vm_flow_vars.append(stl_vm_wr_flow_var)
246 def set_src_port(self, src_port, count=1):
247 """ set packet source port """
248 src_ports = str(src_port).split('-')
249 min_value = int(src_ports[0])
250 if len(src_ports) == 1:
251 max_value = int(src_ports[0])
253 self._set_udp_fields(sport=src_port)
255 max_value = int(src_ports[1])
257 STLVmFlowVarRepeatableRandom(name="port_src",
263 self.vm_flow_vars.append(stl_vm_flow_var)
264 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='port_src',
265 pkt_offset=self.udp_sport)
266 self.vm_flow_vars.append(stl_vm_wr_flow_var)
268 def set_dst_port(self, dst_port, count=1):
269 """ set packet destnation port """
270 dst_ports = str(dst_port).split('-')
271 min_value = int(dst_ports[0])
272 if len(dst_ports) == 1:
273 max_value = int(dst_ports[0])
275 self._set_udp_fields(dport=dst_port)
277 max_value = int(dst_ports[1])
279 STLVmFlowVarRepeatableRandom(name="port_dst",
285 self.vm_flow_vars.append(stl_vm_flow_var)
286 stl_vm_wr_flow_var = STLVmWrFlowVar(fv_name='port_dst',
287 pkt_offset=self.udp_dport)
288 self.vm_flow_vars.append(stl_vm_wr_flow_var)
290 def set_svlan_cvlan(self, svlan, cvlan):
291 """ set svlan & cvlan """
293 ether_params = {'type': 0x8100}
294 self._set_ether_fields(**ether_params)
295 svlans = str(svlan['id']).split('-')
296 svlan_min = int(svlans[0])
297 svlan_max = int(svlans[1]) if len(svlans) == 2 else int(svlans[0])
299 svlan = self._get_random_value(svlan_min, svlan_max)
302 cvlans = str(cvlan['id']).split('-')
303 cvlan_min = int(cvlans[0])
304 cvlan_max = int(cvlans[1]) if len(cvlans) == 2 else int(cvlans[0])
306 cvlan = self._get_random_value(cvlan_min, cvlan_max)
309 self.qinq_packet = Pkt.Dot1Q(vlan=svlan) / Pkt.Dot1Q(vlan=cvlan)
311 def set_qinq(self, qinq):
312 """ set qinq in packet """
313 self.set_svlan_cvlan(qinq['S-VLAN'], qinq['C-VLAN'])
315 def set_outer_l2_fields(self, outer_l2):
316 """ setup outer l2 fields from traffic profile """
317 ether_params = {'type': 0x800}
318 self._set_ether_fields(**ether_params)
319 if 'srcmac' in outer_l2:
320 self.set_src_mac(outer_l2['srcmac'])
321 if 'dstmac' in outer_l2:
322 self.set_dst_mac(outer_l2['dstmac'])
323 if 'QinQ' in outer_l2:
324 self.set_qinq(outer_l2['QinQ'])
326 def set_outer_l3v4_fields(self, outer_l3v4):
327 """ setup outer l3v4 fields from traffic profile """
329 if 'proto' in outer_l3v4:
330 ip_params['proto'] = outer_l3v4['proto']
331 if outer_l3v4['proto'] == 'tcp':
332 self.udp_packet = Pkt.TCP()
333 self.udp_dport = 'TCP.dport'
334 self.udp_sport = 'TCP.sport'
335 tcp_params = {'flags': '', 'window': 0}
336 self._set_udp_fields(**tcp_params)
337 if 'ttl' in outer_l3v4:
338 ip_params['ttl'] = outer_l3v4['ttl']
339 self._set_ip_fields(**ip_params)
340 if 'dscp' in outer_l3v4:
341 self.set_dscp(outer_l3v4['dscp'])
342 if 'srcip4' in outer_l3v4:
343 self.set_src_ip4(outer_l3v4['srcip4'], outer_l3v4['count'])
344 if 'dstip4' in outer_l3v4:
345 self.set_dst_ip4(outer_l3v4['dstip4'], outer_l3v4['count'])
347 def set_outer_l3v6_fields(self, outer_l3v6):
348 """ setup outer l3v6 fields from traffic profile """
349 ether_params = {'type': 0x86dd}
350 self._set_ether_fields(**ether_params)
352 if 'proto' in outer_l3v6:
353 ip6_params['proto'] = outer_l3v6['proto']
354 if outer_l3v6['proto'] == 'tcp':
355 self.udp_packet = Pkt.TCP()
356 self.udp_dport = 'TCP.dport'
357 self.udp_sport = 'TCP.sport'
358 tcp_params = {'flags': '', 'window': 0}
359 self._set_udp_fields(**tcp_params)
360 if 'ttl' in outer_l3v6:
361 ip6_params['ttl'] = outer_l3v6['ttl']
362 if 'tc' in outer_l3v6:
363 ip6_params['tc'] = outer_l3v6['tc']
364 if 'hlim' in outer_l3v6:
365 ip6_params['hlim'] = outer_l3v6['hlim']
366 self._set_ip6_fields(**ip6_params)
367 if 'srcip6' in outer_l3v6:
368 self.set_src_ip6(outer_l3v6['srcip6'])
369 if 'dstip6' in outer_l3v6:
370 self.set_dst_ip6(outer_l3v6['dstip6'])
372 def set_outer_l4_fields(self, outer_l4):
373 """ setup outer l4 fields from traffic profile """
374 if 'srcport' in outer_l4:
375 self.set_src_port(outer_l4['srcport'], outer_l4['count'])
376 if 'dstport' in outer_l4:
377 self.set_dst_port(outer_l4['dstport'], outer_l4['count'])
379 def generate_imix_data(self, packet_definition):
380 """ generate packet size for a given traffic profile """
383 if not packet_definition:
385 imix = packet_definition.get('framesize')
389 imix_data[int(size[:-1])] = int(data)
390 imix_sum = sum(imix_data.values())
392 raise SystemExit("Error in IMIX data")
394 imix_data[64] = imix_data.get(64, 0) + (100 - imix_sum)
397 for size in imix_data:
398 count = int(imix_data[size])
400 avg_size += round(size * count / 100, 2)
401 pps = round(self.pps * count / 100, 0)
402 imix_count[size] = pps
403 self.rate = round(1342177280 / avg_size, 0) * 2
404 logging.debug("Imax: %s rate: %s", imix_count, self.rate)
407 def get_streams(self, profile_data):
408 """ generate trex stream
413 self.pps = self.params['traffic_profile'].get('frame_rate', 100)
414 for packet_name in profile_data:
415 outer_l2 = profile_data[packet_name].get('outer_l2')
416 imix_data = self.generate_imix_data(outer_l2)
418 imix_data = {64: self.pps}
419 self.generate_vm(profile_data[packet_name])
420 for size in imix_data:
421 self._generate_streams(size, imix_data[size])
422 self._generate_profile()
425 def generate_vm(self, packet_definition):
426 """ generate trex vm with flows setup """
427 self.ether_packet = Pkt.Ether()
428 self.ip_packet = Pkt.IP()
429 self.ip6_packet = None
430 self.udp_packet = Pkt.UDP()
431 self.udp_dport = 'UDP.dport'
432 self.udp_sport = 'UDP.sport'
434 self.vm_flow_vars = []
435 outer_l2 = packet_definition.get('outer_l2', None)
436 outer_l3v4 = packet_definition.get('outer_l3v4', None)
437 outer_l3v6 = packet_definition.get('outer_l3v6', None)
438 outer_l4 = packet_definition.get('outer_l4', None)
440 self.set_outer_l2_fields(outer_l2)
442 self.set_outer_l3v4_fields(outer_l3v4)
444 self.set_outer_l3v6_fields(outer_l3v6)
446 self.set_outer_l4_fields(outer_l4)
447 self.trex_vm = STLScVmRaw(self.vm_flow_vars)
449 def generate_packets(self):
450 """ generate packets from trex TG """
451 base_pkt = self.base_pkt
452 size = self.fsize - 4
453 pad = max(0, size - len(base_pkt)) * 'x'
454 self.packets = [STLPktBuilder(pkt=base_pkt / pad,
455 vm=vm) for vm in self.vms]
457 def _create_single_packet(self, size=64):
459 ether_packet = self.ether_packet
460 ip_packet = self.ip6_packet if self.ip6_packet else self.ip_packet
461 udp_packet = self.udp_packet
463 qinq_packet = self.qinq_packet
464 base_pkt = ether_packet / qinq_packet / ip_packet / udp_packet
466 base_pkt = ether_packet / ip_packet / udp_packet
467 pad = max(0, size - len(base_pkt)) * 'x'
468 packet = STLPktBuilder(pkt=base_pkt / pad, vm=self.trex_vm)
471 def _create_single_stream(self, packet_size, pps, isg=0):
472 packet = self._create_single_packet(packet_size)
475 stl_flow = STLFlowLatencyStats(pg_id=self.pg_id)
476 stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps),
479 stream = STLStream(isg=isg, packet=packet, mode=STLTXCont(pps=pps))
482 def _generate_streams(self, packet_size, pps):
483 self.streams.append(self._create_single_stream(packet_size, pps))
485 def _generate_profile(self):
486 self.profile = STLProfile(self.streams)
489 def _get_start_end_ipv6(cls, start_ip, end_ip):
491 ip1 = socket.inet_pton(socket.AF_INET6, start_ip)
492 ip2 = socket.inet_pton(socket.AF_INET6, end_ip)
493 hi1, lo1 = struct.unpack('!QQ', ip1)
494 hi2, lo2 = struct.unpack('!QQ', ip2)
495 if ((hi1 << 64) | lo1) > ((hi2 << 64) | lo2):
496 raise SystemExit("IPv6: start_ip is greater then end_ip")
497 max_p1 = abs(int(lo1) - int(lo2))
499 except Exception as ex_error:
500 raise SystemExit(ex_error)
502 return base_p1, max_p1 + base_p1
505 def _get_random_value(cls, min_port, max_port):
506 cryptogen = SystemRandom()
507 return cryptogen.randrange(min_port, max_port)