• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) Barefoot Networks, Inc.
2# Licensed under the Apache License, Version 2.0 (the "License")
3
4from collections import defaultdict, OrderedDict
5from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \
6    P4_DEFAULT, p4_parse_state, p4_table, \
7    p4_conditional_node, p4_parser_exception, \
8    p4_header_instance, P4_NEXT
9
10import ebpfProgram
11import ebpfInstance
12import ebpfType
13import ebpfStructType
14from topoSorting import Graph
15from programSerializer import ProgramSerializer
16
17def produce_parser_topo_sorting(hlir):
18    # This function is copied from the P4 behavioral model implementation
19    header_graph = Graph()
20
21    def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index):
22        assert(isinstance(parse_state, p4_parse_state))
23        for call in parse_state.call_sequence:
24            call_type = call[0]
25            if call_type == parse_call.extract:
26                hdr = call[1]
27
28                if hdr.virtual:
29                    base_name = hdr.base_name
30                    current_index = tag_stacks_index[base_name]
31                    if current_index > hdr.max_index:
32                        return
33                    tag_stacks_index[base_name] += 1
34                    name = base_name + "[%d]" % current_index
35                    hdr = hlir.p4_header_instances[name]
36
37                if hdr not in header_graph:
38                    header_graph.add_node(hdr)
39                hdr_node = header_graph.get_node(hdr)
40
41                if prev_hdr_node:
42                    prev_hdr_node.add_edge_to(hdr_node)
43                else:
44                    header_graph.root = hdr
45                prev_hdr_node = hdr_node
46
47        for branch_case, next_state in parse_state.branch_to.items():
48            if not next_state:
49                continue
50            if not isinstance(next_state, p4_parse_state):
51                continue
52            walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy())
53
54    start_state = hlir.p4_parse_states["start"]
55    walk_rec(hlir, start_state, None, defaultdict(int))
56
57    header_topo_sorting = header_graph.produce_topo_sorting()
58
59    return header_topo_sorting
60
61class EbpfDeparser(object):
62    def __init__(self, hlir):
63        header_topo_sorting = produce_parser_topo_sorting(hlir)
64        self.headerOrder = [hdr.name for hdr in header_topo_sorting]
65
66    def serialize(self, serializer, program):
67        assert isinstance(serializer, ProgramSerializer)
68        assert isinstance(program, ebpfProgram.EbpfProgram)
69
70        serializer.emitIndent()
71        serializer.blockStart()
72        serializer.emitIndent()
73        serializer.appendLine("/* Deparser */")
74        serializer.emitIndent()
75        serializer.appendFormat("{0} = 0;", program.offsetVariableName)
76        serializer.newline()
77        for h in self.headerOrder:
78            header = program.getHeaderInstance(h)
79            self.serializeHeaderEmit(header, serializer, program)
80        serializer.blockEnd(True)
81
82    def serializeHeaderEmit(self, header, serializer, program):
83        assert isinstance(header, ebpfInstance.EbpfHeader)
84        assert isinstance(serializer, ProgramSerializer)
85        assert isinstance(program, ebpfProgram.EbpfProgram)
86        p4header = header.hlirInstance
87        assert isinstance(p4header, p4_header_instance)
88
89        serializer.emitIndent()
90        serializer.appendFormat("if ({0}.{1}.valid) ",
91                                program.headerStructName, header.name)
92        serializer.blockStart()
93
94        if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header):
95            ebpfStack = program.getStackInstance(p4header.base_name)
96            assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
97
98            if isinstance(p4header.index, int):
99                index = "[" + str(headerInstance.index) + "]"
100            elif p4header.index is P4_NEXT:
101                index = "[" + ebpfStack.indexVar + "]"
102            else:
103                raise CompilationException(
104                    True, "Unexpected index for array {0}",
105                    p4header.index)
106            basetype = ebpfStack.basetype
107        else:
108            ebpfHeader = program.getHeaderInstance(p4header.name)
109            basetype = ebpfHeader.type
110            index = ""
111
112        alignment = 0
113        for field in basetype.fields:
114            assert isinstance(field, ebpfStructType.EbpfField)
115
116            self.serializeFieldEmit(serializer, p4header.base_name,
117                                    index, field, alignment, program)
118            alignment += field.widthInBits()
119            alignment = alignment % 8
120        serializer.blockEnd(True)
121
122    def serializeFieldEmit(self, serializer, name, index,
123                           field, alignment, program):
124        assert isinstance(index, str)
125        assert isinstance(name, str)
126        assert isinstance(field, ebpfStructType.EbpfField)
127        assert isinstance(serializer, ProgramSerializer)
128        assert isinstance(alignment, int)
129        assert isinstance(program, ebpfProgram.EbpfProgram)
130
131        if field.name == "valid":
132            return
133
134        fieldToEmit = (program.headerStructName + "." + name +
135                       index + "." + field.name)
136        width = field.widthInBits()
137        if width <= 32:
138            store = self.generatePacketStore(fieldToEmit, 0, alignment,
139                                             width, program)
140            serializer.emitIndent()
141            serializer.appendLine(store)
142        else:
143            # Destination is bigger than 4 bytes and
144            # represented as a byte array.
145            b = (width + 7) / 8
146            for i in range(0, b):
147                serializer.emitIndent()
148                store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]",
149                                                 i,
150                                                 alignment,
151                                                 8, program)
152                serializer.appendLine(store)
153
154        serializer.emitIndent()
155        serializer.appendFormat("{0} += {1};",
156                                program.offsetVariableName, width)
157        serializer.newline()
158
159    def generatePacketStore(self, value, offset, alignment, width, program):
160        assert width > 0
161        assert alignment < 8
162        assert isinstance(width, int)
163        assert isinstance(alignment, int)
164
165        return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format(
166            program.packetName,
167            program.offsetVariableName,
168            offset,
169            alignment,
170            width,
171            value
172        )
173