Carmelo Cascone | 700648c | 2018-04-11 12:02:16 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 22 | const bit<16> ETH_TYPE_MYTUNNEL = 0x1212; |
| 23 | const bit<16> ETH_TYPE_IPV4 = 0x800; |
| 24 | |
| 25 | typedef bit<9> port_t; |
| 26 | const port_t CPU_PORT = 255; |
| 27 | |
| 28 | //------------------------------------------------------------------------------ |
| 29 | // HEADERS |
| 30 | //------------------------------------------------------------------------------ |
| 31 | |
| 32 | header ethernet_t { |
| 33 | bit<48> dst_addr; |
| 34 | bit<48> src_addr; |
| 35 | bit<16> ether_type; |
| 36 | } |
| 37 | |
| 38 | header my_tunnel_t { |
| 39 | bit<16> proto_id; |
| 40 | bit<32> tun_id; |
| 41 | } |
| 42 | |
| 43 | header 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") |
| 61 | header 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") |
| 68 | header packet_out_header_t { |
| 69 | bit<9> egress_port; |
| 70 | } |
| 71 | |
| 72 | // For convenience we collect all headers under the same struct. |
| 73 | struct 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. |
| 82 | struct metadata_t { |
| 83 | // Empty. We don't use it in this program. |
| 84 | } |
| 85 | |
| 86 | //------------------------------------------------------------------------------ |
| 87 | // PARSER |
| 88 | //------------------------------------------------------------------------------ |
| 89 | |
| 90 | parser 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 | |
| 134 | control 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 | |
| 258 | control 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 | |
| 270 | control 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 | |
| 276 | control 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 | |
| 286 | control 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 | |
| 301 | V1Switch(c_parser(), |
| 302 | c_verify_checksum(), |
| 303 | c_ingress(), |
| 304 | c_egress(), |
| 305 | c_compute_checksum(), |
| 306 | c_deparser()) main; |