blob: 4912847fd523d3ee37937bb37bc92cd6657e826b [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
Carmelo Cascone9bc6a142018-04-16 17:35:15 -070017 /*
18 * This program describes a pipeline implementing a very simple
19 * tunneling protocol called MyTunnel. The pipeline defines also table called
20 * t_l2_fwd that provides basic L2 forwarding capabilities and actions to
21 * send packets to the controller. This table is needed to provide
22 * compatibility with existing ONOS applications such as Proxy-ARP, LLDP Link
23 * Discovery and Reactive Forwarding.
24 */
25
Carmelo Cascone700648c2018-04-11 12:02:16 -070026#include <core.p4>
27#include <v1model.p4>
28
29#define MAX_PORTS 255
30
31const bit<16> ETH_TYPE_MYTUNNEL = 0x1212;
32const bit<16> ETH_TYPE_IPV4 = 0x800;
33
34typedef bit<9> port_t;
35const port_t CPU_PORT = 255;
36
37//------------------------------------------------------------------------------
38// HEADERS
39//------------------------------------------------------------------------------
40
41header ethernet_t {
42 bit<48> dst_addr;
43 bit<48> src_addr;
44 bit<16> ether_type;
45}
46
47header my_tunnel_t {
48 bit<16> proto_id;
49 bit<32> tun_id;
50}
51
52header ipv4_t {
53 bit<4> version;
54 bit<4> ihl;
55 bit<8> diffserv;
56 bit<16> len;
57 bit<16> identification;
58 bit<3> flags;
59 bit<13> frag_offset;
60 bit<8> ttl;
61 bit<8> protocol;
62 bit<16> hdr_checksum;
63 bit<32> src_addr;
64 bit<32> dst_addr;
65}
66
67// Packet-in header. Prepended to packets sent to the controller and used to
68// carry the original ingress port where the packet was received.
69@controller_header("packet_in")
70header packet_in_header_t {
71 bit<9> ingress_port;
72}
73
74// Packet-out header. Prepended to packets received by the controller and used
75// to tell the switch on which port this packet should be forwarded.
76@controller_header("packet_out")
77header packet_out_header_t {
78 bit<9> egress_port;
79}
80
81// For convenience we collect all headers under the same struct.
82struct headers_t {
83 ethernet_t ethernet;
84 my_tunnel_t my_tunnel;
85 ipv4_t ipv4;
86 packet_out_header_t packet_out;
87 packet_in_header_t packet_in;
88}
89
90// Metadata can be used to carry information from one table to another.
91struct metadata_t {
92 // Empty. We don't use it in this program.
93}
94
95//------------------------------------------------------------------------------
96// PARSER
97//------------------------------------------------------------------------------
98
99parser c_parser(packet_in packet,
100 out headers_t hdr,
101 inout metadata_t meta,
102 inout standard_metadata_t standard_metadata) {
103
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700104 // A P4 parser is described as a state machine, with initial state "start"
105 // and final one "accept". Each intermediate state can specify the next
106 // state by using a select statement over the header fields extracted.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700107 state start {
108 transition select(standard_metadata.ingress_port) {
109 CPU_PORT: parse_packet_out;
110 default: parse_ethernet;
111 }
112 }
113
114 state parse_packet_out {
115 packet.extract(hdr.packet_out);
116 transition parse_ethernet;
117 }
118
119 state parse_ethernet {
120 packet.extract(hdr.ethernet);
121 transition select(hdr.ethernet.ether_type) {
122 ETH_TYPE_MYTUNNEL: parse_my_tunnel;
123 ETH_TYPE_IPV4: parse_ipv4;
124 default: accept;
125 }
126 }
127
128 state parse_my_tunnel {
129 packet.extract(hdr.my_tunnel);
130 transition select(hdr.my_tunnel.proto_id) {
131 ETH_TYPE_IPV4: parse_ipv4;
132 default: accept;
133 }
134 }
135
136 state parse_ipv4 {
137 packet.extract(hdr.ipv4);
138 transition accept;
139 }
140}
141
142//------------------------------------------------------------------------------
143// INGRESS PIPELINE
144//------------------------------------------------------------------------------
145
146control c_ingress(inout headers_t hdr,
147 inout metadata_t meta,
148 inout standard_metadata_t standard_metadata) {
149
150 // We use these counters to count packets/bytes received/sent on each port.
151 // For each counter we instantiate a number of cells equal to MAX_PORTS.
152 counter(MAX_PORTS, CounterType.packets_and_bytes) tx_port_counter;
153 counter(MAX_PORTS, CounterType.packets_and_bytes) rx_port_counter;
154
155 action send_to_cpu() {
156 standard_metadata.egress_spec = CPU_PORT;
157 // Packets sent to the controller needs to be prepended with the
158 // packet-in header. By setting it valid we make sure it will be
159 // deparsed on the wire (see c_deparser).
160 hdr.packet_in.setValid();
161 hdr.packet_in.ingress_port = standard_metadata.ingress_port;
162 }
163
164 action set_out_port(port_t port) {
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700165 // Specifies the output port for this packet by setting the
166 // corresponding metadata.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700167 standard_metadata.egress_spec = port;
168 }
169
170 action _drop() {
171 mark_to_drop();
172 }
173
174 action my_tunnel_ingress(bit<32> tun_id) {
175 hdr.my_tunnel.setValid();
176 hdr.my_tunnel.tun_id = tun_id;
177 hdr.my_tunnel.proto_id = hdr.ethernet.ether_type;
178 hdr.ethernet.ether_type = ETH_TYPE_MYTUNNEL;
179 }
180
181 action my_tunnel_egress(bit<9> port) {
182 standard_metadata.egress_spec = port;
183 hdr.ethernet.ether_type = hdr.my_tunnel.proto_id;
184 hdr.my_tunnel.setInvalid();
185 }
186
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700187 // Table counter used to count packets and bytes matched by each entry of
188 // t_l2_fwd table.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700189 direct_counter(CounterType.packets_and_bytes) l2_fwd_counter;
190
191 table t_l2_fwd {
192 key = {
193 standard_metadata.ingress_port : ternary;
194 hdr.ethernet.dst_addr : ternary;
195 hdr.ethernet.src_addr : ternary;
196 hdr.ethernet.ether_type : ternary;
197 }
198 actions = {
199 set_out_port();
200 send_to_cpu();
201 _drop();
202 NoAction;
203 }
204 default_action = NoAction();
205 counters = l2_fwd_counter;
206 }
207
208 table t_tunnel_ingress {
209 key = {
210 hdr.ipv4.dst_addr: lpm;
211 }
212 actions = {
213 my_tunnel_ingress;
214 _drop();
215 }
216 default_action = _drop();
217 }
218
219 table t_tunnel_fwd {
220 key = {
221 hdr.my_tunnel.tun_id: exact;
222 }
223 actions = {
224 set_out_port;
225 my_tunnel_egress;
226 _drop();
227 }
228 default_action = _drop();
229 }
230
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700231 // Defines the processing applied by this control block. You can see this as
232 // the main function applied to every packet received by the switch.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700233 apply {
234 if (standard_metadata.ingress_port == CPU_PORT) {
235 // Packet received from CPU_PORT, this is a packet-out sent by the
236 // controller. Skip table processing, set the egress port as
237 // requested by the controller (packet_out header) and remove the
238 // packet_out header.
239 standard_metadata.egress_spec = hdr.packet_out.egress_port;
240 hdr.packet_out.setInvalid();
241 } else {
242 // Packet received from data plane port.
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700243 // Applies table t_l2_fwd to the packet.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700244 if (t_l2_fwd.apply().hit) {
245 // Packet hit an entry in t_l2_fwd table. A forwarding action
246 // has already been taken. No need to apply other tables, exit
247 // this control block.
248 return;
249 }
250
251 if (hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()) {
252 // Process only non-tunneled IPv4 packets.
253 t_tunnel_ingress.apply();
254 }
255
256 if (hdr.my_tunnel.isValid()) {
257 // Process all tunneled packets.
258 t_tunnel_fwd.apply();
259 }
260 }
261
262 // Update port counters at index = ingress or egress port.
263 if (standard_metadata.egress_spec < MAX_PORTS) {
264 tx_port_counter.count((bit<32>) standard_metadata.egress_spec);
265 }
266 if (standard_metadata.ingress_port < MAX_PORTS) {
267 rx_port_counter.count((bit<32>) standard_metadata.ingress_port);
268 }
269 }
270}
271
272//------------------------------------------------------------------------------
273// EGRESS PIPELINE
274//------------------------------------------------------------------------------
275
276control c_egress(inout headers_t hdr,
277 inout metadata_t meta,
278 inout standard_metadata_t standard_metadata) {
279 apply {
280 // Nothing to do on the egress pipeline.
281 }
282}
283
284//------------------------------------------------------------------------------
285// CHECKSUM HANDLING
286//------------------------------------------------------------------------------
287
288control c_verify_checksum(inout headers_t hdr, inout metadata_t meta) {
289 apply {
290 // Nothing to do here, we assume checksum is always correct.
291 }
292}
293
294control c_compute_checksum(inout headers_t hdr, inout metadata_t meta) {
295 apply {
296 // No need to compute checksum as we do not modify packet headers.
297 }
298}
299
300//------------------------------------------------------------------------------
301// DEPARSER
302//------------------------------------------------------------------------------
303
304control c_deparser(packet_out packet, in headers_t hdr) {
305 apply {
306 // Emit headers on the wire in the following order.
307 // Only valid headers are emitted.
308 packet.emit(hdr.packet_in);
309 packet.emit(hdr.ethernet);
310 packet.emit(hdr.my_tunnel);
311 packet.emit(hdr.ipv4);
312 }
313}
314
315//------------------------------------------------------------------------------
316// SWITCH INSTANTIATION
317//------------------------------------------------------------------------------
318
319V1Switch(c_parser(),
320 c_verify_checksum(),
321 c_ingress(),
322 c_egress(),
323 c_compute_checksum(),
324 c_deparser()) main;