blob: 421c7ce7e3201218890559ef9e9c373c10995021 [file] [log] [blame]
Qi Jiang73b25352017-08-02 19:56:31 +00001#!/usr/bin/env python3.4
2#
3# Copyright 2017 Google, Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import logging
Qi Jiang73b25352017-08-02 19:56:31 +000018import time
19from acts import utils
20from acts.controllers import monsoon
Qi Jiangcaca55e2017-10-10 23:10:40 +000021from acts.libs.proc import job
Qi Jiang7f6dc982018-02-24 01:29:29 +000022from acts.controllers.ap_lib import bridge_interface as bi
Qi Jiang73b25352017-08-02 19:56:31 +000023from acts.test_utils.wifi import wifi_test_utils as wutils
Qi Jiang8f9c7582017-09-06 21:00:14 +000024from bokeh.layouts import layout
Qi Jiang73b25352017-08-02 19:56:31 +000025from bokeh.models import CustomJS, ColumnDataSource
Omar El Ayachf4f132b2018-01-26 03:37:08 +000026from bokeh.models import tools as bokeh_tools
Qi Jiang73b25352017-08-02 19:56:31 +000027from bokeh.models.widgets import DataTable, TableColumn
Qi Jiang8f9c7582017-09-06 21:00:14 +000028from bokeh.plotting import figure, output_file, save
Qi Jiang73b25352017-08-02 19:56:31 +000029from acts.controllers.ap_lib import hostapd_security
30from acts.controllers.ap_lib import hostapd_ap_preset
Daniel Barros43760302017-12-06 04:16:03 +000031
Daniel Barrosfefd8c32017-08-04 15:36:40 -070032# http://www.secdev.org/projects/scapy/
33# On ubuntu, sudo pip3 install scapy-python3
34import scapy.all as scapy
Qi Jiang73b25352017-08-02 19:56:31 +000035
Daniel Barrosebbf91a2017-10-04 01:08:40 +000036GET_FROM_PHONE = 'get_from_dut'
37GET_FROM_AP = 'get_from_ap'
Qi Jiang487e7582018-04-24 08:10:12 -070038ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
39MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
Qi Jiang73b25352017-08-02 19:56:31 +000040
41
Qi Jiang73b25352017-08-02 19:56:31 +000042def monsoon_data_plot(mon_info, file_path, tag=""):
43 """Plot the monsoon current data using bokeh interactive plotting tool.
44
45 Plotting power measurement data with bokeh to generate interactive plots.
46 You can do interactive data analysis on the plot after generating with the
47 provided widgets, which make the debugging much easier. To realize that,
48 bokeh callback java scripting is used. View a sample html output file:
49 https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA
50
51 Args:
Tom Turney35d297e2018-05-11 13:37:41 -070052 mon_info: obj with information of monsoon measurement, including
Qi Jiang73b25352017-08-02 19:56:31 +000053 monsoon device object, measurement frequency, duration and
54 offset etc.
55 file_path: the path to the monsoon log file with current data
56
57 Returns:
58 plot: the plotting object of bokeh, optional, will be needed if multiple
59 plots will be combined to one html file.
60 dt: the datatable object of bokeh, optional, will be needed if multiple
61 datatables will be combined to one html file.
62 """
63
64 log = logging.getLogger()
65 log.info("Plot the power measurement data")
66 #Get results as monsoon data object from the input file
67 results = monsoon.MonsoonData.from_text_file(file_path)
68 #Decouple current and timestamp data from the monsoon object
69 current_data = []
70 timestamps = []
71 voltage = results[0].voltage
72 [current_data.extend(x.data_points) for x in results]
73 [timestamps.extend(x.timestamps) for x in results]
Tom Turney35d297e2018-05-11 13:37:41 -070074 period = 1 / float(mon_info.freq)
Qi Jiang73b25352017-08-02 19:56:31 +000075 time_relative = [x * period for x in range(len(current_data))]
76 #Calculate the average current for the test
77 current_data = [x * 1000 for x in current_data]
78 avg_current = sum(current_data) / len(current_data)
79 color = ['navy'] * len(current_data)
80
81 #Preparing the data and source link for bokehn java callback
Qi Jiang94b289a2018-03-01 03:50:15 +000082 source = ColumnDataSource(
83 data=dict(x0=time_relative, y0=current_data, color=color))
84 s2 = ColumnDataSource(
85 data=dict(
Tom Turney35d297e2018-05-11 13:37:41 -070086 z0=[mon_info.duration],
Qi Jiang94b289a2018-03-01 03:50:15 +000087 y0=[round(avg_current, 2)],
88 x0=[round(avg_current * voltage, 2)],
Tom Turney35d297e2018-05-11 13:37:41 -070089 z1=[round(avg_current * voltage * mon_info.duration, 2)],
90 z2=[round(avg_current * mon_info.duration, 2)]))
Qi Jiang73b25352017-08-02 19:56:31 +000091 #Setting up data table for the output
92 columns = [
93 TableColumn(field='z0', title='Total Duration (s)'),
94 TableColumn(field='y0', title='Average Current (mA)'),
95 TableColumn(field='x0', title='Average Power (4.2v) (mW)'),
96 TableColumn(field='z1', title='Average Energy (mW*s)'),
97 TableColumn(field='z2', title='Normalized Average Energy (mA*s)')
98 ]
99 dt = DataTable(
100 source=s2, columns=columns, width=1300, height=60, editable=True)
101
102 plot_title = file_path[file_path.rfind('/') + 1:-4] + tag
Tom Turney35d297e2018-05-11 13:37:41 -0700103 output_file("%s/%s.html" % (mon_info.data_path, plot_title))
Omar El Ayachf4f132b2018-01-26 03:37:08 +0000104 TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save')
Qi Jiang73b25352017-08-02 19:56:31 +0000105 # Create a new plot with the datatable above
106 plot = figure(
107 plot_width=1300,
108 plot_height=700,
109 title=plot_title,
110 tools=TOOLS,
Omar El Ayachf4f132b2018-01-26 03:37:08 +0000111 output_backend="webgl")
112 plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width"))
113 plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height"))
Qi Jiang73b25352017-08-02 19:56:31 +0000114 plot.line('x0', 'y0', source=source, line_width=2)
115 plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color')
116 plot.xaxis.axis_label = 'Time (s)'
117 plot.yaxis.axis_label = 'Current (mA)'
118 plot.title.text_font_size = {'value': '15pt'}
119
120 #Callback Java scripting
121 source.callback = CustomJS(
122 args=dict(mytable=dt),
123 code="""
124 var inds = cb_obj.get('selected')['1d'].indices;
125 var d1 = cb_obj.get('data');
126 var d2 = mytable.get('source').get('data');
127 ym = 0
128 ts = 0
129 d2['x0'] = []
130 d2['y0'] = []
131 d2['z1'] = []
132 d2['z2'] = []
133 d2['z0'] = []
134 min=max=d1['x0'][inds[0]]
135 if (inds.length==0) {return;}
136 for (i = 0; i < inds.length; i++) {
137 ym += d1['y0'][inds[i]]
138 d1['color'][inds[i]] = "red"
139 if (d1['x0'][inds[i]] < min) {
140 min = d1['x0'][inds[i]]}
141 if (d1['x0'][inds[i]] > max) {
142 max = d1['x0'][inds[i]]}
143 }
144 ym /= inds.length
145 ts = max - min
146 dx0 = Math.round(ym*4.2*100.0)/100.0
147 dy0 = Math.round(ym*100.0)/100.0
148 dz1 = Math.round(ym*4.2*ts*100.0)/100.0
149 dz2 = Math.round(ym*ts*100.0)/100.0
150 dz0 = Math.round(ts*1000.0)/1000.0
151 d2['z0'].push(dz0)
152 d2['x0'].push(dx0)
153 d2['y0'].push(dy0)
154 d2['z1'].push(dz1)
155 d2['z2'].push(dz2)
156 mytable.trigger('change');
157 """)
158
159 #Layout the plot and the datatable bar
160 l = layout([[dt], [plot]])
161 save(l)
162 return [plot, dt]
163
164
Qi Jiang43c80e12017-11-04 03:05:21 +0000165def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10):
Qi Jiang73b25352017-08-02 19:56:31 +0000166 """Function to change the DTIM setting in the phone.
167
168 Args:
169 ad: the target android device, AndroidDevice object
170 gEnableModulatedDTIM: Modulated DTIM, int
171 gMaxLIModulatedDTIM: Maximum modulated DTIM, int
172 """
Qi Jiang487e7582018-04-24 08:10:12 -0700173 # First trying to find the ini file with DTIM settings
174 ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini')
175 ini_file_local = ini_file_phone.split('/')[-1]
176
177 # Pull the file and change the DTIM to desired value
178 ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local))
Qi Jiang73b25352017-08-02 19:56:31 +0000179
180 with open(ini_file_local, 'r') as fin:
181 for line in fin:
Qi Jiang487e7582018-04-24 08:10:12 -0700182 if ENABLED_MODULATED_DTIM in line:
183 gE_old = line.strip('\n')
184 gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n')
185 if MAX_MODULATED_DTIM in line:
186 gM_old = line.strip('\n')
187 gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n')
188 fin.close()
Qi Jiang43c80e12017-11-04 03:05:21 +0000189 if int(gEDTIM_old) == gEnableModulatedDTIM and int(
190 gMDTIM_old) == gMaxLIModulatedDTIM:
Qi Jiang73b25352017-08-02 19:56:31 +0000191 ad.log.info('Current DTIM is already the desired value,'
192 'no need to reset it')
Qi Jiang119d8b82018-05-21 16:53:52 -0700193 return 0
Qi Jiang73b25352017-08-02 19:56:31 +0000194
Qi Jiang487e7582018-04-24 08:10:12 -0700195 gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM)
196 gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM)
Qi Jiang73b25352017-08-02 19:56:31 +0000197
Qi Jiang487e7582018-04-24 08:10:12 -0700198 sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local)
199 sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local)
200 job.run(sed_gE)
201 job.run(sed_gM)
Qi Jiang73b25352017-08-02 19:56:31 +0000202
Qi Jiang487e7582018-04-24 08:10:12 -0700203 # Push the file to the phone
204 push_file_to_phone(ad, ini_file_local, ini_file_phone)
205 ad.log.info('DTIM changes checked in and rebooting...')
206 ad.reboot()
Qi Jiang119d8b82018-05-21 16:53:52 -0700207 # Wait for auto-wifi feature to start
208 time.sleep(20)
Qi Jiang487e7582018-04-24 08:10:12 -0700209 ad.log.info('DTIM updated and device back from reboot')
Qi Jiang119d8b82018-05-21 16:53:52 -0700210 return 1
Qi Jiang487e7582018-04-24 08:10:12 -0700211
212
213def push_file_to_phone(ad, file_local, file_phone):
214 """Function to push local file to android phone.
215
216 Args:
217 ad: the target android device
218 file_local: the locla file to push
219 file_phone: the file/directory on the phone to be pushed
220 """
221 ad.adb.root()
222 cmd_out = ad.adb.remount()
223 if 'Permission denied' in cmd_out:
Qi Jiang73b25352017-08-02 19:56:31 +0000224 ad.log.info('Need to disable verity first and reboot')
Qi Jiang487e7582018-04-24 08:10:12 -0700225 ad.adb.disable_verity()
Qi Jiang73b25352017-08-02 19:56:31 +0000226 time.sleep(1)
227 ad.reboot()
228 ad.log.info('Verity disabled and device back from reboot')
Qi Jiang487e7582018-04-24 08:10:12 -0700229 ad.adb.root()
230 ad.adb.remount()
Qi Jiang73b25352017-08-02 19:56:31 +0000231 time.sleep(1)
Qi Jiang487e7582018-04-24 08:10:12 -0700232 ad.adb.push('{} {}'.format(file_local, file_phone))
Qi Jiang73b25352017-08-02 19:56:31 +0000233
234
Qi Jiangdc073e52017-12-19 19:12:35 +0000235def ap_setup(ap, network, bandwidth=80):
Qi Jiang73b25352017-08-02 19:56:31 +0000236 """Set up the whirlwind AP with provided network info.
237
238 Args:
239 ap: access_point object of the AP
240 network: dict with information of the network, including ssid, password
241 bssid, channel etc.
Qi Jiangdc073e52017-12-19 19:12:35 +0000242 bandwidth: the operation bandwidth for the AP, default 80MHz
Qi Jiang7f6dc982018-02-24 01:29:29 +0000243 Returns:
244 brconfigs: the bridge interface configs
Qi Jiang73b25352017-08-02 19:56:31 +0000245 """
Qi Jiang73b25352017-08-02 19:56:31 +0000246 log = logging.getLogger()
247 bss_settings = []
248 ssid = network[wutils.WifiEnums.SSID_KEY]
Qi Jiangcaca55e2017-10-10 23:10:40 +0000249 if "password" in network.keys():
250 password = network["password"]
251 security = hostapd_security.Security(
252 security_mode="wpa", password=password)
253 else:
254 security = hostapd_security.Security(security_mode=None, password=None)
Qi Jiang73b25352017-08-02 19:56:31 +0000255 channel = network["channel"]
Qi Jiang73b25352017-08-02 19:56:31 +0000256 config = hostapd_ap_preset.create_ap_preset(
257 channel=channel,
258 ssid=ssid,
259 security=security,
260 bss_settings=bss_settings,
Qi Jiangdc073e52017-12-19 19:12:35 +0000261 vht_bandwidth=bandwidth,
262 profile_name='whirlwind',
263 iface_wlan_2g=ap.wlan_2g,
264 iface_wlan_5g=ap.wlan_5g)
Qi Jiang7f6dc982018-02-24 01:29:29 +0000265 config_bridge = ap.generate_bridge_configs(channel)
266 brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1],
267 config_bridge[2])
268 ap.bridge.startup(brconfigs)
Qi Jiang73b25352017-08-02 19:56:31 +0000269 ap.start_ap(config)
270 log.info("AP started on channel {} with SSID {}".format(channel, ssid))
Qi Jiang7f6dc982018-02-24 01:29:29 +0000271 return brconfigs
Qi Jiang73b25352017-08-02 19:56:31 +0000272
273
Omar El Ayachc01e1ef2018-02-28 04:52:49 +0000274def bokeh_plot(data_sets,
275 legends,
276 fig_property,
277 shaded_region=None,
278 output_file_path=None):
Qi Jiang73b25352017-08-02 19:56:31 +0000279 """Plot bokeh figs.
280 Args:
281 data_sets: data sets including lists of x_data and lists of y_data
282 ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]]
283 legends: list of legend for each curve
284 fig_property: dict containing the plot property, including title,
285 lables, linewidth, circle size, etc.
Omar El Ayachc01e1ef2018-02-28 04:52:49 +0000286 shaded_region: optional dict containing data for plot shading
287 output_file_path: optional path at which to save figure
Qi Jiang73b25352017-08-02 19:56:31 +0000288 Returns:
289 plot: bokeh plot figure object
290 """
Omar El Ayachf4f132b2018-01-26 03:37:08 +0000291 TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save')
Qi Jiang73b25352017-08-02 19:56:31 +0000292 plot = figure(
293 plot_width=1300,
294 plot_height=700,
295 title=fig_property['title'],
296 tools=TOOLS,
Omar El Ayachf4f132b2018-01-26 03:37:08 +0000297 output_backend="webgl")
298 plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width"))
299 plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height"))
Qi Jiang73b25352017-08-02 19:56:31 +0000300 colors = [
301 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy',
302 'yellow', 'darkred', 'goldenrod'
303 ]
Omar El Ayachc01e1ef2018-02-28 04:52:49 +0000304 if shaded_region:
305 band_x = shaded_region["x_vector"]
306 band_x.extend(shaded_region["x_vector"][::-1])
307 band_y = shaded_region["lower_limit"]
308 band_y.extend(shaded_region["upper_limit"][::-1])
309 plot.patch(
310 band_x, band_y, color='#7570B3', line_alpha=0.1, fill_alpha=0.1)
311
Qi Jiang73b25352017-08-02 19:56:31 +0000312 for x_data, y_data, legend in zip(data_sets[0], data_sets[1], legends):
313 index_now = legends.index(legend)
314 color = colors[index_now % len(colors)]
315 plot.line(
316 x_data, y_data, legend=str(legend), line_width=3, color=color)
317 plot.circle(
318 x_data, y_data, size=10, legend=str(legend), fill_color=color)
Omar El Ayachc01e1ef2018-02-28 04:52:49 +0000319
Qi Jiang73b25352017-08-02 19:56:31 +0000320 #Plot properties
321 plot.xaxis.axis_label = fig_property['x_label']
322 plot.yaxis.axis_label = fig_property['y_label']
323 plot.legend.location = "top_right"
324 plot.legend.click_policy = "hide"
325 plot.title.text_font_size = {'value': '15pt'}
Omar El Ayachded64232017-12-08 00:53:12 +0000326 if output_file_path is not None:
327 output_file(output_file_path)
328 save(plot)
Qi Jiang73b25352017-08-02 19:56:31 +0000329 return plot
330
331
332def run_iperf_client_nonblocking(ad, server_host, extra_args=""):
333 """Start iperf client on the device with nohup.
334
335 Return status as true if iperf client start successfully.
336 And data flow information as results.
337
338 Args:
339 ad: the android device under test
340 server_host: Address of the iperf server.
341 extra_args: A string representing extra arguments for iperf client,
342 e.g. "-i 1 -t 30".
343
344 """
345 log = logging.getLogger()
346 ad.adb.shell_nb("nohup iperf3 -c {} {} &".format(server_host, extra_args))
347 log.info("IPerf client started")
348
349
350def get_wifi_rssi(ad):
351 """Get the RSSI of the device.
352
353 Args:
354 ad: the android device under test
355 Returns:
356 RSSI: the rssi level of the device
357 """
358 RSSI = ad.droid.wifiGetConnectionInfo()['rssi']
359 return RSSI
Qi Jiang50733382017-08-22 01:11:58 +0000360
361
362def get_phone_ip(ad):
363 """Get the WiFi IP address of the phone.
364
365 Args:
366 ad: the android device under test
367 Returns:
368 IP: IP address of the phone for WiFi, as a string
369 """
370 IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
371
372 return IP
373
374
375def get_phone_mac(ad):
376 """Get the WiFi MAC address of the phone.
377
378 Args:
379 ad: the android device under test
380 Returns:
381 mac: MAC address of the phone for WiFi, as a string
382 """
383 mac = ad.droid.wifiGetConnectionInfo()["mac_address"]
384
385 return mac
386
387
388def get_phone_ipv6(ad):
389 """Get the WiFi IPV6 address of the phone.
390
391 Args:
392 ad: the android device under test
393 Returns:
394 IPv6: IPv6 address of the phone for WiFi, as a string
395 """
396 IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6]
397
398 return IPv6
Daniel Barrosfefd8c32017-08-04 15:36:40 -0700399
400
401def get_if_addr6(intf, address_type):
402 """Returns the Ipv6 address from a given local interface.
403
404 Returns the desired IPv6 address from the interface 'intf' in human
405 readable form. The address type is indicated by the IPv6 constants like
406 IPV6_ADDR_LINKLOCAL, IPV6_ADDR_GLOBAL, etc. If no address is found,
407 None is returned.
408
409 Args:
410 intf: desired interface name
411 address_type: addrees typle like LINKLOCAL or GLOBAL
412
413 Returns:
414 Ipv6 address of the specified interface in human readable format
415 """
416 for if_list in scapy.in6_getifaddr():
417 if if_list[2] == intf and if_list[1] == address_type:
418 return if_list[0]
419
420 return None
Daniel Barrosebbf91a2017-10-04 01:08:40 +0000421
422
Qi Jiangcaca55e2017-10-10 23:10:40 +0000423@utils.timeout(60)
424def wait_for_dhcp(intf):
425 """Wait the DHCP address assigned to desired interface.
426
427 Getting DHCP address takes time and the wait time isn't constant. Utilizing
428 utils.timeout to keep trying until success
429
430 Args:
431 intf: desired interface name
432 Returns:
433 ip: ip address of the desired interface name
434 Raise:
435 TimeoutError: After timeout, if no DHCP assigned, raise
436 """
437 log = logging.getLogger()
438 reset_host_interface(intf)
439 ip = '0.0.0.0'
440 while ip == '0.0.0.0':
441 ip = scapy.get_if_addr(intf)
Qi Jiang0ded53a2018-02-03 06:23:11 +0000442 log.info('DHCP address assigned to {} as {}'.format(intf, ip))
Qi Jiangcaca55e2017-10-10 23:10:40 +0000443 return ip
444
445
446def reset_host_interface(intf):
447 """Reset the host interface.
448
449 Args:
450 intf: the desired interface to reset
451 """
452 log = logging.getLogger()
453 intf_down_cmd = 'ifconfig %s down' % intf
454 intf_up_cmd = 'ifconfig %s up' % intf
455 try:
456 job.run(intf_down_cmd)
Qi Jiang0ded53a2018-02-03 06:23:11 +0000457 time.sleep(10)
Qi Jiangcaca55e2017-10-10 23:10:40 +0000458 job.run(intf_up_cmd)
Qi Jiangcaca55e2017-10-10 23:10:40 +0000459 log.info('{} has been reset'.format(intf))
460 except job.Error:
461 raise Exception('No such interface')
462
463
Daniel Barrosebbf91a2017-10-04 01:08:40 +0000464def create_pkt_config(test_class):
465 """Creates the config for generating multicast packets
466
467 Args:
468 test_class: object with all networking paramters
469
470 Returns:
471 Dictionary with the multicast packet config
472 """
473 addr_type = (scapy.IPV6_ADDR_LINKLOCAL
474 if test_class.ipv6_src_type == 'LINK_LOCAL' else
475 scapy.IPV6_ADDR_GLOBAL)
476
477 mac_dst = test_class.mac_dst
478 if GET_FROM_PHONE in test_class.mac_dst:
479 mac_dst = get_phone_mac(test_class.dut)
480
481 ipv4_dst = test_class.ipv4_dst
482 if GET_FROM_PHONE in test_class.ipv4_dst:
483 ipv4_dst = get_phone_ip(test_class.dut)
484
485 ipv6_dst = test_class.ipv6_dst
486 if GET_FROM_PHONE in test_class.ipv6_dst:
487 ipv6_dst = get_phone_ipv6(test_class.dut)
488
489 ipv4_gw = test_class.ipv4_gwt
490 if GET_FROM_AP in test_class.ipv4_gwt:
491 ipv4_gw = test_class.access_point.ssh_settings.hostname
492
493 pkt_gen_config = {
494 'interf': test_class.pkt_sender.interface,
495 'subnet_mask': test_class.sub_mask,
496 'src_mac': test_class.mac_src,
497 'dst_mac': mac_dst,
498 'src_ipv4': test_class.ipv4_src,
499 'dst_ipv4': ipv4_dst,
500 'src_ipv6': test_class.ipv6_src,
501 'src_ipv6_type': addr_type,
502 'dst_ipv6': ipv6_dst,
503 'gw_ipv4': ipv4_gw
504 }
505 return pkt_gen_config