/* -*- P4_16 -*- */ #include #include typedef bit<48> mac_addr_t; typedef bit<32> ipv4_addr_t; #define ETHERTYPE_TPID 0x8100 #define ETHERTYPE_IPV4 0x0800 #define IP_PROTOCOL_UDP 17 const int MAC_TABLE_SIZE = 65536; const int MEM_TABLE_SIZE = 64; 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 { bit<48> dst_addr; bit<48> src_addr; bit<16> ether_type; } header vlan_tag_h { bit<16> tpid; bit<3> pcp; bit<1> dei; bit<12> vid; bit<16> ether_type; } header ipv4_h { 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; bit<8> protocol; bit<16> hdr_checksum; bit<32> src_addr; bit<32> dst_addr; } header udp_h { bit<16> src_port; bit<16> dst_port; bit<16> len; bit<16> checksum; } header mem_h { bit<8> op_type; bit<64> req_id; bit<64> addr; bit<16> len; bit<64> as_id; } /************************************************************************* ************** 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 { ethernet_h ethernet; ipv4_h ipv4; udp_h udp; mem_h mem; } /****** G L O B A L I N G R E S S M E T A D A T A *********/ struct my_ingress_metadata_t { bit<9> mac_move; bit<1> is_static; bit<1> smac_hit; PortId_t ingress_port; } /*********************** P A R S E R **************************/ parser IngressParser( packet_in pkt, out my_ingress_headers_t hdr, out my_ingress_metadata_t meta, out ingress_intrinsic_metadata_t ig_intr_md) { /* This is a mandatory state, required by Tofino Architecture */ state start { pkt.extract(ig_intr_md); pkt.advance(PORT_METADATA_SIZE); transition meta_init; } state meta_init { meta.mac_move = 0; meta.is_static = 0; meta.smac_hit = 0; meta.ingress_port = ig_intr_md.ingress_port; transition parse_ethernet; } state parse_ethernet { pkt.extract(hdr.ethernet); transition select(hdr.ethernet.ether_type) { ETHERTYPE_IPV4: parse_ipv4; default: accept; } } state parse_ipv4 { pkt.extract(hdr.ipv4); transition select(hdr.ipv4.protocol) { IP_PROTOCOL_UDP: parse_udp; default: accept; } } state parse_udp { pkt.extract(hdr.udp); transition parse_mem; } state parse_mem { pkt.extract(hdr.mem); 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) { action drop() { ig_dprsr_md.drop_ctl = 1; } action send(PortId_t port) { ig_tm_md.ucast_egress_port = port; } 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 = { hdr.ethernet.src_addr : exact; } actions = { smac_hit; smac_miss; smac_drop; } size = MAC_TABLE_SIZE; const default_action = smac_miss(); idle_timeout = true; } 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(); } } action dmac_unicast(PortId_t port) { send(port); } action dmac_miss() { ig_tm_md.mcast_grp_a = 1; } action dmac_drop() { drop(); exit; } table dmac { key = { hdr.ethernet.dst_addr : exact; } actions = { dmac_unicast; dmac_miss; dmac_drop; } size = MAC_TABLE_SIZE; default_action = dmac_miss(); } action mem_forward(PortId_t port) { ig_tm_md.mcast_grp_a = 0; send(port); } action mem_miss() { drop(); exit; } table mem { key = { hdr.mem.as_id : exact; } actions = { mem_forward; mem_miss; } size = MEM_TABLE_SIZE; default_action = mem_miss(); } apply { ig_tm_md.bypass_egress = 1w1; smac.apply(); smac_results.apply(); switch (dmac.apply().action_run) { dmac_unicast: { /* Unicast source pruning */ if (ig_intr_md.ingress_port == ig_tm_md.ucast_egress_port) { drop(); } } dmac_miss: { if (hdr.mem.isValid()) { mem.apply(); } } } } } /********************* D E P A R S E R ************************/ /* This struct is needed for proper digest receive API generation */ struct l2_digest_t { 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) { Digest () l2_digest; apply { if (ig_dprsr_md.digest_type == L2_LEARN_DIGEST) { l2_digest.pack({ hdr.ethernet.src_addr, meta.ingress_port, meta.mac_move, meta.is_static, meta.smac_hit }); } 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; ipv4_h ipv4; } /******** 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 { } /*********************** 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) { /* This is a mandatory state, required by Tofino Architecture */ state start { pkt.extract(eg_intr_md); transition parse_ethernet; } state parse_ethernet { pkt.extract(hdr.ethernet); transition select(pkt.lookahead>()) { ETHERTYPE_TPID: parse_vlan_tag; ETHERTYPE_IPV4: parse_ipv4; default: accept; } } state parse_vlan_tag { pkt.extract(hdr.vlan_tag); transition select(hdr.vlan_tag.ether_type) { ETHERTYPE_IPV4: parse_ipv4; default: accept; } } state parse_ipv4 { pkt.extract(hdr.ipv4); transition accept; } } /***************** M A T C H - A C T I O N *********************/ 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;