| # Copyright (c) Barefoot Networks, Inc. |
| # Licensed under the Apache License, Version 2.0 (the "License") |
| |
| from collections import defaultdict, OrderedDict |
| from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \ |
| P4_DEFAULT, p4_parse_state, p4_table, \ |
| p4_conditional_node, p4_parser_exception, \ |
| p4_header_instance, P4_NEXT |
| |
| import ebpfProgram |
| import ebpfInstance |
| import ebpfType |
| import ebpfStructType |
| from topoSorting import Graph |
| from programSerializer import ProgramSerializer |
| |
| def produce_parser_topo_sorting(hlir): |
| # This function is copied from the P4 behavioral model implementation |
| header_graph = Graph() |
| |
| def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index): |
| assert(isinstance(parse_state, p4_parse_state)) |
| for call in parse_state.call_sequence: |
| call_type = call[0] |
| if call_type == parse_call.extract: |
| hdr = call[1] |
| |
| if hdr.virtual: |
| base_name = hdr.base_name |
| current_index = tag_stacks_index[base_name] |
| if current_index > hdr.max_index: |
| return |
| tag_stacks_index[base_name] += 1 |
| name = base_name + "[%d]" % current_index |
| hdr = hlir.p4_header_instances[name] |
| |
| if hdr not in header_graph: |
| header_graph.add_node(hdr) |
| hdr_node = header_graph.get_node(hdr) |
| |
| if prev_hdr_node: |
| prev_hdr_node.add_edge_to(hdr_node) |
| else: |
| header_graph.root = hdr |
| prev_hdr_node = hdr_node |
| |
| for branch_case, next_state in parse_state.branch_to.items(): |
| if not next_state: |
| continue |
| if not isinstance(next_state, p4_parse_state): |
| continue |
| walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy()) |
| |
| start_state = hlir.p4_parse_states["start"] |
| walk_rec(hlir, start_state, None, defaultdict(int)) |
| |
| header_topo_sorting = header_graph.produce_topo_sorting() |
| |
| return header_topo_sorting |
| |
| class EbpfDeparser(object): |
| def __init__(self, hlir): |
| header_topo_sorting = produce_parser_topo_sorting(hlir) |
| self.headerOrder = [hdr.name for hdr in header_topo_sorting] |
| |
| def serialize(self, serializer, program): |
| assert isinstance(serializer, ProgramSerializer) |
| assert isinstance(program, ebpfProgram.EbpfProgram) |
| |
| serializer.emitIndent() |
| serializer.blockStart() |
| serializer.emitIndent() |
| serializer.appendLine("/* Deparser */") |
| serializer.emitIndent() |
| serializer.appendFormat("{0} = 0;", program.offsetVariableName) |
| serializer.newline() |
| for h in self.headerOrder: |
| header = program.getHeaderInstance(h) |
| self.serializeHeaderEmit(header, serializer, program) |
| serializer.blockEnd(True) |
| |
| def serializeHeaderEmit(self, header, serializer, program): |
| assert isinstance(header, ebpfInstance.EbpfHeader) |
| assert isinstance(serializer, ProgramSerializer) |
| assert isinstance(program, ebpfProgram.EbpfProgram) |
| p4header = header.hlirInstance |
| assert isinstance(p4header, p4_header_instance) |
| |
| serializer.emitIndent() |
| serializer.appendFormat("if ({0}.{1}.valid) ", |
| program.headerStructName, header.name) |
| serializer.blockStart() |
| |
| if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header): |
| ebpfStack = program.getStackInstance(p4header.base_name) |
| assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) |
| |
| if isinstance(p4header.index, int): |
| index = "[" + str(headerInstance.index) + "]" |
| elif p4header.index is P4_NEXT: |
| index = "[" + ebpfStack.indexVar + "]" |
| else: |
| raise CompilationException( |
| True, "Unexpected index for array {0}", |
| p4header.index) |
| basetype = ebpfStack.basetype |
| else: |
| ebpfHeader = program.getHeaderInstance(p4header.name) |
| basetype = ebpfHeader.type |
| index = "" |
| |
| alignment = 0 |
| for field in basetype.fields: |
| assert isinstance(field, ebpfStructType.EbpfField) |
| |
| self.serializeFieldEmit(serializer, p4header.base_name, |
| index, field, alignment, program) |
| alignment += field.widthInBits() |
| alignment = alignment % 8 |
| serializer.blockEnd(True) |
| |
| def serializeFieldEmit(self, serializer, name, index, |
| field, alignment, program): |
| assert isinstance(index, str) |
| assert isinstance(name, str) |
| assert isinstance(field, ebpfStructType.EbpfField) |
| assert isinstance(serializer, ProgramSerializer) |
| assert isinstance(alignment, int) |
| assert isinstance(program, ebpfProgram.EbpfProgram) |
| |
| if field.name == "valid": |
| return |
| |
| fieldToEmit = (program.headerStructName + "." + name + |
| index + "." + field.name) |
| width = field.widthInBits() |
| if width <= 32: |
| store = self.generatePacketStore(fieldToEmit, 0, alignment, |
| width, program) |
| serializer.emitIndent() |
| serializer.appendLine(store) |
| else: |
| # Destination is bigger than 4 bytes and |
| # represented as a byte array. |
| b = (width + 7) / 8 |
| for i in range(0, b): |
| serializer.emitIndent() |
| store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]", |
| i, |
| alignment, |
| 8, program) |
| serializer.appendLine(store) |
| |
| serializer.emitIndent() |
| serializer.appendFormat("{0} += {1};", |
| program.offsetVariableName, width) |
| serializer.newline() |
| |
| def generatePacketStore(self, value, offset, alignment, width, program): |
| assert width > 0 |
| assert alignment < 8 |
| assert isinstance(width, int) |
| assert isinstance(alignment, int) |
| |
| return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format( |
| program.packetName, |
| program.offsetVariableName, |
| offset, |
| alignment, |
| width, |
| value |
| ) |