def generate_test_cases_for_parser_paths(self, parser_paths): Statistics().init() self.total_switch_time = 0.0 self.parser_path_edge_count = defaultdict(int) if Config().get_round_robin_parser_paths(): results = self.generate_test_cases_round_robin(parser_paths) else: results = self.generate_test_cases_linearly(parser_paths) if self.table_solver is not None: self.table_solver.flush() logging.info("Final statistics on use of control path edges:") Statistics().log_control_path_stats( Statistics().stats_per_control_path_edge, Statistics().num_control_path_edges) self.test_case_writer.cleanup() Statistics().dump() Statistics().cleanup() for result, count in Statistics().stats.items(): print('{}: {}'.format(result, count)) if Config().get_dump_test_case(): str_items = [] for (parser_path, control_path), v in results.items(): str_items.append('{}: {}'.format( path_tuple(parser_path, control_path), v)) print('{{ {} }}'.format(', '.join(str_items))) return results
def check_checksum_ipv4_with_options(self): Config().load_test_defaults() # This test case exercises variable-length extract, lookahead, # and verify statements in the parser. results = process_json_file('examples/checksum-ipv4-with-options.json') expected_results = { ('start', u'parse_ipv4', u'parse_tcp', 'sink', (u'node_2', (True, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()'))), (u'node_3', (True, (u'p4_programs/checksum-ipv4-with-options.p4', 130, u'hdr.ipv4.ihl == 14')))): TestPathResult.SUCCESS, ('start', u'parse_ipv4', u'parse_tcp', 'sink', (u'node_2', (True, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()'))), (u'node_3', (False, (u'p4_programs/checksum-ipv4-with-options.p4', 130, u'hdr.ipv4.ihl == 14'))), (u'guh', u'foo')): TestPathResult.SUCCESS, ('start', u'parse_ipv4', u'parse_tcp', 'sink', (u'node_2', (False, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (False, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()')))): TestPathResult.SUCCESS, # TBD Andy: What is this path doing in here? It doesn't # look like a complete path to me for this program. ('start', u'parse_ipv4', 'sink'): TestPathResult.NO_PACKET_FOUND, ('start', 'sink', (u'node_2', (True, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'sink', (u'node_2', (False, (u'p4_programs/checksum-ipv4-with-options.p4', 125, u'hdr.ipv4.isValid() && hdr.tcp.isValid()')))): TestPathResult.SUCCESS } assert results == expected_results
def __init__(self, json_file, pipeline, test_case_writer): # Only need to solve again on flush if randomizing. solve_again = Config().get_randomize() super(TableConsolidatedSolver, self).__init__(json_file, pipeline, test_case_writer, solve_again) self.pipeline = pipeline for table in self.pipeline.tables.values(): assert table.has_const_default_entry(), \ "Tables with non-const defaults are not currently supported" # TODO: Consider implementing push/pop model for table_sym_vals and # table_vars. Harder than in base class as they're not simple lists. # List of consolidated symbolic keys and symbolic action params for the # table to use across all paths. # Currently only allowing a single key per action. self.table_action_sym_vals = { } # {table_name: {action_name: (sym_key, sym_params)}} # Filled with any pending entries during add path, empty otherwise self.pending_table_action_sym_vals = {} # Filled with [(cmd, cmd_data), ...] during flush, None otherwise. self.table_setup_cmds = None # Object for managing table-data variables. self.table_vars = Variables()
def check_demo1b(self): Config().load_test_defaults() results = process_json_file('examples/demo1b.json') expected_results = { ('start', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (False, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (True, (u'p4_programs/demo1b.p4', 143, u'acl_drop')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_drop'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act_0', u'act_0'), (u'ipv4_da_lpm', u'my_drop'), (u'node_8', (True, (u'p4_programs/demo1b.p4', 149, u'meta.fwd_metadata.l2ptr != L2PTR_UNSET')))): TestPathResult.NO_PACKET_FOUND, ('start', 'sink', (u'node_2', (False, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()')))): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_drop'), (u'node_4', (True, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act', u'act')): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act_0', u'act_0'), (u'ipv4_da_lpm', u'my_drop'), (u'node_8', (False, (u'p4_programs/demo1b.p4', 149, u'meta.fwd_metadata.l2ptr != L2PTR_UNSET')))): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act_0', u'act_0'), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_8', (False, (u'p4_programs/demo1b.p4', 149, u'meta.fwd_metadata.l2ptr != L2PTR_UNSET')))): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act_0', u'act_0'), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_8', (True, (u'p4_programs/demo1b.p4', 149, u'meta.fwd_metadata.l2ptr != L2PTR_UNSET'))), (u'mac_da', u'set_bd_dmac_intf')): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo1b.p4', 141, u'hdr.ipv4.isValid()'))), (u'ipv4_acl', u'do_acl_permit'), (u'node_4', (False, (u'p4_programs/demo1b.p4', 143, u'acl_drop'))), (u'tbl_act_0', u'act_0'), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_8', (True, (u'p4_programs/demo1b.p4', 149, u'meta.fwd_metadata.l2ptr != L2PTR_UNSET'))), (u'mac_da', u'my_drop')): TestPathResult.SUCCESS } assert results == expected_results
def preprocess_edges(self, path, edges): if Config().get_random_tlubf(): shuffle(edges) return edges custom_order = sorted( edges, key=lambda t: Statistics().stats_per_control_path_edge[t]) return reversed(custom_order) visited_es = [] unvisited_es = [] path_has_new_edges = False for e in path: if self.labels[e] == EdgeLabels.UNVISITED: path_has_new_edges = True break for e in edges: label = self.labels[e] if label == EdgeLabels.UNVISITED: unvisited_es.append(e) elif label == EdgeLabels.VISITED: visited_es.append(e) else: assert label == EdgeLabels.DONE if path_has_new_edges: visited_es.append(e) # shuffle(visited_es) #shuffle(unvisited_es) return list(reversed(visited_es)) + list(reversed(unvisited_es))
def generate_test_cases(input_file): top = P4_Top() top.load_json_file(input_file) top.build_graph() top.load_extern_backends() num_control_paths, num_control_path_nodes, num_control_path_edges = \ top.in_graph.count_all_paths(top.in_pipeline.init_table_name) num_parser_path_edges = top.parser_graph.num_edges() Statistics( ).num_control_path_edges = num_parser_path_edges + num_control_path_edges graph_visitor = ParserGraphVisitor(top.hlir) parser_paths = [ path for path in top.parser_graph.visit_all_paths( top.hlir.parsers['parser'].init_state, 'sink', graph_visitor) ] max_path_len = max([len(p) for p in parser_paths]) logging.info("Found %d parser paths, longest with length %d" "" % (len(parser_paths), max_path_len)) if Config().get_show_parser_paths(): print_parser_paths(parser_paths) logging.info( "Counted %d paths, %d nodes, %d edges" " in parser + ingress control flow graph" "" % (len(parser_paths) * num_control_paths, num_control_path_nodes, num_parser_path_edges + num_control_path_edges)) generator = TestCaseGenerator(input_file, top) return generator.generate_test_cases_for_parser_paths(parser_paths)
def generate_test_cases_linearly(self, parser_paths): path_solver = PathSolver(self.top, self.top.in_pipeline) results = OrderedDict() for i_path, parser_path in enumerate(parser_paths): self.count_parser_path_edges(parser_path) logging.info("Analyzing parser_path %d of %d: %s" "" % (i_path, len(parser_paths), parser_path)) for path_model in self.iterate_paths_for_parser_path( parser_path, results=results, path_solver=path_solver): for i_solution, path_solution in enumerate( path_model.solutions()): self.process_path_solution(path_solution) logging.info("Processed %d solutions for path" % (i_solution + 1, )) # If we have produced enough test cases overall, enough for this # path, or have exhausted possible packets for this path, move on. if enough_test_cases() or \ (i_solution + 1) == Config().get_max_test_cases_per_path(): break if enough_test_cases(): break if enough_test_cases(): break return results
def generate_test_case_for_path(self, path_solution): path = path_solution.path context = path_solution.context sym_packet = path_solution.sym_packet model = path_solution.model start_time = time.time() build_result, test_case, payloads = \ self.test_case_builder.build_for_path( context, model, sym_packet, path ) assert build_result == path_solution.result test_case[ "time_sec_generate_ingress_constraints"] = path_solution.time_sec_generate_ingress_constraints test_case["time_sec_solve"] = path_solution.time_sec_solve if Config().get_run_simple_switch(): test_result = self.test_case_builder.run_simple_switch( path.expected_path, test_case, payloads, path.is_complete, self.top.in_source_info_to_node_name) assert test_result == path_solution.result self.total_switch_time += time.time() - start_time return (test_case, payloads)
def check_add_remove_header(self): Config().load_test_defaults() results = process_json_file('examples/add-remove-header.json') expected_results = { ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_4', (True, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()'))), (u'mac_da', u'set_bd_dmac_intf')): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_4', (True, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()'))), (u'mac_da', u'my_drop')): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'set_l2ptr'), (u'node_4', (False, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'my_drop'), (u'node_4', (True, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()'))), (u'mac_da', u'set_bd_dmac_intf')): TestPathResult.UNINITIALIZED_READ, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'my_drop'), (u'node_4', (True, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()'))), (u'mac_da', u'my_drop')): TestPathResult.UNINITIALIZED_READ, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'my_drop'), (u'node_4', (False, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'add_outer_ipv4'), (u'node_4', (True, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', u'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()'))), (u'ipv4_da_lpm', u'add_outer_ipv4'), (u'node_4', (False, (u'p4_programs/add-remove-header.p4', 146, u'!hdr.outer_ipv4.isValid()')))): TestPathResult.SUCCESS, ('start', u'parse_ipv4', 'sink', (u'node_2', (False, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'sink', (u'node_2', (True, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'sink', (u'node_2', (False, (u'p4_programs/add-remove-header.p4', 144, u'hdr.ipv4.isValid()')))): TestPathResult.SUCCESS } assert results == expected_results
def iterate_paths_for_parser_path(self, parser_path, results, path_solver=None): if path_solver is None: path_solver = PathSolver(self.top, self.top.in_pipeline) if not path_solver.generate_parser_constraints(parser_path): logging.info("Could not find any packet to satisfy parser path: %s" "" % (parser_path)) # Skip unsatisfiable parser paths return start_node, control_graph = self.get_control_graph() if Config().get_edge_coverage(): graph_visitor = EdgeCoverageGraphVisitor(path_solver, parser_path, results, control_graph) else: graph_visitor = PathCoverageGraphVisitor(path_solver, parser_path, results) for path_model in control_graph.visit_all_paths( start_node, None, graph_visitor): # Only paths that will generate something useful if record_test_case(path_model.result, path_model.path.is_complete): yield path_model
def choose_edge(self, edges): if Config().get_random_tlubf(): return random.choice(edges) edge_counts = [self.path_count[e] for e in edges] min_index, min_value = min(enumerate(edge_counts), key=operator.itemgetter(1)) return edges[min_index]
def __init__(self, input_file, top): self.input_file = input_file self.top = top self.total_switch_time = 0.0 self.parser_path_edge_count = defaultdict(int) self.test_case_builder = TestCaseBuilder(input_file, top.in_pipeline) # TBD: Make this filename specifiable via command line option self.test_case_writer = TestCaseWriter(Config().get_output_json_path(), Config().get_output_pcap_path()) self.table_solver = None if Config().get_do_consolidate_tables(): # TODO: Remove this once these two options are made compatible assert Config().get_max_test_cases_per_path() == 1 self.table_solver = \ TableConsolidatedSolver(self.input_file, self.top.in_pipeline, self.test_case_writer)
def load_instances(self, extern_instances): extern_definitions = Config().get_extern_definitions() for name, instance in extern_instances.items(): assert name in extern_definitions, \ "Extern definition not provided for '{}'".format(name) src_file = extern_definitions[name] args = self.parse_instance_attribute_values(instance) self.extern_backends[name] = self.load_extern_instance( src_file, args)
def solutions(self): extract_vl_variation = Config().get_extract_vl_variation() current_result = self.result solve_time = self.time_sec_initial_solve while current_result != TestPathResult.NO_PACKET_FOUND: assert current_result == self.result # Choose values for randomization variables. random_constraints = [] fix_random = self.path.is_complete if fix_random: self.path_solver.push() random_constraints = self.path_solver.fix_random_constraints() try: path_solution = PathSolution( self.path, self.result, self.path_solver.constraints + [random_constraints], self.path_solver.current_context(), self.path_solver.sym_packet, self.path_solver.solver.model(), time_sec_generate_ingress_constraints=self. time_sec_generate_ingress_constraints, time_sec_solve=solve_time, ) yield path_solution finally: # Clear the constraints on the values of the randomization # variables. if fix_random: self.path_solver.pop() if not self.path_solver.constrain_last_extract_vl_lengths( extract_vl_variation): # Special case: unbounded numbers of test cases are only # safe when we're building up constraints on VL-extraction # lengths, or else we'll loop forever. if not Config().get_max_test_cases_per_path(): break time2 = time.time() current_result = self.path_solver.solve_path() solve_time = time.time()
def visit_result(self, result): # TODO: Fix this option. if ge_than_not_none(self.success_path_count, Config().get_max_paths_per_parser_path()): return VisitResult.BACKTRACK if result != TestPathResult.SUCCESS: return VisitResult.BACKTRACK return VisitResult.CONTINUE
def check_demo1_rm_header(self): Config().load_test_defaults() results = process_json_file('examples/demo1_rm_header.json') expected_results = { ('start', 'parse_ipv4', 'sink', (u'tbl_act', u'act')): TestPathResult.INVALID_HEADER_WRITE, ('start', 'sink', (u'tbl_act', u'act')): TestPathResult.INVALID_HEADER_WRITE } assert results == expected_results
def get_var(self, var_name): if var_name not in self.var_to_smt_val: # The variable that we're reading has not been set by the program. field = self.fields[var_name] new_var = self.fresh_var(var_name, field.size) if field.hdr.metadata and Config().get_solve_for_metadata(): # We're solving for metadata. Set the field to an # unconstrained value. self.set_field_value(var_name[0], var_name[1], new_var) self.input_metadata[var_name] = new_var elif Config().get_allow_uninitialized_reads(): # Read the uninitialized value as zero. return BitVecVal(0, field.size) else: # If the header field has not been initialized, return a # fresh variable for each read access self.uninitialized_reads.append((var_name, self.source_info)) return new_var assert var_name in self.var_to_smt_val return self.var_to_smt_val[var_name]
def get_var(self, var_name): if var_name not in self.var_to_smt_var: if Config().get_allow_uninitialized_reads(): return BitVecVal(0, self.fields[var_name].size) else: # If the header field has not been initialized, return a fresh # variable for each read access self.uninitialized_reads.append((var_name, self.source_info)) return BitVec( self.fresh_var(var_name), self.fields[var_name].size) else: return self.var_to_smt_var[var_name]
def set_field_value(self, header_name, header_field, sym_val): # XXX: clean up if header_field != '$valid$' and (header_name, '$valid$') in self.var_to_smt_val: valid = self.var_to_smt_val[(header_name, '$valid$')] if simplify(valid) == BitVecVal(0, 1): if Config().get_allow_invalid_header_writes(): return else: self.invalid_header_writes.append( ((header_name, header_field), self.source_info)) self.set_field_var(header_name, header_field, sym_val)
def new(self, name, size): """Creates a new variable with the specified name and size, with a random displacement if required. """ var = z3.BitVec(name, size) # If we're randomizing, XOR in a variable that will be set to a random # value before the expression is evaluated. if Config().get_randomize(): disp_name = '$rand_disp$.{}'.format(name) rand_disp = z3.BitVec(disp_name, size) self.rand_disp_vars.append(rand_disp) var ^= rand_disp return var
def __init__(self, json_file, hlir, pipeline): if Config().get_run_simple_switch(): self.json_file = json_file else: self.json_file = None self.solver = Solver() self.solver.push() self.hlir = hlir self.pipeline = pipeline self.context_history = [Context()] # XXX: implement better mechanism self.context_history_lens = [] self.total_solver_time = 0.0 self.total_switch_time = 0.0
def check_parser_impossible_transitions2_with_epl(self): Config().load_test_defaults(no_packet_length_errs=False) # Similar to the previous test case, this test case has # several parser paths that are impossible to traverse, and # several that are possible. results = process_json_file( 'examples/parser-impossible-transitions2.json') expected_results = { ('start', 'sink', (u'node_2', (False, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()')))): TestPathResult.SUCCESS, ('start', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (False, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (False, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()'))), (u'node_15', (False, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (False, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()'))), (u'node_15', (True, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff'))), (u'tbl_parserimpossibletransitions2l130', u'parserimpossibletransitions2l130')): TestPathResult.SUCCESS, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (True, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (True, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (True, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (True, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'tbl_parserimpossibletransitions2l115', u'parserimpossibletransitions2l115'), (u'node_15', (False, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff'))), (u'tbl_parserimpossibletransitions2l132', u'parserimpossibletransitions2l132')): TestPathResult.SUCCESS, ('start', 'parse_h5', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (True, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'tbl_parserimpossibletransitions2l115', u'parserimpossibletransitions2l115'), (u'node_15', (True, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (False, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (False, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()'))), (u'node_15', (False, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (False, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()'))), (u'node_15', (True, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff'))), (u'tbl_parserimpossibletransitions2l130', u'parserimpossibletransitions2l130')): TestPathResult.SUCCESS, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (False, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()'))), (u'node_8', (True, (u'parser-impossible-transitions2.p4', 116, u'hdr.h2.isValid() || hdr.h3.isValid() || hdr.h4.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'node_6', (True, (u'parser-impossible-transitions2.p4', 114, u'hdr.h5.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (True, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (False, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (True, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'tbl_parserimpossibletransitions2l113', u'parserimpossibletransitions2l113'), (u'node_15', (False, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff'))), (u'tbl_parserimpossibletransitions2l132', u'parserimpossibletransitions2l132')): TestPathResult.SUCCESS, ('start', 'parse_h1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions2.p4', 110, u'hdr.ethernet.isValid()'))), (u'tbl_parserimpossibletransitions2l111', u'parserimpossibletransitions2l111'), (u'node_4', (True, (u'parser-impossible-transitions2.p4', 112, u'hdr.h1.isValid()'))), (u'tbl_parserimpossibletransitions2l113', u'parserimpossibletransitions2l113'), (u'node_15', (True, (u'parser-impossible-transitions2.p4', 126, u'hdr.ethernet.dstAddr == 0xffffffff')))): TestPathResult.NO_PACKET_FOUND } assert results == expected_results
def set_field_value(self, header_name, header_field, sym_val): var_name = '{}.{}'.format(header_name, header_field) do_write = True # XXX: clean up if header_field != '$valid$' and ('{}.{}'.format( header_name, '$valid$') in self.sym_vars) and simplify( self.get_header_field(header_name, '$valid$')) == BitVecVal(0, 1): if Config().get_allow_invalid_header_writes(): do_write = False else: self.invalid_header_writes.append((var_name, self.source_info)) if do_write: self.sym_vars[var_name] = sym_val
def preprocess_edges(self, path_prefix, onward_edges): # Count the number of extractions for each header stack in the path so # far. stack_counts = defaultdict(int) if len(path_prefix) > 0: self.count(stack_counts, path_prefix[0].src) for e in path_prefix: self.count(stack_counts, e.dst) edges = onward_edges if any( self.hlir.get_header_stack(stack).size < count for stack, count in stack_counts.items()): # If the path so far involves an extraction beyond the end of a # header stack, the only legal onward transitions are error # transitions. If there are no such transitions, the returned list # will be empty, which will cause the caller to drop the current # path-prefix entirely. edges = [ edge for edge in edges if isinstance(edge, ParserErrorTransition) ] elif Config().get_collapse_parser_paths(): # Collapse any parallel transitions into a single edge with merged # constraints. Note that although the nodes on either side of the # new edge are part of the graph, the edge itself is not. good_edges = [ edge for edge in edges if isinstance(edge, ParserTransition) ] other_edges = [ edge for edge in edges if not isinstance(edge, ParserTransition) ] good_edges_by_next_state = defaultdict(list) for edge in good_edges: good_edges_by_next_state[edge.next_state_name].append(edge) edges = other_edges for grouped_edges in good_edges_by_next_state.values(): if len(grouped_edges) == 1: edges.append(grouped_edges[0]) else: assert len(grouped_edges) > 1 edges.append(ParserCompositeTransition(grouped_edges)) return edges
def init(self): self.num_control_path_edges = 0 self.avg_full_path_len = Average('full_path_len') self.avg_unsat_path_len = Average('unsat_path_len') self.count_unsat_paths = Counter('unsat_paths') self.timing_file = None self.breakdown_file = None if Config().get_record_statistics(): self.timing_file = open('timing.log', 'w') self.breakdown_file = open('breakdown.log', 'w') self.start_time = time.time() self.stats = defaultdict(int) self.stats_per_control_path_edge = defaultdict(int) self.last_time_printed_stats_per_control_path_edge = self.start_time self.record_count = 0
def check_demo1(self): Config().load_test_defaults() results = process_json_file( 'examples/demo1-action-names-uniquified.p4_16.json') expected_results = { ('start', 'sink', (u'ipv4_da_lpm', u'set_l2ptr')): TestPathResult.UNINITIALIZED_READ, ('start', 'sink', (u'ipv4_da_lpm', u'my_drop1')): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ipv4', 'sink', (u'ipv4_da_lpm', u'set_l2ptr'), (u'mac_da', u'set_bd_dmac_intf')): TestPathResult.SUCCESS, ('start', 'parse_ipv4', 'sink', (u'ipv4_da_lpm', u'set_l2ptr'), (u'mac_da', u'my_drop2')): TestPathResult.SUCCESS, ('start', 'parse_ipv4', 'sink', (u'ipv4_da_lpm', u'my_drop1'), (u'mac_da', u'set_bd_dmac_intf')): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ipv4', 'sink', (u'ipv4_da_lpm', u'my_drop1'), (u'mac_da', u'my_drop2')): TestPathResult.UNINITIALIZED_READ } assert results == expected_results
def check_parser_impossible_transitions(self): Config().load_test_defaults() # This test case has at least one parser path that is # impossible to traverse, and several that are possible that, # when taken, make certain paths through ingress impossible. # Note that there are no test cases containing the state # parse_unreachable_state in the parser paths. results = process_json_file( 'examples/parser-impossible-transitions.json') expected_results = { ('start', 'parse_good', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_good', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_3', (False, (u'parser-impossible-transitions.p4', 93, u'hdr.ethernet.etherType_lsb == 4')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_good', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_3', (True, (u'parser-impossible-transitions.p4', 93, u'hdr.ethernet.etherType_lsb == 4'))), (u'tbl_parserimpossibletransitions94', u'parserimpossibletransitions94')): TestPathResult.SUCCESS, ('start', 'parse_bad4', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (False, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad4', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (True, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4'))), (u'tbl_parserimpossibletransitions100', u'parserimpossibletransitions100')): TestPathResult.SUCCESS, ('start', 'parse_bad4', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad3', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (False, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad3', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (True, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4'))), (u'tbl_parserimpossibletransitions100', u'parserimpossibletransitions100')): TestPathResult.SUCCESS, ('start', 'parse_bad3', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad2', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (False, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad2', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (True, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4'))), (u'tbl_parserimpossibletransitions100', u'parserimpossibletransitions100')): TestPathResult.SUCCESS, ('start', 'parse_bad2', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad1', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (False, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4')))): TestPathResult.NO_PACKET_FOUND, ('start', 'parse_bad1', 'sink', (u'node_2', (False, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0'))), (u'node_6', (True, (u'parser-impossible-transitions.p4', 99, u'meta.fwd_metadata.parse_status <= 4'))), (u'tbl_parserimpossibletransitions100', u'parserimpossibletransitions100')): TestPathResult.SUCCESS, ('start', 'parse_bad1', 'sink', (u'node_2', (True, (u'parser-impossible-transitions.p4', 92, u'meta.fwd_metadata.parse_status == 0')))): TestPathResult.NO_PACKET_FOUND } assert results == expected_results
def _add_path(self, path_id, constraints, path_data): # note: constraints is a list of lists, each sub-list represents the # constraints added by a single transition. max_n_paths = Config().get_consolidate_tables() if len(self.paths_data) == max_n_paths: logging.info("Too many paths-per-solve") self.flush() self.push(path_id, path_data) if not self._try_add_path(path_id, constraints, path_data): logging.info("Failed to add path %d" % (path_id, )) self.pop() self.flush() self.reset() self.push(path_id, path_data) logging.info("Flushed existing paths, re-adding path %d" % (path_id, )) assert self._try_add_path(path_id, constraints, path_data) else: logging.info("Successfully added path %d" % (path_id, ))
def solve_path(self, control_path, is_complete_control_path): expected_path = list( self.path_solver.translator.expected_path(self.parser_path, control_path)) path = Path(self.path_solver.path_id, expected_path, self.parser_path, control_path, is_complete_control_path) logging.info("") logging.info("BEGIN %s" % str(path)) if not Config().get_incremental(): self.path_solver.solver.reset() time1 = time.time() self.path_solver.add_path_constraints(control_path) time2 = time.time() result = self.path_solver.try_quick_solve(control_path, is_complete_control_path) if result == TestPathResult.SUCCESS: assert not (record_path_result(result, is_complete_control_path) or record_test_case(result, is_complete_control_path)) # Path trivially found to be satisfiable and not complete. # No test cases required. logging.info( "Path trivially found to be satisfiable and not complete.") logging.info("END %s" % str(path)) return result, None result = self.path_solver.solve_path() time3 = time.time() logging.info("END %s: %s" % (str(path), result)) path_model = PathModel(path, result, self.path_solver, time_sec_generate_ingress_constraints=time2 - time1, time_sec_initial_solve=time3 - time2) return result, path_model
def check_demo9b(self): Config().load_test_defaults() results = process_json_file('examples/demo9b.json') expected_results = { ('start', 'parse_ethernet', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'parse_tcp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'parse_tcp', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'parse_udp', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv4', 'parse_udp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.UNINITIALIZED_READ, ('start', 'parse_ethernet', 'parse_ipv6', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_tcp', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_udp', 'sink', (u'node_2', (True, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6')))): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (False, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act_0', u'act_0')): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (True, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act', u'act')): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_tcp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (False, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act_0', u'act_0')): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_tcp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (True, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act', u'act')): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_udp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (False, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act_0', u'act_0')): TestPathResult.SUCCESS, ('start', 'parse_ethernet', 'parse_ipv6', 'parse_udp', 'sink', (u'node_2', (False, (u'p4_programs/demo9b.p4', 157, u'hdr.ipv6.version != 6'))), (u'node_3', (True, (u'p4_programs/demo9b.p4', 160, u'hdr.ethernet.srcAddr == 123456'))), (u'tbl_act', u'act')): TestPathResult.SUCCESS } assert results == expected_results