blob: f88f58291f0baa62cdf83abd29f358d968847a12 [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;
Carmelo Casconea4dc3c12019-02-12 17:30:00 -080072 bit<7> _padding;
Carmelo Cascone700648c2018-04-11 12:02:16 -070073}
74
75// Packet-out header. Prepended to packets received by the controller and used
76// to tell the switch on which port this packet should be forwarded.
77@controller_header("packet_out")
78header packet_out_header_t {
79 bit<9> egress_port;
Carmelo Casconea4dc3c12019-02-12 17:30:00 -080080 bit<7> _padding;
Carmelo Cascone700648c2018-04-11 12:02:16 -070081}
82
83// For convenience we collect all headers under the same struct.
84struct headers_t {
85 ethernet_t ethernet;
86 my_tunnel_t my_tunnel;
87 ipv4_t ipv4;
88 packet_out_header_t packet_out;
89 packet_in_header_t packet_in;
90}
91
92// Metadata can be used to carry information from one table to another.
93struct metadata_t {
94 // Empty. We don't use it in this program.
95}
96
97//------------------------------------------------------------------------------
98// PARSER
99//------------------------------------------------------------------------------
100
101parser c_parser(packet_in packet,
102 out headers_t hdr,
103 inout metadata_t meta,
104 inout standard_metadata_t standard_metadata) {
105
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700106 // A P4 parser is described as a state machine, with initial state "start"
107 // and final one "accept". Each intermediate state can specify the next
108 // state by using a select statement over the header fields extracted.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700109 state start {
110 transition select(standard_metadata.ingress_port) {
111 CPU_PORT: parse_packet_out;
112 default: parse_ethernet;
113 }
114 }
115
116 state parse_packet_out {
117 packet.extract(hdr.packet_out);
118 transition parse_ethernet;
119 }
120
121 state parse_ethernet {
122 packet.extract(hdr.ethernet);
123 transition select(hdr.ethernet.ether_type) {
124 ETH_TYPE_MYTUNNEL: parse_my_tunnel;
125 ETH_TYPE_IPV4: parse_ipv4;
126 default: accept;
127 }
128 }
129
130 state parse_my_tunnel {
131 packet.extract(hdr.my_tunnel);
132 transition select(hdr.my_tunnel.proto_id) {
133 ETH_TYPE_IPV4: parse_ipv4;
134 default: accept;
135 }
136 }
137
138 state parse_ipv4 {
139 packet.extract(hdr.ipv4);
140 transition accept;
141 }
142}
143
144//------------------------------------------------------------------------------
145// INGRESS PIPELINE
146//------------------------------------------------------------------------------
147
148control c_ingress(inout headers_t hdr,
149 inout metadata_t meta,
150 inout standard_metadata_t standard_metadata) {
151
152 // We use these counters to count packets/bytes received/sent on each port.
153 // For each counter we instantiate a number of cells equal to MAX_PORTS.
154 counter(MAX_PORTS, CounterType.packets_and_bytes) tx_port_counter;
155 counter(MAX_PORTS, CounterType.packets_and_bytes) rx_port_counter;
156
157 action send_to_cpu() {
158 standard_metadata.egress_spec = CPU_PORT;
159 // Packets sent to the controller needs to be prepended with the
160 // packet-in header. By setting it valid we make sure it will be
161 // deparsed on the wire (see c_deparser).
162 hdr.packet_in.setValid();
163 hdr.packet_in.ingress_port = standard_metadata.ingress_port;
164 }
165
166 action set_out_port(port_t port) {
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700167 // Specifies the output port for this packet by setting the
168 // corresponding metadata.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700169 standard_metadata.egress_spec = port;
170 }
171
172 action _drop() {
173 mark_to_drop();
174 }
175
176 action my_tunnel_ingress(bit<32> tun_id) {
177 hdr.my_tunnel.setValid();
178 hdr.my_tunnel.tun_id = tun_id;
179 hdr.my_tunnel.proto_id = hdr.ethernet.ether_type;
180 hdr.ethernet.ether_type = ETH_TYPE_MYTUNNEL;
181 }
182
183 action my_tunnel_egress(bit<9> port) {
184 standard_metadata.egress_spec = port;
185 hdr.ethernet.ether_type = hdr.my_tunnel.proto_id;
186 hdr.my_tunnel.setInvalid();
187 }
188
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700189 // Table counter used to count packets and bytes matched by each entry of
190 // t_l2_fwd table.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700191 direct_counter(CounterType.packets_and_bytes) l2_fwd_counter;
192
193 table t_l2_fwd {
194 key = {
195 standard_metadata.ingress_port : ternary;
196 hdr.ethernet.dst_addr : ternary;
197 hdr.ethernet.src_addr : ternary;
198 hdr.ethernet.ether_type : ternary;
199 }
200 actions = {
Carmelo Cascone0bd3b182018-04-18 18:59:39 +0900201 set_out_port;
202 send_to_cpu;
203 _drop;
Carmelo Cascone700648c2018-04-11 12:02:16 -0700204 NoAction;
205 }
206 default_action = NoAction();
207 counters = l2_fwd_counter;
208 }
209
210 table t_tunnel_ingress {
211 key = {
212 hdr.ipv4.dst_addr: lpm;
213 }
214 actions = {
215 my_tunnel_ingress;
Carmelo Cascone0bd3b182018-04-18 18:59:39 +0900216 _drop;
Carmelo Cascone700648c2018-04-11 12:02:16 -0700217 }
218 default_action = _drop();
219 }
220
221 table t_tunnel_fwd {
222 key = {
223 hdr.my_tunnel.tun_id: exact;
224 }
225 actions = {
226 set_out_port;
227 my_tunnel_egress;
Carmelo Cascone0bd3b182018-04-18 18:59:39 +0900228 _drop;
Carmelo Cascone700648c2018-04-11 12:02:16 -0700229 }
230 default_action = _drop();
231 }
232
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700233 // Defines the processing applied by this control block. You can see this as
234 // the main function applied to every packet received by the switch.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700235 apply {
236 if (standard_metadata.ingress_port == CPU_PORT) {
237 // Packet received from CPU_PORT, this is a packet-out sent by the
238 // controller. Skip table processing, set the egress port as
239 // requested by the controller (packet_out header) and remove the
240 // packet_out header.
241 standard_metadata.egress_spec = hdr.packet_out.egress_port;
242 hdr.packet_out.setInvalid();
243 } else {
244 // Packet received from data plane port.
Carmelo Cascone9bc6a142018-04-16 17:35:15 -0700245 // Applies table t_l2_fwd to the packet.
Carmelo Cascone700648c2018-04-11 12:02:16 -0700246 if (t_l2_fwd.apply().hit) {
247 // Packet hit an entry in t_l2_fwd table. A forwarding action
248 // has already been taken. No need to apply other tables, exit
249 // this control block.
250 return;
251 }
252
253 if (hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()) {
254 // Process only non-tunneled IPv4 packets.
255 t_tunnel_ingress.apply();
256 }
257
258 if (hdr.my_tunnel.isValid()) {
259 // Process all tunneled packets.
260 t_tunnel_fwd.apply();
261 }
262 }
263
264 // Update port counters at index = ingress or egress port.
265 if (standard_metadata.egress_spec < MAX_PORTS) {
266 tx_port_counter.count((bit<32>) standard_metadata.egress_spec);
267 }
268 if (standard_metadata.ingress_port < MAX_PORTS) {
269 rx_port_counter.count((bit<32>) standard_metadata.ingress_port);
270 }
271 }
272}
273
274//------------------------------------------------------------------------------
275// EGRESS PIPELINE
276//------------------------------------------------------------------------------
277
278control c_egress(inout headers_t hdr,
279 inout metadata_t meta,
280 inout standard_metadata_t standard_metadata) {
281 apply {
282 // Nothing to do on the egress pipeline.
283 }
284}
285
286//------------------------------------------------------------------------------
287// CHECKSUM HANDLING
288//------------------------------------------------------------------------------
289
290control c_verify_checksum(inout headers_t hdr, inout metadata_t meta) {
291 apply {
292 // Nothing to do here, we assume checksum is always correct.
293 }
294}
295
296control c_compute_checksum(inout headers_t hdr, inout metadata_t meta) {
297 apply {
298 // No need to compute checksum as we do not modify packet headers.
299 }
300}
301
302//------------------------------------------------------------------------------
303// DEPARSER
304//------------------------------------------------------------------------------
305
306control c_deparser(packet_out packet, in headers_t hdr) {
307 apply {
308 // Emit headers on the wire in the following order.
309 // Only valid headers are emitted.
310 packet.emit(hdr.packet_in);
311 packet.emit(hdr.ethernet);
312 packet.emit(hdr.my_tunnel);
313 packet.emit(hdr.ipv4);
314 }
315}
316
317//------------------------------------------------------------------------------
318// SWITCH INSTANTIATION
319//------------------------------------------------------------------------------
320
321V1Switch(c_parser(),
322 c_verify_checksum(),
323 c_ingress(),
324 c_egress(),
325 c_compute_checksum(),
326 c_deparser()) main;