blob: 7d734890815a83eef7c687beb4da6b22bc2c87e2 [file] [log] [blame]
Carmelo Cascone700648c2018-04-11 12:02:16 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <core.p4>
18#include <v1model.p4>
19
20#define MAX_PORTS 255
21
22const bit<16> ETH_TYPE_MYTUNNEL = 0x1212;
23const bit<16> ETH_TYPE_IPV4 = 0x800;
24
25typedef bit<9> port_t;
26const port_t CPU_PORT = 255;
27
28//------------------------------------------------------------------------------
29// HEADERS
30//------------------------------------------------------------------------------
31
32header ethernet_t {
33 bit<48> dst_addr;
34 bit<48> src_addr;
35 bit<16> ether_type;
36}
37
38header my_tunnel_t {
39 bit<16> proto_id;
40 bit<32> tun_id;
41}
42
43header ipv4_t {
44 bit<4> version;
45 bit<4> ihl;
46 bit<8> diffserv;
47 bit<16> len;
48 bit<16> identification;
49 bit<3> flags;
50 bit<13> frag_offset;
51 bit<8> ttl;
52 bit<8> protocol;
53 bit<16> hdr_checksum;
54 bit<32> src_addr;
55 bit<32> dst_addr;
56}
57
58// Packet-in header. Prepended to packets sent to the controller and used to
59// carry the original ingress port where the packet was received.
60@controller_header("packet_in")
61header packet_in_header_t {
62 bit<9> ingress_port;
63}
64
65// Packet-out header. Prepended to packets received by the controller and used
66// to tell the switch on which port this packet should be forwarded.
67@controller_header("packet_out")
68header packet_out_header_t {
69 bit<9> egress_port;
70}
71
72// For convenience we collect all headers under the same struct.
73struct headers_t {
74 ethernet_t ethernet;
75 my_tunnel_t my_tunnel;
76 ipv4_t ipv4;
77 packet_out_header_t packet_out;
78 packet_in_header_t packet_in;
79}
80
81// Metadata can be used to carry information from one table to another.
82struct metadata_t {
83 // Empty. We don't use it in this program.
84}
85
86//------------------------------------------------------------------------------
87// PARSER
88//------------------------------------------------------------------------------
89
90parser c_parser(packet_in packet,
91 out headers_t hdr,
92 inout metadata_t meta,
93 inout standard_metadata_t standard_metadata) {
94
95 state start {
96 transition select(standard_metadata.ingress_port) {
97 CPU_PORT: parse_packet_out;
98 default: parse_ethernet;
99 }
100 }
101
102 state parse_packet_out {
103 packet.extract(hdr.packet_out);
104 transition parse_ethernet;
105 }
106
107 state parse_ethernet {
108 packet.extract(hdr.ethernet);
109 transition select(hdr.ethernet.ether_type) {
110 ETH_TYPE_MYTUNNEL: parse_my_tunnel;
111 ETH_TYPE_IPV4: parse_ipv4;
112 default: accept;
113 }
114 }
115
116 state parse_my_tunnel {
117 packet.extract(hdr.my_tunnel);
118 transition select(hdr.my_tunnel.proto_id) {
119 ETH_TYPE_IPV4: parse_ipv4;
120 default: accept;
121 }
122 }
123
124 state parse_ipv4 {
125 packet.extract(hdr.ipv4);
126 transition accept;
127 }
128}
129
130//------------------------------------------------------------------------------
131// INGRESS PIPELINE
132//------------------------------------------------------------------------------
133
134control c_ingress(inout headers_t hdr,
135 inout metadata_t meta,
136 inout standard_metadata_t standard_metadata) {
137
138 // We use these counters to count packets/bytes received/sent on each port.
139 // For each counter we instantiate a number of cells equal to MAX_PORTS.
140 counter(MAX_PORTS, CounterType.packets_and_bytes) tx_port_counter;
141 counter(MAX_PORTS, CounterType.packets_and_bytes) rx_port_counter;
142
143 action send_to_cpu() {
144 standard_metadata.egress_spec = CPU_PORT;
145 // Packets sent to the controller needs to be prepended with the
146 // packet-in header. By setting it valid we make sure it will be
147 // deparsed on the wire (see c_deparser).
148 hdr.packet_in.setValid();
149 hdr.packet_in.ingress_port = standard_metadata.ingress_port;
150 }
151
152 action set_out_port(port_t port) {
153 standard_metadata.egress_spec = port;
154 }
155
156 action _drop() {
157 mark_to_drop();
158 }
159
160 action my_tunnel_ingress(bit<32> tun_id) {
161 hdr.my_tunnel.setValid();
162 hdr.my_tunnel.tun_id = tun_id;
163 hdr.my_tunnel.proto_id = hdr.ethernet.ether_type;
164 hdr.ethernet.ether_type = ETH_TYPE_MYTUNNEL;
165 }
166
167 action my_tunnel_egress(bit<9> port) {
168 standard_metadata.egress_spec = port;
169 hdr.ethernet.ether_type = hdr.my_tunnel.proto_id;
170 hdr.my_tunnel.setInvalid();
171 }
172
173 direct_counter(CounterType.packets_and_bytes) l2_fwd_counter;
174
175 table t_l2_fwd {
176 key = {
177 standard_metadata.ingress_port : ternary;
178 hdr.ethernet.dst_addr : ternary;
179 hdr.ethernet.src_addr : ternary;
180 hdr.ethernet.ether_type : ternary;
181 }
182 actions = {
183 set_out_port();
184 send_to_cpu();
185 _drop();
186 NoAction;
187 }
188 default_action = NoAction();
189 counters = l2_fwd_counter;
190 }
191
192 table t_tunnel_ingress {
193 key = {
194 hdr.ipv4.dst_addr: lpm;
195 }
196 actions = {
197 my_tunnel_ingress;
198 _drop();
199 }
200 default_action = _drop();
201 }
202
203 table t_tunnel_fwd {
204 key = {
205 hdr.my_tunnel.tun_id: exact;
206 }
207 actions = {
208 set_out_port;
209 my_tunnel_egress;
210 _drop();
211 }
212 default_action = _drop();
213 }
214
215 // Define processing applied by this control block.
216 apply {
217 if (standard_metadata.ingress_port == CPU_PORT) {
218 // Packet received from CPU_PORT, this is a packet-out sent by the
219 // controller. Skip table processing, set the egress port as
220 // requested by the controller (packet_out header) and remove the
221 // packet_out header.
222 standard_metadata.egress_spec = hdr.packet_out.egress_port;
223 hdr.packet_out.setInvalid();
224 } else {
225 // Packet received from data plane port.
226 if (t_l2_fwd.apply().hit) {
227 // Packet hit an entry in t_l2_fwd table. A forwarding action
228 // has already been taken. No need to apply other tables, exit
229 // this control block.
230 return;
231 }
232
233 if (hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()) {
234 // Process only non-tunneled IPv4 packets.
235 t_tunnel_ingress.apply();
236 }
237
238 if (hdr.my_tunnel.isValid()) {
239 // Process all tunneled packets.
240 t_tunnel_fwd.apply();
241 }
242 }
243
244 // Update port counters at index = ingress or egress port.
245 if (standard_metadata.egress_spec < MAX_PORTS) {
246 tx_port_counter.count((bit<32>) standard_metadata.egress_spec);
247 }
248 if (standard_metadata.ingress_port < MAX_PORTS) {
249 rx_port_counter.count((bit<32>) standard_metadata.ingress_port);
250 }
251 }
252}
253
254//------------------------------------------------------------------------------
255// EGRESS PIPELINE
256//------------------------------------------------------------------------------
257
258control c_egress(inout headers_t hdr,
259 inout metadata_t meta,
260 inout standard_metadata_t standard_metadata) {
261 apply {
262 // Nothing to do on the egress pipeline.
263 }
264}
265
266//------------------------------------------------------------------------------
267// CHECKSUM HANDLING
268//------------------------------------------------------------------------------
269
270control c_verify_checksum(inout headers_t hdr, inout metadata_t meta) {
271 apply {
272 // Nothing to do here, we assume checksum is always correct.
273 }
274}
275
276control c_compute_checksum(inout headers_t hdr, inout metadata_t meta) {
277 apply {
278 // No need to compute checksum as we do not modify packet headers.
279 }
280}
281
282//------------------------------------------------------------------------------
283// DEPARSER
284//------------------------------------------------------------------------------
285
286control c_deparser(packet_out packet, in headers_t hdr) {
287 apply {
288 // Emit headers on the wire in the following order.
289 // Only valid headers are emitted.
290 packet.emit(hdr.packet_in);
291 packet.emit(hdr.ethernet);
292 packet.emit(hdr.my_tunnel);
293 packet.emit(hdr.ipv4);
294 }
295}
296
297//------------------------------------------------------------------------------
298// SWITCH INSTANTIATION
299//------------------------------------------------------------------------------
300
301V1Switch(c_parser(),
302 c_verify_checksum(),
303 c_ingress(),
304 c_egress(),
305 c_compute_checksum(),
306 c_deparser()) main;