blob: 69e622bf4753bb34b0f699033e2c254920bd5523 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <core.p4>
#include <v1model.p4>
#include "../header.p4"
#include "../action.p4"
control Next (
inout parsed_headers_t hdr,
inout fabric_metadata_t fabric_metadata,
inout standard_metadata_t standard_metadata) {
/*
* General actions.
*/
action pop_vlan() {
hdr.ethernet.ether_type = hdr.vlan_tag.ether_type;
hdr.vlan_tag.setInvalid();
}
action rewrite_smac(mac_addr_t smac) {
hdr.ethernet.src_addr = smac;
}
action rewrite_dmac(mac_addr_t dmac) {
hdr.ethernet.dst_addr = dmac;
}
action push_mpls (mpls_label_t label, bit<3> tc) {
// Suppose that the maximum number of label is one.
hdr.mpls.setValid();
hdr.vlan_tag.ether_type = ETHERTYPE_MPLS;
hdr.mpls.label = label;
hdr.mpls.tc = tc;
hdr.mpls.bos = 1w1; // BOS = TRUE
hdr.mpls.ttl = DEFAULT_MPLS_TTL;
}
/*
* VLAN Metadata Table.
* Modify VLAN Id according to metadata from NextObjective(next id).
*/
direct_counter(CounterType.packets_and_bytes) vlan_meta_counter;
action set_vlan(vlan_id_t new_vlan_id) {
hdr.vlan_tag.vlan_id = new_vlan_id;
vlan_meta_counter.count();
}
table vlan_meta {
key = {
fabric_metadata.next_id: exact;
}
actions = {
set_vlan;
@defaultonly nop;
}
default_action = nop;
counters = vlan_meta_counter;
}
/*
* Simple Table.
* Do a single egress action based on next id.
*/
direct_counter(CounterType.packets_and_bytes) simple_counter;
action output_simple(port_num_t port_num) {
standard_metadata.egress_spec = port_num;
simple_counter.count();
}
action set_vlan_output(vlan_id_t new_vlan_id, port_num_t port_num){
hdr.vlan_tag.vlan_id = new_vlan_id;
output_simple(port_num);
}
action l3_routing_simple(port_num_t port_num, mac_addr_t smac, mac_addr_t dmac) {
rewrite_smac(smac);
rewrite_dmac(dmac);
output_simple(port_num);
}
action mpls_routing_v4_simple(port_num_t port_num, mac_addr_t smac, mac_addr_t dmac,
mpls_label_t label) {
l3_routing_simple(port_num, smac, dmac);
// TODO: set tc according to diffserv from ipv4
push_mpls(label, 3w0);
}
action mpls_routing_v6_simple (port_num_t port_num, mac_addr_t smac, mac_addr_t dmac,
mpls_label_t label) {
l3_routing_simple(port_num, smac, dmac);
// TODO: set tc according to traffic_class from ipv4
push_mpls(label, 3w0);
}
action l3_routing_vlan(port_num_t port_num, mac_addr_t smac, mac_addr_t dmac, vlan_id_t new_vlan_id) {
rewrite_smac(smac);
rewrite_dmac(dmac);
set_vlan_output(new_vlan_id, port_num);
}
table simple {
key = {
fabric_metadata.next_id: exact;
}
actions = {
output_simple;
set_vlan_output;
l3_routing_simple;
mpls_routing_v4_simple;
mpls_routing_v6_simple;
l3_routing_vlan;
}
counters = simple_counter;
}
/*
* Hashed table.
* Execute an action profile group based on next id.
* One action profile group may contains multple egress decision.
* The execution picks one action profile group memebr by using 5-tuple
* hashing.
*/
action_selector(HashAlgorithm.crc16, 32w64, 32w16) ecmp_selector;
direct_counter(CounterType.packets_and_bytes) hashed_counter;
action output_hashed(port_num_t port_num) {
standard_metadata.egress_spec = port_num;
hashed_counter.count();
}
action l3_routing_hashed(port_num_t port_num, mac_addr_t smac, mac_addr_t dmac) {
rewrite_smac(smac);
rewrite_dmac(dmac);
output_hashed(port_num);
}
action mpls_routing_v4_hashed (port_num_t port_num, mac_addr_t smac, mac_addr_t dmac,
mpls_label_t label) {
l3_routing_hashed(port_num, smac, dmac);
// TODO: set tc according to diffserv from ipv4
push_mpls(label, 3w0);
}
action mpls_routing_v6_hashed (port_num_t port_num, mac_addr_t smac, mac_addr_t dmac,
mpls_label_t label) {
l3_routing_hashed(port_num, smac, dmac);
// TODO: set tc according to traffic_class from ipv4
push_mpls(label, 3w0);
}
table hashed {
key = {
fabric_metadata.next_id: exact;
hdr.ipv4.dst_addr: selector;
hdr.ipv4.src_addr: selector;
fabric_metadata.ip_proto: selector;
fabric_metadata.l4_src_port: selector;
fabric_metadata.l4_dst_port: selector;
}
actions = {
l3_routing_hashed;
mpls_routing_v4_hashed;
mpls_routing_v6_hashed;
}
implementation = ecmp_selector;
counters = hashed_counter;
}
/*
* Multicast Table.
* Setup multicast group id for packet replication engine (PRE).
*/
direct_counter(CounterType.packets_and_bytes) multicast_counter;
action set_mcast_group(group_id_t gid) {
standard_metadata.mcast_grp = gid;
fabric_metadata.is_multicast = _TRUE;
multicast_counter.count();
}
table multicast {
key = {
fabric_metadata.next_id: exact;
}
actions = {
set_mcast_group;
}
counters = multicast_counter;
}
apply {
vlan_meta.apply();
if (!simple.apply().hit) {
if (!hashed.apply().hit) {
if (!multicast.apply().hit) {
// Next ID doesn't match any table.
return;
}
}
}
// Decrement TTL
if (!hdr.mpls.isValid()) {
if(hdr.ipv4.isValid()) {
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}
#ifdef WITH_IPV6
else if (hdr.ipv6.isValid()) {
hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1;
}
#endif // WITH_IPV6
}
}
}
control EgressNextControl (
inout parsed_headers_t hdr,
inout fabric_metadata_t fabric_metadata,
inout standard_metadata_t standard_metadata) {
/*
* Egress VLAN Table.
* Pops VLAN tag according to interface(Port and VLAN) configuration.
*/
direct_counter(CounterType.packets_and_bytes) egress_vlan_counter;
action pop_vlan() {
hdr.ethernet.ether_type = hdr.vlan_tag.ether_type;
hdr.vlan_tag.setInvalid();
egress_vlan_counter.count();
}
table egress_vlan {
key = {
hdr.vlan_tag.vlan_id: exact;
standard_metadata.egress_port: exact;
}
actions = {
pop_vlan;
@defaultonly nop;
}
default_action = nop;
counters = egress_vlan_counter;
}
apply {
if (fabric_metadata.is_multicast == _TRUE
&& standard_metadata.ingress_port == standard_metadata.egress_port) {
drop_now();
}
egress_vlan.apply();
}
}