diff options
Diffstat (limited to 'vlan_aggregation/p4src/vlan_aggregation.p4')
| -rw-r--r-- | vlan_aggregation/p4src/vlan_aggregation.p4 | 821 |
1 files changed, 821 insertions, 0 deletions
diff --git a/vlan_aggregation/p4src/vlan_aggregation.p4 b/vlan_aggregation/p4src/vlan_aggregation.p4 new file mode 100644 index 0000000..a2a33f8 --- /dev/null +++ b/vlan_aggregation/p4src/vlan_aggregation.p4 @@ -0,0 +1,821 @@ +/* -*- P4_16 -*- */ + +#include <core.p4> +#include <tna.p4> + + +/************************************************************************* + ************* C O N S T A N T S A N D T Y P E S ******************* +**************************************************************************/ +enum bit<16> ether_type_t { + IPV4 = 0x0800, + ARP = 0x0806, + TPID = 0x8100, + TPID2 = 0x9100, + IPV6 = 0x86DD +} + +enum bit<8> ip_protocol_t { + ICMP = 1, + IGMP = 2, + TCP = 6, + UDP = 17 +} + +enum bit<16> arp_opcode_t { + REQUEST = 1, + REPLY = 2 +} + +enum bit<8> icmp_type_t { + ECHO_REPLY = 0, + ECHO_REQUEST = 8 +} + +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +const int NEXTHOP_ID_WIDTH = 14; +typedef bit<(NEXTHOP_ID_WIDTH)> nexthop_id_t; + +const int MAC_TABLE_SIZE = 65536; +const int VLAN_PORT_TABLE_SIZE = 1 << (7 + 12); +const int NEXTHOP_TABLE_SIZE = 1 << NEXTHOP_ID_WIDTH; +const int SUBVLAN_CHECK_TABLE_SIZE = 12288; + +const int IPV4_HOST_SIZE = 65536; +const int IPV4_LPM_SIZE = 12288; +const int IPV6_HOST_SIZE = 12000; +const int IPV6_LPM_SIZE = 6000; + +/* We can use up to 8 different digest types */ +const bit<3> L2_LEARN_DIGEST = 1; + +/************************************************************************* + *********************** H E A D E R S ********************************* + *************************************************************************/ + +/* Define all the headers the program will recognize */ +/* The actual sets of headers processed by each gress can differ */ + +/* Standard ethernet header */ +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; +} + +header vlan_tag_h { + bit<16> tpid; + bit<3> pcp; + bit<1> dei; + bit<12> vid; +} + +header etherII_h { + ether_type_t ether_type; +} + +header ipv4_t { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + ip_protocol_t protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header ipv4_options_t { + varbit<320> options; +} + +header ipv6_t { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + bit<128> src_addr; + bit<128> dst_addr; +} + +header arp_h { + bit<16> hw_type; + ether_type_t proto_type; + bit<8> hw_addr_len; + bit<8> proto_addr_len; + arp_opcode_t opcode; +} + +header arp_payload_h { + mac_addr_t src_hw_addr; + ipv4_addr_t src_proto_addr; + mac_addr_t dst_hw_addr; + ipv4_addr_t dst_proto_addr; +} + +header icmp_h { + icmp_type_t msg_type; + bit<8> msg_code; + bit<16> checksum; +} + +/*** Internal Headers ***/ + +typedef bit<4> header_type_t; /* These fields can be coombined into one */ +typedef bit<4> header_info_t; /* 8-bit field as well. Exercise for the brave */ + +const header_type_t HEADER_TYPE_BRIDGE = 0xB; +const header_type_t HEADER_TYPE_MIRROR_INGRESS = 0xC; +const header_type_t HEADER_TYPE_MIRROR_EGRESS = 0xD; +const header_type_t HEADER_TYPE_RESUBMIT = 0xA; + +/* + * This is a common "preamble" header that must be present in all internal + * headers. The only time you do not need it is when you know that you are + * not going to have more than one internal header type ever + */ +#define INTERNAL_HEADER \ + header_type_t header_type; \ + header_info_t header_info + + +header inthdr_h { + INTERNAL_HEADER; +} + +/* Bridged metadata */ +header bridge_h { + INTERNAL_HEADER; + + bit<7> pad0; + PortId_t ingress_port; + + bit<3> pcp; + bit<1> dei; + bit<12> vid; +} + +/************************************************************************* + ************** I N G R E S S P R O C E S S I N G ******************* + *************************************************************************/ + + /*********************** H E A D E R S ************************/ + +struct my_ingress_headers_t { + bridge_h bridge; + ethernet_h ethernet; + vlan_tag_h vlan_tag; + etherII_h etherII; + arp_h arp; + arp_payload_h arp_payload; + ipv4_t ipv4; + ipv4_options_t ipv4_options; + ipv6_t ipv6; + icmp_h icmp; +} + + /****** G L O B A L I N G R E S S M E T A D A T A *********/ + +struct port_metadata_t { + bit<3> port_pcp; + bit<12> port_vid; + bit<9> l2_xid; +} + +struct my_ingress_metadata_t { + + bool ipv4_checksum_err; + ipv4_addr_t dst_ipv4; + port_metadata_t port_properties; + + bit<3> pcp; + bit<1> dei; + bit<12> vid; + bit<12> dvid; + bit<9> mac_move; /* Should have the same size as PortId_t */ + bit<1> is_static; + bit<1> smac_hit; + PortId_t ingress_port; +} + + /*********************** P A R S E R **************************/ +parser IngressParser(packet_in pkt, + /* User */ + out my_ingress_headers_t hdr, + out my_ingress_metadata_t meta, + /* Intrinsic */ + out ingress_intrinsic_metadata_t ig_intr_md) +{ + + Checksum() ipv4_checksum; + /* This is a mandatory state, required by Tofino Architecture */ + state start { + pkt.extract(ig_intr_md); + meta.port_properties = port_metadata_unpack<port_metadata_t>(pkt); + transition meta_init; + } + + state meta_init { + meta.pcp = 0; + meta.dei = 0; + meta.vid = 0; + meta.dvid = 0; + meta.mac_move = 0; + meta.is_static = 0; + meta.smac_hit = 0; + meta.ingress_port = ig_intr_md.ingress_port; + meta.ipv4_checksum_err = false; + meta.dst_ipv4 = 0; + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(pkt.lookahead<bit<16>>()) { + (bit<16>)ether_type_t.TPID &&& 0xEFFF: parse_vlan_tag; + default: parse_etherII; + } + } + + state parse_vlan_tag { + pkt.extract(hdr.vlan_tag); + meta.pcp = hdr.vlan_tag.pcp; + meta.dei = hdr.vlan_tag.dei; + meta.vid = hdr.vlan_tag.vid; + transition parse_etherII; + } + + state parse_etherII { + pkt.extract(hdr.etherII); + transition select(hdr.etherII.ether_type) { + ether_type_t.IPV4 : parse_ipv4; + ether_type_t.IPV6 : parse_ipv6; + ether_type_t.ARP : parse_arp; + default: accept; + } + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + meta.dst_ipv4 = hdr.ipv4.dst_addr; + ipv4_checksum.add(hdr.ipv4); + transition select(hdr.ipv4.ihl) { + 0x5 : parse_ipv4_no_options; + 0x6 &&& 0xE : parse_ipv4_options; + 0x8 &&& 0x8 : parse_ipv4_options; + default: reject; + } + } + + state parse_ipv4_options { + pkt.extract( + hdr.ipv4_options, + ((bit<32>)hdr.ipv4.ihl - 32w5) * 32); + + ipv4_checksum.add(hdr.ipv4_options); + transition parse_ipv4_no_options; + } + + state parse_ipv4_no_options { + meta.ipv4_checksum_err = ipv4_checksum.verify(); + + transition select( + hdr.ipv4.ihl, + hdr.ipv4.frag_offset, + hdr.ipv4.protocol) { + (5, 0, ip_protocol_t.ICMP) : parse_icmp; + default: accept; + } + } + + state parse_icmp { + pkt.extract(hdr.icmp); + transition accept; + } + + state parse_arp { + pkt.extract(hdr.arp); + transition select(hdr.arp.hw_type, hdr.arp.proto_type) { + (0x0001, ether_type_t.IPV4) : parse_arp_payload; + default: reject; // Currently the same as accept + } + } + + state parse_arp_payload { + pkt.extract(hdr.arp_payload); + meta.dst_ipv4 = hdr.arp_payload.dst_proto_addr; + transition accept; + } + + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition accept; + } +} + + /***************** M A T C H - A C T I O N *********************/ + +control Ingress( + /* User */ + inout my_ingress_headers_t hdr, + inout my_ingress_metadata_t meta, + /* Intrinsic */ + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) +{ + + nexthop_id_t nexthop_id = 0; + PortId_t dst_sub_port = 0; + bit<16> src_sid = 0; + mac_addr_t dst_mac = 0; + bit<8> ttl_dec = 0; + bit<1> is_to_l3 = 0; + bit<1> is_same_vlan = 0; + +/************************ Common Action *************************/ + action drop() { + ig_dprsr_md.drop_ctl = 1; + } + + action send(PortId_t port) { + ig_tm_md.ucast_egress_port = port; + } +/************************* Port VLAN Table *************************/ + + action vlan_port_miss() { } + + action get_sid_vid(bit<16> superid, bit<12> subid){ + meta.vid = subid; + src_sid = superid; + } + + table vlan_port { + key = { ig_intr_md.ingress_port : exact; } + actions = { + get_sid_vid; + vlan_port_miss; + } + size = MAC_TABLE_SIZE; + const default_aciotn = vlan_port_miss(); + } + +/*************************** Smac **********************************/ + action smac_hit(PortId_t port, bit<1> is_static) { + meta.mac_move = ig_intr_md.ingress_port ^ port; + meta.smac_hit = 1; + meta.is_static = is_static; + } + + action smac_miss() { } + + action smac_drop() { + drop(); exit; + } + + //@idletime_precision(3) + table smac { + key = { + meta.vid : exact; + hdr.ethernet.src_addr : exact; + } + actions = { + smac_hit; smac_miss; smac_drop; + } + size = MAC_TABLE_SIZE; + const default_action = smac_miss(); + //idle_timeout = true; + } + +/*************************** Smac Result **************************/ + action mac_learn_notify() { + ig_dprsr_md.digest_type = L2_LEARN_DIGEST; + } + + table smac_results { + key = { + meta.mac_move : ternary; + meta.is_static : ternary; + meta.smac_hit : ternary; + } + actions = { + mac_learn_notify; NoAction; smac_drop; + } + const entries = { + ( _, _, 0) : mac_learn_notify(); + ( 0, _, 1) : NoAction(); + ( _, 0, 1) : mac_learn_notify(); + ( _, 1, 1) : smac_drop(); + } + } + +/************************** Dmac ****************************/ + + action change_to_l3(bit<1> to_l3) { + is_to_l3 = to_l3; + } + + action dmac_unicast(PortId_t port) { + send(port); + } + + action dmac_miss() { + ig_tm_md.mcast_grp_a = (MulticastGroupId_t)src_sid; + ig_tm_md.rid = src_sid; + /* Set the exclusion id here, since parser can't acces ig_tm_md */ + ig_tm_md.level2_exclusion_id = meta.port_properties.l2_xid; + } + + action dmac_sub_multicast(MulticastGroupId_t mcast_grp) { + ig_tm_md.mcast_grp_a = mcast_grp; + ig_tm_md.rid = (bit<16>)meta.vid; + /* Set the exclusion id here, since parser can't acces ig_tm_md */ + ig_tm_md.level2_exclusion_id = meta.port_properties.l2_xid; + } + + action dmac_super_multicast(MulticastGroupId_t mcast_grp) { + ig_tm_md.mcast_grp_a = mcast_grp; + ig_tm_md.rid = src_sid; + /* Set the exclusion id here, since parser can't acces ig_tm_md */ + ig_tm_md.level2_exclusion_id = meta.port_properties.l2_xid; + } + + action dmac_drop() { + drop(); + exit; + } + + table dmac { + key = { + meta.vid : exact; + hdr.ethernet.dst_addr : exact; + } + actions = { + dmac_unicast; dmac_miss; dmac_sub_multicast; dmac_super_multicast; dmac_drop; change_to_l3; + } + size = MAC_TABLE_SIZE; + default_action = dmac_miss(); + } + + +/**************************** Ipv4/Ipv6 Table*******************************/ + action set_nexthop(nexthop_id_t id) { + nexthop_id = id; + } + + table ipv4_host { + key = { + hdr.ipv4.dst_addr : exact; + } + actions = { + set_nexthop; + @defaultonly NoAction; + } + const default_action = NoAction(); + size = IPV4_HOST_SIZE; + } + + table ipv4_lpm { + key = { + hdr.ipv4.dst_addr : lpm; + } + actions = { set_nexthop; } + default_action = set_nexthop(0); + size = IPV4_LPM_SIZE; + } + + table ipv6_host { + key = { hdr.ipv6.dst_addr : exact; } + actions = { + set_nexthop; + @defaultonly NoAction; + } + const default_action = NoAction(); + size = IPV6_HOST_SIZE; + } + + table ipv6_lpm { + key = { hdr.ipv6.dst_addr : lpm; } + actions = { set_nexthop; } + + default_action = set_nexthop(0); + size = IPV6_LPM_SIZE; + } + + +/******************************** Nexthop ***********************************/ + + + action l3_switch(PortId_t port, mac_addr_t new_mac_da, mac_addr_t new_mac_sa) { + hdr.ethernet.dst_addr = new_mac_da; + hdr.ethernet.src_addr = new_mac_sa; + ttl_dec = 1; + send(port); + } + + + table nexthop { + key = { nexthop_id : exact; } + actions = { send; drop; l3_switch; } + default_action = send(64); + size = NEXTHOP_TABLE_SIZE; + } + +/************************** SubVlan_check table *************************/ + + + action subvlan_send(PortId_t port){ + dst_sub_port = port; + send(port); + } + + table subvlan_check{ + key = { + meta.dst_ipv4:exact; + meta.vid: exact; + } + actions = { + subvlan_send; + change_to_l3; + } + default_action = change_to_l3(1); + size = SUBVLAN_CHECK_TABLE_SIZE; + } + + +/************************** ARP Response ********************************/ + action send_back() { + ig_tm_md.ucast_egress_port = ig_intr_md.ingress_port; + } + + action send_arp_reply(mac_addr_t gate_mac) { + hdr.ethernet.dst_addr = hdr.arp_payload.src_hw_addr; + hdr.ethernet.src_addr = gate_mac; + hdr.arp.opcode = arp_opcode_t.REPLY; + + hdr.arp_payload.dst_hw_addr = hdr.arp_payload.src_hw_addr; + hdr.arp_payload.dst_proto_addr = hdr.arp_payload.src_proto_addr; + hdr.arp_payload.src_hw_addr = gate_mac; + hdr.arp_payload.src_proto_addr = meta.dst_ipv4; + + send_back(); + } + + table arp_reply { + key = { + //hdr.arp.isValid() : exact; + //hdr.arp_payload.isValid() : exact; + hdr.arp.opcode : exact; + } + actions = { + send_arp_reply; + drop; + } + default_action = drop(); + } + +/****************************** Apply *******************************/ + + apply { + + /* Assign Port-based VLAN to untagged/priority-tagged packets */ + if (meta.vid == 0) { + meta.vid = meta.port_properties.port_vid; + } + + if (!hdr.vlan_tag.isValid()) { + meta.pcp = meta.port_properties.port_pcp; + } + + vlan_port.apply(); + smac.apply(); + smac_results.apply(); + switch (dmac.apply().action_run) { + dmac_unicast: { + if (ig_intr_md.ingress_port == + ig_tm_md.ucast_egress_port) { + drop(); + } + } + } + + subvlan_check.apply(); + + if(is_to_l3 == 1) { + if(hdr.arp.isValid() && hdr.arp_payload.isValid()){ + arp_reply.apply(); + } + else { + if (hdr.ipv4.isValid()) { + if (meta.ipv4_checksum_err == false) { + if (!ipv4_host.apply().hit) { + ipv4_lpm.apply(); + } + } + } + else if (hdr.ipv6.isValid()) { + if (hdr.ipv6.hop_limit > 1) { + if (!ipv6_host.apply().hit) { + ipv6_lpm.apply(); + } + } + } + nexthop.apply(); + if (hdr.ipv4.isValid()) { + hdr.ipv4.ttl = hdr.ipv4.ttl |-| ttl_dec; + } + else if (hdr.ipv6.isValid()) { + hdr.ipv6.hop_limit = hdr.ipv6.hop_limit |-| ttl_dec; + } + } + } + + + /* Bridge metadata to the egress pipeline */ + hdr.bridge.setValid(); + hdr.bridge.header_type = HEADER_TYPE_BRIDGE; + hdr.bridge.header_info = 0; /* Ignore */ + hdr.bridge.ingress_port = ig_intr_md.ingress_port; + hdr.bridge.pcp = meta.pcp; + hdr.bridge.dei = meta.dei; + hdr.bridge.vid = meta.vid; + } +} + + /********************* D E P A R S E R ************************/ + +/* This struct is needed for proper digest receive API generation */ +struct l2_digest_t { + bit<12> vid; + bit<48> src_mac; + bit<9> ingress_port; + bit<9> mac_move; + bit<1> is_static; + bit<1> smac_hit; +} + +control IngressDeparser(packet_out pkt, + /* User */ + inout my_ingress_headers_t hdr, + in my_ingress_metadata_t meta, + /* Intrinsic */ + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) +{ + Checksum() ipv4_checksum; + Digest <l2_digest_t>() l2_digest; + + apply { + if (ig_dprsr_md.digest_type == L2_LEARN_DIGEST) { + l2_digest.pack({ + meta.vid, + hdr.ethernet.src_addr, + meta.ingress_port, + meta.mac_move, + meta.is_static, + meta.smac_hit }); + } + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ + hdr.ipv4.version, + hdr.ipv4.ihl, + hdr.ipv4.diffserv, + hdr.ipv4.total_len, + hdr.ipv4.identification, + hdr.ipv4.flags, + hdr.ipv4.frag_offset, + hdr.ipv4.ttl, + hdr.ipv4.protocol, + hdr.ipv4.src_addr, + hdr.ipv4.dst_addr, + hdr.ipv4_options.options + }); + pkt.emit(hdr); + } +} + + +/************************************************************************* + **************** E G R E S S P R O C E S S I N G ******************* + *************************************************************************/ + + /*********************** H E A D E R S ************************/ + +struct my_egress_headers_t { + ethernet_h ethernet; + vlan_tag_h vlan_tag; + etherII_h etherII; +} + + /******** G L O B A L E G R E S S M E T A D A T A *********/ + +struct my_egress_metadata_t { + bridge_h bridge; + + PortId_t ingress_port; + bit<3> pcp; + bit<1> dei; + bit<12> vid; +} + + /*********************** P A R S E R **************************/ + +parser EgressParser(packet_in pkt, + /* User */ + out my_egress_headers_t hdr, + out my_egress_metadata_t meta, + /* Intrinsic */ + out egress_intrinsic_metadata_t eg_intr_md) +{ + inthdr_h inthdr; + + /* This is a mandatory state, required by Tofino Architecture */ + state start { + pkt.extract(eg_intr_md); + inthdr = pkt.lookahead<inthdr_h>(); + transition select(inthdr.header_type, inthdr.header_info) { + ( HEADER_TYPE_BRIDGE, _ ) : parse_bridge; + default : reject; + } + } + + state parse_bridge { + pkt.extract(meta.bridge); + meta.ingress_port = meta.bridge.ingress_port; + meta.pcp = meta.bridge.pcp; + meta.dei = meta.bridge.dei; + meta.vid = meta.bridge.vid; + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(pkt.lookahead<bit<16>>()) { + (bit<16>)ether_type_t.TPID &&& 0xEFFF: parse_vlan_tag; + default: parse_etherII; + } + } + + state parse_vlan_tag { + pkt.extract(hdr.vlan_tag); + transition parse_etherII; + } + + state parse_etherII { + pkt.extract(hdr.etherII); + transition accept; + } +} + + /***************** M A T C H - A C T I O N *********************/ + +/* This program is written in a modular fashion using multiple different + * implementations of the Egress control. You can try to see which ones are + * more efficient + */ + +control Egress( + /* User */ + inout my_egress_headers_t hdr, + inout my_egress_metadata_t meta, + /* Intrinsic */ + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_prsr_md, + inout egress_intrinsic_metadata_for_deparser_t eg_dprsr_md, + inout egress_intrinsic_metadata_for_output_port_t eg_oport_md) +{ + apply{ + } +} + + /********************* D E P A R S E R ************************/ + +control EgressDeparser(packet_out pkt, + /* User */ + inout my_egress_headers_t hdr, + in my_egress_metadata_t meta, + /* Intrinsic */ + in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md) +{ + apply { + pkt.emit(hdr); + } +} + + +/************ F I N A L P A C K A G E ******************************/ +Pipeline( + IngressParser(), + Ingress(), + IngressDeparser(), + EgressParser(), + Egress(), + EgressDeparser() +) pipe; + +Switch(pipe) main; |
