blob: 9f0b809c428bfb97d834cfdc0129f6a5de93e636 [file] [log] [blame]
Carmelo Cascone770507f2017-09-14 20:58:04 +02001/*
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 ETH_TYPE_IPV4 0x0800
21#define MAX_PORTS 511
22
23typedef bit<9> port_t;
24const port_t CPU_PORT = 255;
25const port_t DROP_PORT = 511;
26
27//------------------------------------------------------------------------------
28// HEADERS
29//------------------------------------------------------------------------------
30
31header ethernet_t {
32 bit<48> dst_addr;
33 bit<48> src_addr;
34 bit<16> ether_type;
35}
36
37header ipv4_t {
38 bit<4> version;
39 bit<4> ihl;
40 bit<8> diffserv;
41 bit<16> len;
42 bit<16> identification;
43 bit<3> flags;
44 bit<13> frag_offset;
45 bit<8> ttl;
46 bit<8> protocol;
47 bit<16> hdr_checksum;
48 bit<32> src_addr;
49 bit<32> dst_addr;
50}
51
52/*
53Packet-in header. Prepended to packets sent to the controller and used to carry
54the original ingress port where the packet was received.
55 */
56@controller_header("packet_in")
57header packet_in_header_t {
58 bit<9> ingress_port;
59}
60
61/*
62Packet-out header. Prepended to packets received by the controller and used to
63tell the switch on which physical port this packet should be forwarded.
64 */
65@controller_header("packet_out")
66header packet_out_header_t {
67 bit<9> egress_port;
68}
69
70/*
71For convenience we collect all headers under the same struct.
72 */
73struct headers_t {
74 ethernet_t ethernet;
75 ipv4_t ipv4;
76 packet_out_header_t packet_out;
77 packet_in_header_t packet_in;
78}
79
80/*
81Metadata can be used to carry information from one table to another.
82 */
83struct metadata_t {
84 /* Empty. We don't use it in this program. */
85}
86
87//------------------------------------------------------------------------------
88// PARSER
89//------------------------------------------------------------------------------
90
91parser ParserImpl(packet_in packet,
92 out headers_t hdr,
93 inout metadata_t meta,
94 inout standard_metadata_t standard_metadata) {
95
96 state start {
97 transition select(standard_metadata.ingress_port) {
98 CPU_PORT: parse_packet_out;
99 default: parse_ethernet;
100 }
101 }
102
103 state parse_packet_out {
104 packet.extract(hdr.packet_out);
105 transition parse_ethernet;
106 }
107
108 state parse_ethernet {
109 packet.extract(hdr.ethernet);
110 transition select(hdr.ethernet.ether_type) {
111 ETH_TYPE_IPV4: parse_ipv4;
112 default: accept;
113 }
114 }
115
116 state parse_ipv4 {
117 packet.extract(hdr.ipv4);
118 transition accept;
119 }
120}
121
122//------------------------------------------------------------------------------
123// INGRESS PIPELINE
124//------------------------------------------------------------------------------
125
126control IngressImpl(inout headers_t hdr,
127 inout metadata_t meta,
128 inout standard_metadata_t standard_metadata) {
129
130 action send_to_cpu() {
131 standard_metadata.egress_spec = CPU_PORT;
132 /*
133 Packets sent to the controller needs to be prepended with the packet-in
134 header. By setting it valid we make sure it will be deparsed before the
135 ethernet header (see DeparserImpl).
136 */
137 hdr.packet_in.setValid();
138 hdr.packet_in.ingress_port = standard_metadata.ingress_port;
139 }
140
141 action set_egress_port(port_t port) {
142 standard_metadata.egress_spec = port;
143 }
144
145 action _drop() {
146 standard_metadata.egress_spec = DROP_PORT;
147 }
148
149 table table0 {
150 key = {
151 standard_metadata.ingress_port : ternary;
152 hdr.ethernet.dst_addr : ternary;
153 hdr.ethernet.src_addr : ternary;
154 hdr.ethernet.ether_type : ternary;
155 }
156 actions = {
157 set_egress_port();
158 send_to_cpu();
159 _drop();
160 }
161 default_action = _drop();
162 }
163
164 table ip_proto_filter_table {
165 key = {
166 hdr.ipv4.src_addr : ternary;
167 hdr.ipv4.protocol : exact;
168 }
169 actions = {
170 _drop();
171 }
172 }
173
174 /*
175 Port counters.
176 We use these counter instances to count packets/bytes received/sent on each
177 port. BMv2 always counts both packets and bytes, even if the counter is
178 instantiated as "packets". For each counter we instantiate a number of cells
179 equal to MAX_PORTS.
180 */
181 counter(MAX_PORTS, CounterType.packets) egr_port_counter;
182 counter(MAX_PORTS, CounterType.packets) igr_port_counter;
183
184 /*
185 We define here the processing to be executed by this ingress pipeline.
186 */
187 apply {
188 if (standard_metadata.ingress_port == CPU_PORT) {
189 /*
190 Packet received from CPU_PORT, this is a packet-out sent by the
191 controller. Skip pipeline processing, set the egress port as
192 requested by the controller (packet_out header) and remove the
193 packet_out header.
194 */
195 standard_metadata.egress_spec = hdr.packet_out.egress_port;
196 hdr.packet_out.setInvalid();
197 } else {
198 /*
199 Packet received from switch port. Apply table0, if action is
200 set_egress_port and packet is IPv4, then apply
201 ip_proto_filter_table.
202 */
203 switch(table0.apply().action_run) {
204 set_egress_port: {
205 if (hdr.ipv4.isValid()) {
206 ip_proto_filter_table.apply();
207 }
208 }
209 }
210 }
211
212 /*
213 For each port counter, we update the cell at index = ingress/egress
214 port. We avoid counting packets sent/received on CPU_PORT or dropped
215 (DROP_PORT).
216 */
217 if (standard_metadata.egress_spec < MAX_PORTS) {
218 egr_port_counter.count((bit<32>) standard_metadata.egress_spec);
219 }
220 if (standard_metadata.ingress_port < MAX_PORTS) {
221 igr_port_counter.count((bit<32>) standard_metadata.ingress_port);
222 }
223 }
224}
225
226//------------------------------------------------------------------------------
227// EGRESS PIPELINE
228//------------------------------------------------------------------------------
229
230control EgressImpl(inout headers_t hdr,
231 inout metadata_t meta,
232 inout standard_metadata_t standard_metadata) {
233 apply {
234 /*
235 Nothing to do on the egress pipeline.
236 */
237 }
238}
239
240//------------------------------------------------------------------------------
241// CHECKSUM HANDLING
242//------------------------------------------------------------------------------
243
244control VerifyChecksumImpl(in headers_t hdr, inout metadata_t meta) {
245 apply {
246 /*
247 Nothing to do here, we assume checksum is always correct.
248 */
249 }
250}
251
252control ComputeChecksumImpl(inout headers_t hdr, inout metadata_t meta) {
253 apply {
254 /*
255 Nothing to do here, as we do not modify packet headers.
256 */
257 }
258}
259
260//------------------------------------------------------------------------------
261// DEPARSER
262//------------------------------------------------------------------------------
263
264control DeparserImpl(packet_out packet, in headers_t hdr) {
265 apply {
266 /*
267 Deparse headers in order. Only valid headers are emitted.
268 */
269 packet.emit(hdr.packet_in);
270 packet.emit(hdr.ethernet);
271 packet.emit(hdr.ipv4);
272 }
273}
274
275//------------------------------------------------------------------------------
276// SWITCH INSTANTIATION
277//------------------------------------------------------------------------------
278
279V1Switch(ParserImpl(),
280 VerifyChecksumImpl(),
281 IngressImpl(),
282 EgressImpl(),
283 ComputeChecksumImpl(),
284 DeparserImpl()) main;