blob: 5e57abab06b943fc4a1227b55a2e35af892630b9 [file] [log] [blame]
Dan Talaycoc901f4d2010-03-07 21:55:45 -08001
Dan Talaycod2ca1032010-03-10 14:40:26 -08002import sys
Dan Talayco92c99122010-06-03 13:53:18 -07003import copy
Dan Talaycod2ca1032010-03-10 14:40:26 -08004
5try:
6 import scapy.all as scapy
7except:
8 try:
9 import scapy as scapy
10 except:
11 sys.exit("Need to install scapy for packet parsing")
Dan Talayco41eae8b2010-03-10 13:57:06 -080012
Dan Talaycoc901f4d2010-03-07 21:55:45 -080013import oftest.controller as controller
14import oftest.cstruct as ofp
15import oftest.message as message
16import oftest.dataplane as dataplane
17import oftest.action as action
Dan Talayco41eae8b2010-03-10 13:57:06 -080018import oftest.parse as parse
Dan Talaycoc901f4d2010-03-07 21:55:45 -080019import logging
Dan Talayco4b2bee62010-07-20 14:10:05 -070020import types
Dan Talaycoc901f4d2010-03-07 21:55:45 -080021
Dan Talayco551befa2010-07-15 17:05:32 -070022# Some useful defines
23IP_ETHERTYPE = 0x800
24TCP_PROTOCOL = 0x6
25UDP_PROTOCOL = 0x11
26
Dan Talayco98fada92010-07-17 00:36:21 -070027def clear_switch(parent, port_list, logger):
28 """
29 Clear the switch configuration
30
31 @param parent Object implementing controller and assert equal
32 @param logger Logging object
33 """
34 for port in port_list:
35 clear_port_config(parent, port, logger)
36 delete_all_flows(parent.controller, logger)
37
Dan Talayco41eae8b2010-03-10 13:57:06 -080038def delete_all_flows(ctrl, logger):
39 """
40 Delete all flows on the switch
41 @param ctrl The controller object for the test
42 @param logger Logging object
43 """
44
Dan Talaycoc901f4d2010-03-07 21:55:45 -080045 logger.info("Deleting all flows")
46 msg = message.flow_mod()
47 msg.match.wildcards = ofp.OFPFW_ALL
Dan Talayco41eae8b2010-03-10 13:57:06 -080048 msg.out_port = ofp.OFPP_NONE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080049 msg.command = ofp.OFPFC_DELETE
50 msg.buffer_id = 0xffffffff
Dan Talayco41eae8b2010-03-10 13:57:06 -080051 return ctrl.message_send(msg)
52
Dan Talayco98fada92010-07-17 00:36:21 -070053def clear_port_config(parent, port, logger):
54 """
55 Clear the port configuration (currently only no flood setting)
56
57 @param parent Object implementing controller and assert equal
58 @param logger Logging object
59 """
60 rv = port_config_set(parent.controller, port,
61 0, ofp.OFPPC_NO_FLOOD, logger)
62 self.assertEqual(rv, 0, "Failed to reset port config")
63
Dan Talayco41eae8b2010-03-10 13:57:06 -080064def simple_tcp_packet(pktlen=100,
65 dl_dst='00:01:02:03:04:05',
66 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070067 dl_vlan_enable=False,
68 dl_vlan=0,
69 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070070 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080071 ip_src='192.168.0.1',
72 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070073 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080074 tcp_sport=1234,
75 tcp_dport=80
76 ):
77 """
78 Return a simple dataplane TCP packet
79
80 Supports a few parameters:
81 @param len Length of packet in bytes w/o CRC
82 @param dl_dst Destinatino MAC
83 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070084 @param dl_vlan_enable True if the packet is with vlan, False otherwise
85 @param dl_vlan VLAN ID
86 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080087 @param ip_src IP source
88 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -070089 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -080090 @param tcp_dport TCP destination port
91 @param ip_sport TCP source port
92
93 Generates a simple TCP request. Users
94 shouldn't assume anything about this packet other than that
95 it is a valid ethernet/IP/TCP frame.
96 """
Dan Talayco551befa2010-07-15 17:05:32 -070097 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -070098 if (dl_vlan_enable):
99 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -0700100 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700101 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
102 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
103 else:
104 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
105 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
106 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
107
Dan Talayco41eae8b2010-03-10 13:57:06 -0800108 pkt = pkt/("D" * (pktlen - len(pkt)))
109
110 return pkt
111
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700112def simple_icmp_packet(pktlen=60,
113 dl_dst='00:01:02:03:04:05',
114 dl_src='00:06:07:08:09:0a',
115 dl_vlan_enable=False,
116 dl_vlan=0,
117 dl_vlan_pcp=0,
118 ip_src='192.168.0.1',
119 ip_dst='192.168.0.2',
120 ip_tos=0,
121 icmp_type=8,
122 icmp_code=0
123 ):
124 """
125 Return a simple ICMP packet
126
127 Supports a few parameters:
128 @param len Length of packet in bytes w/o CRC
129 @param dl_dst Destinatino MAC
130 @param dl_src Source MAC
131 @param dl_vlan_enable True if the packet is with vlan, False otherwise
132 @param dl_vlan VLAN ID
133 @param dl_vlan_pcp VLAN priority
134 @param ip_src IP source
135 @param ip_dst IP destination
136 @param ip_tos IP ToS
137 @param icmp_type ICMP type
138 @param icmp_code ICMP code
139
140 Generates a simple ICMP ECHO REQUEST. Users
141 shouldn't assume anything about this packet other than that
142 it is a valid ethernet/ICMP frame.
143 """
144 if (dl_vlan_enable):
145 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
146 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
147 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
148 scapy.ICMP(type=icmp_type, code=icmp_code)
149 else:
150 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
151 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
152 scapy.ICMP(type=icmp_type, code=icmp_code)
153
154 pkt = pkt/("0" * (pktlen - len(pkt)))
155
156 return pkt
157
Dan Talayco41eae8b2010-03-10 13:57:06 -0800158def do_barrier(ctrl):
159 b = message.barrier_request()
160 ctrl.transact(b)
Dan Talayco92c99122010-06-03 13:53:18 -0700161
162
163def port_config_get(controller, port_no, logger):
164 """
165 Get a port's configuration
166
167 Gets the switch feature configuration and grabs one port's
168 configuration
169
170 @returns (hwaddr, config, advert) The hwaddress, configuration and
171 advertised values
172 """
173 request = message.features_request()
174 reply, pkt = controller.transact(request, timeout=2)
175 logger.debug(reply.show())
176 if reply is None:
177 logger.warn("Get feature request failed")
178 return None, None, None
179 for idx in range(len(reply.ports)):
180 if reply.ports[idx].port_no == port_no:
181 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
182 reply.ports[idx].advertised)
183
184 logger.warn("Did not find port number for port config")
185 return None, None, None
186
187def port_config_set(controller, port_no, config, mask, logger):
188 """
189 Set the port configuration according the given parameters
190
191 Gets the switch feature configuration and updates one port's
192 configuration value according to config and mask
193 """
194 logger.info("Setting port " + str(port_no) + " to config " + str(config))
195 request = message.features_request()
196 reply, pkt = controller.transact(request, timeout=2)
197 if reply is None:
198 return -1
199 logger.debug(reply.show())
200 for idx in range(len(reply.ports)):
201 if reply.ports[idx].port_no == port_no:
202 break
203 if idx >= len(reply.ports):
204 return -1
205 mod = message.port_mod()
206 mod.port_no = port_no
207 mod.hw_addr = reply.ports[idx].hw_addr
208 mod.config = config
209 mod.mask = mask
210 mod.advertise = reply.ports[idx].advertised
211 rv = controller.message_send(mod)
212 return rv
213
214def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger):
215 """
216 Check for proper receive packets across all ports
217 @param dataplane The dataplane object
218 @param pkt Expected packet; may be None if yes_ports is empty
219 @param yes_ports Set or list of ports that should recieve packet
220 @param no_ports Set or list of ports that should not receive packet
221 @param assert_if Object that implements assertXXX
222 """
223 for ofport in yes_ports:
224 logger.debug("Checking for pkt on port " + str(ofport))
225 (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
226 port_number=ofport, timeout=1)
227 assert_if.assertTrue(rcv_pkt is not None,
228 "Did not receive pkt on " + str(ofport))
229 assert_if.assertEqual(str(pkt), str(rcv_pkt),
230 "Response packet does not match send packet " +
231 "on port " + str(ofport))
232
233 for ofport in no_ports:
234 logger.debug("Negative check for pkt on port " + str(ofport))
235 (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
236 port_number=ofport, timeout=1)
237 assert_if.assertTrue(rcv_pkt is None,
238 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700239
240
241def receive_pkt_verify(parent, egr_port, exp_pkt):
242 """
243 Receive a packet and verify it matches an expected value
244
245 parent must implement dataplane, assertTrue and assertEqual
246 """
247 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(port_number=egr_port,
248 timeout=1)
249 if rcv_pkt is None:
250 parent.logger.error("ERROR: No packet received from " + str(egr_port))
251
252 parent.assertTrue(rcv_pkt is not None,
253 "Did not receive packet port " + str(egr_port))
254 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
255 str(rcv_port))
256
257 if str(exp_pkt) != str(rcv_pkt):
258 parent.logger.error("ERROR: Packet match failed.")
259 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
260 + str(exp_pkt).encode('hex'))
261 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
262 + str(rcv_pkt).encode('hex'))
263 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
264 "Packet match error on port " + str(egr_port))
265
266def match_verify(parent, req_match, res_match):
267 """
268 Verify flow matches agree; if they disagree, report where
269
270 parent must implement assertEqual
271 Use str() to ensure content is compared and not pointers
272 """
273
274 parent.assertEqual(req_match.wildcards, res_match.wildcards,
275 'Match failed: wildcards: ' + hex(req_match.wildcards) +
276 " != " + hex(res_match.wildcards))
277 parent.assertEqual(req_match.in_port, res_match.in_port,
278 'Match failed: in_port: ' + str(req_match.in_port) +
279 " != " + str(res_match.in_port))
280 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
281 'Match failed: dl_src: ' + str(req_match.dl_src) +
282 " != " + str(res_match.dl_src))
283 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
284 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
285 " != " + str(res_match.dl_dst))
286 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
287 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
288 " != " + str(res_match.dl_vlan))
289 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
290 'Match failed: dl_vlan_pcp: ' +
291 str(req_match.dl_vlan_pcp) + " != " +
292 str(res_match.dl_vlan_pcp))
293 parent.assertEqual(req_match.dl_type, res_match.dl_type,
294 'Match failed: dl_type: ' + str(req_match.dl_type) +
295 " != " + str(res_match.dl_type))
296
297 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
298 and (req_match.dl_type == IP_ETHERTYPE)):
299 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
300 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
301 " != " + str(res_match.nw_tos))
302 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
303 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
304 " != " + str(res_match.nw_proto))
305 parent.assertEqual(req_match.nw_src, res_match.nw_src,
306 'Match failed: nw_src: ' + str(req_match.nw_src) +
307 " != " + str(res_match.nw_src))
308 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
309 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
310 " != " + str(res_match.nw_dst))
311
312 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
313 and ((req_match.nw_proto == TCP_PROTOCOL)
314 or (req_match.nw_proto == UDP_PROTOCOL))):
315 parent.assertEqual(req_match.tp_src, res_match.tp_src,
316 'Match failed: tp_src: ' +
317 str(req_match.tp_src) +
318 " != " + str(res_match.tp_src))
319 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
320 'Match failed: tp_dst: ' +
321 str(req_match.tp_dst) +
322 " != " + str(res_match.tp_dst))
323
324def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
325 """
326 Receive a flow removed msg and verify it matches expected
327
328 @params parent Must implement controller, assertEqual
329 @param pkt_count If >= 0, verify packet count
330 @param byte_count If >= 0, verify byte count
331 """
332 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
333 parent.assertTrue(response is not None, 'No flow removed message received')
334
335 if request is None:
336 return
337
338 parent.assertEqual(request.cookie, response.cookie,
339 "Flow removed cookie error: " +
340 hex(request.cookie) + " != " + hex(response.cookie))
341
342 req_match = request.match
343 res_match = response.match
344 verifyMatchField(req_match, res_match)
345
346 if (req_match.wildcards != 0):
347 parent.assertEqual(request.priority, response.priority,
348 'Flow remove prio mismatch: ' +
349 str(request,priority) + " != " +
350 str(response.priority))
351 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
352 'Flow remove reason is not HARD TIMEOUT:' +
353 str(response.reason))
354 if pkt_count >= 0:
355 parent.assertEqual(response.packet_count, pkt_count,
356 'Flow removed failed, packet count: ' +
357 str(response.packet_count) + " != " +
358 str(pkt_count))
359 if byte_count >= 0:
360 parent.assertEqual(response.byte_count, byte_count,
361 'Flow removed failed, byte count: ' +
362 str(response.byte_count) + " != " +
363 str(byte_count))
364
365def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
366 egr_port=None, egr_queue=None, check_expire=False):
367 """
368 Create a flow message
369
370 Match on packet with given wildcards.
371 See flow_match_test for other parameter descriptoins
372 @param egr_queue if not None, make the output an enqueue action
373 """
374 match = parse.packet_to_flow_match(pkt)
375 parent.assertTrue(match is not None, "Flow match from pkt failed")
376 match.wildcards = wildcards
377 match.in_port = ing_port
378
379 request = message.flow_mod()
380 request.match = match
381 request.buffer_id = 0xffffffff
382 if check_expire:
383 request.flags |= ofp.OFPFF_SEND_FLOW_REM
384 request.hard_timeout = 1
385
386 if action_list is not None:
387 for act in action_list:
388 parent.logger.debug("Adding action " + act.show())
389 rv = request.actions.add(act)
390 parent.assertTrue(rv, "Could not add action" + act.show())
391
392 # Set up output/enqueue action if directed
393 if egr_queue is not None:
394 parent.assertTrue(egr_port is not None, "Egress port not set")
395 act = action.action_enqueue()
396 act.port = egr_port
397 act.queue_id = egr_queue
398 rv = request.actions.add(act)
399 parent.assertTrue(rv, "Could not add enqueue action " +
400 str(egr_port) + " Q: " + str(egr_queue))
401 elif egr_port is not None:
402 act = action.action_output()
403 act.port = egr_port
404 rv = request.actions.add(act)
405 parent.assertTrue(rv, "Could not add output action " + str(egr_port))
406
407 parent.logger.debug(request.show())
408
409 return request
410
411def flow_msg_install(parent, request, clear_table=True):
412 """
413 Install a flow mod message in the switch
414
415 @param parent Must implement controller, assertEqual, assertTrue
416 @param request The request, all set to go
417 @param clear_table If true, clear the flow table before installing
418 """
419 if clear_table:
420 parent.logger.debug("Clear flow table")
421 rc = delete_all_flows(parent.controller, parent.logger)
422 parent.assertEqual(rc, 0, "Failed to delete all flows")
423 do_barrier(parent.controller)
424
425 parent.logger.debug("Insert flow")
426 rv = parent.controller.message_send(request)
427 parent.assertTrue(rv != -1, "Error installing flow mod")
428 do_barrier(parent.controller)
429
430def flow_match_test_port_pair(parent, ing_port, egr_port, wildcards=0,
431 dl_vlan=-1, pkt=None, exp_pkt=None,
432 action_list=None, check_expire=False):
433 """
434 Flow match test on single TCP packet
435
436 Run test with packet through switch from ing_port to egr_port
437 See flow_match_test for parameter descriptions
438 """
439
440 parent.logger.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
441 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700442 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700443 if pkt is None:
444 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
445
446 request = flow_msg_create(parent, pkt, ing_port=ing_port,
447 wildcards=wildcards, egr_port=egr_port,
448 action_list=action_list)
449
450 flow_msg_install(parent, request)
451
452 parent.logger.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
453 parent.dataplane.send(ing_port, str(pkt))
454
455 if exp_pkt is None:
456 exp_pkt = pkt
457 receive_pkt_verify(parent, egr_port, exp_pkt)
458
459 if check_expire:
460 #@todo Not all HW supports both pkt and byte counters
461 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
462
463def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
464 exp_pkt=None, action_list=None, check_expire=False,
465 max_test=0):
466 """
467 Run flow_match_test_port_pair on all port pairs
468
469 @param max_test If > 0 no more than this number of tests are executed.
470 @param parent Must implement controller, dataplane, assertTrue, assertEqual
471 and logger
472 @param pkt If not None, use this packet for ingress
473 @param wildcards For flow match entry
474 @param dl_vlan If not -1, and pkt is not None, create a pkt w/ VLAN tag
475 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
476 @param action_list Additional actions to add to flow mod
477 @param check_expire Check for flow expiration message
478 """
479 of_ports = port_map.keys()
480 of_ports.sort()
481 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
482 test_count = 0
483
484 for ing_idx in range(len(of_ports)):
485 ingress_port = of_ports[ing_idx]
486 for egr_idx in range(len(of_ports)):
487 if egr_idx == ing_idx:
488 continue
489 egress_port = of_ports[egr_idx]
490 flow_match_test_port_pair(parent, ingress_port, egress_port,
Dan Talayco98fada92010-07-17 00:36:21 -0700491 wildcards=wildcards, dl_vlan=dl_vlan,
492 pkt=pkt, exp_pkt=exp_pkt,
493 action_list=action_list,
Dan Talayco551befa2010-07-15 17:05:32 -0700494 check_expire=check_expire)
495 test_count += 1
496 if (max_test > 0) and (test_count > max_test):
497 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
498 return
499
Dan Talayco4b2bee62010-07-20 14:10:05 -0700500def test_param_get(config, key, default=None):
501 """
502 Return value passed via test-params if present
503
504 @param config The configuration structure for OFTest
505 @param key The lookup key
506 @param default Default value to use if not found
507
508 If the pair 'key=val' appeared in the string passed to --test-params
509 on the command line, return val (as interpreted by exec). Otherwise
510 return default value.
511 """
512 try:
513 exec config["test_params"]
514 except:
515 return default
516
517 s = "val = " + str(key)
518 try:
519 exec s
520 return val
521 except:
522 return default
523
524def action_generate(parent, field_to_mod, mod_field_vals):
525 """
526 Create an action to modify the field indicated in field_to_mod
527
528 @param parent Must implement, assertTrue
529 @param field_to_mod The field to modify as a string name
530 @param mod_field_vals Hash of values to use for modified values
531 """
532
533 act = None
534
535 if field_to_mod in ['pktlen']:
536 return None
537
538 if field_to_mod == 'dl_dst':
539 act = action.action_set_dl_dst()
540 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
541 elif field_to_mod == 'dl_src':
542 act = action.action_set_dl_src()
543 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
544 elif field_to_mod == 'dl_vlan_enable':
545 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
546 act = action.action_strip_vlan()
547 # Add VLAN tag is handled by dl_vlan field
548 # Will return None in this case
549 elif field_to_mod == 'dl_vlan':
550 act = action.action_set_vlan_vid()
551 act.vlan_vid = mod_field_vals['dl_vlan']
552 elif field_to_mod == 'dl_vlan_pcp':
553 act = action.action_set_vlan_pcp()
554 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
555 elif field_to_mod == 'ip_src':
556 act = action.action_set_nw_src()
557 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
558 elif field_to_mod == 'ip_dst':
559 act = action.action_set_nw_dst()
560 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
561 elif field_to_mod == 'ip_tos':
562 act = action.action_set_nw_tos()
563 act.nw_tos = mod_field_vals['ip_tos']
564 elif field_to_mod == 'tcp_sport':
565 act = action.action_set_tp_src()
566 act.tp_port = mod_field_vals['tcp_sport']
567 elif field_to_mod == 'tcp_dport':
568 act = action.action_set_tp_dst()
569 act.tp_port = mod_field_vals['tcp_dport']
570 else:
571 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
572
573 return act
574
575def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
576 mod_fields={}, check_test_params=False):
577 """
578 Set up the ingress and expected packet and action list for a test
579
580 @param parent Must implement, assertTrue, config hash and logger
581 @param start_field_values Field values to use for ingress packet (optional)
582 @param mod_field_values Field values to use for modified packet (optional)
583 @param mod_fields The list of fields to be modified by the switch in the test.
584 @params check_test_params If True, will check the parameters vid, add_vlan
585 and strip_vlan from the command line.
586
587 Returns a triple: pkt-to-send, expected-pkt, action-list
588 """
589
590 new_actions = []
591
592
593 base_pkt_params = {}
594 base_pkt_params['pktlen'] = 100
595 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
596 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
597 base_pkt_params['dl_vlan_enable'] = False
598 base_pkt_params['dl_vlan'] = 2
599 base_pkt_params['dl_vlan_pcp'] = 0
600 base_pkt_params['ip_src'] = '192.168.0.1'
601 base_pkt_params['ip_dst'] = '192.168.0.2'
602 base_pkt_params['ip_tos'] = 0
603 base_pkt_params['tcp_sport'] = 1234
604 base_pkt_params['tcp_dport'] = 80
605 for keyname in start_field_vals.keys():
606 base_pkt_params[keyname] = start_field_vals[keyname]
607
608 mod_pkt_params = {}
609 mod_pkt_params['pktlen'] = 100
610 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
611 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
612 mod_pkt_params['dl_vlan_enable'] = False
613 mod_pkt_params['dl_vlan'] = 3
614 mod_pkt_params['dl_vlan_pcp'] = 7
615 mod_pkt_params['ip_src'] = '10.20.30.40'
616 mod_pkt_params['ip_dst'] = '50.60.70.80'
617 mod_pkt_params['ip_tos'] = 0xf0
618 mod_pkt_params['tcp_sport'] = 4321
619 mod_pkt_params['tcp_dport'] = 8765
620 for keyname in mod_field_vals.keys():
621 mod_pkt_params[keyname] = mod_field_vals[keyname]
622
623 # Check for test param modifications
624 strip = False
625 if check_test_params:
626 add_vlan = test_param_get(parent.config, 'add_vlan')
627 strip_vlan = test_param_get(parent.config, 'strip_vlan')
628 vid = test_param_get(parent.config, 'vid')
629
630 if add_vlan and strip_vlan:
631 parent.assertTrue(0, "Add and strip VLAN both specified")
632
633 if vid:
634 base_pkt_params['dl_vlan_enable'] = True
635 base_pkt_params['dl_vlan'] = vid
636 if 'dl_vlan' in mod_fields:
637 mod_pkt_params['dl_vlan'] = vid + 1
638
639 if add_vlan:
640 base_pkt_params['dl_vlan_enable'] = False
641 mod_pkt_params['dl_vlan_enable'] = True
642 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
643 mod_fields.append('pktlen')
644 mod_fields.append('dl_vlan_enable')
645 if 'dl_vlan' not in mod_fields:
646 mod_fields.append('dl_vlan')
647 elif strip_vlan:
648 base_pkt_params['dl_vlan_enable'] = True
649 mod_pkt_params['dl_vlan_enable'] = False
650 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
651 mod_fields.append('dl_vlan_enable')
652 mod_fields.append('pktlen')
653
654 # Build the ingress packet
655 ingress_pkt = simple_tcp_packet(**base_pkt_params)
656
657 # Build the expected packet, modifying the indicated fields
658 for item in mod_fields:
659 base_pkt_params[item] = mod_pkt_params[item]
660 act = action_generate(parent, item, mod_pkt_params)
661 if act:
662 new_actions.append(act)
663
664 expected_pkt = simple_tcp_packet(**base_pkt_params)
665
666 return (ingress_pkt, expected_pkt, new_actions)
667