diff options
| author | yyyIce <[email protected]> | 2020-04-24 00:45:04 +0800 |
|---|---|---|
| committer | yyyIce <[email protected]> | 2020-04-24 00:45:04 +0800 |
| commit | 8acfaff12ad7f7b5bfb517ff8c6f697ed053237f (patch) | |
| tree | 71628f3f1109a544542d495b82d8ae40a9411e7c | |
| parent | 247bc895be2736913b0efc86c13435bea35e5697 (diff) | |
add 1.vlan_aggregation
| -rw-r--r-- | vlan_aggregation/cmd.txt | 29 | ||||
| -rw-r--r-- | vlan_aggregation/p4src/vlan_aggregation.p4 | 821 | ||||
| -rw-r--r-- | vlan_aggregation/ptf-tests/test.py | 1140 |
3 files changed, 1990 insertions, 0 deletions
diff --git a/vlan_aggregation/cmd.txt b/vlan_aggregation/cmd.txt new file mode 100644 index 0000000..2e0d85c --- /dev/null +++ b/vlan_aggregation/cmd.txt @@ -0,0 +1,29 @@ +#all ttys need to set $PATH +cd ~/bf-sde-9.0.0 +. ~/tools/set_sde.bash + +#tty1 +~/tools/p4_build.sh ~/labs/simple_vlanagg/vlan_aggregation.p4 +sudo $SDE_INSTALL/bin/veth_setup.sh +./run_tofino_model.sh -p vlan_aggregation + +#tty2 +./run_switchd.sh -p vlan_aggregation + +#tty3 +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.HostSend +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.HostDrop +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.HostL3Switch +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.LpmSend +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.LpmDrop +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.LpmL3Switch +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.BadTTL +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.BadChecksum +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.ArpReply +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.DiffSubVLANSend +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.SameSubVLANSend +./run_p4_tests.sh -p vlan_aggregation -t ~/labs/simple_vlanagg/ -s test.SameSubVLANSend + + +#may use tty4 +./run_bfshell.sh 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; diff --git a/vlan_aggregation/ptf-tests/test.py b/vlan_aggregation/ptf-tests/test.py new file mode 100644 index 0000000..1f8c2c2 --- /dev/null +++ b/vlan_aggregation/ptf-tests/test.py @@ -0,0 +1,1140 @@ +""" +Simple PTF test for vlan aggregation +""" + +######### STANDARD MODULE IMPORTS ######## +import logging +import grpc +import pdb + +######### PTF modules for BFRuntime Client Library APIs ####### +import ptf +from ptf.testutils import * +from bfruntime_client_base_tests import BfRuntimeTest +import bfrt_grpc.bfruntime_pb2 as bfruntime_pb2 +import bfrt_grpc.client as gc +import bfrt_grpc.info_parse as info_parse + +########## Basic Initialization ############ +class BaseProgramTest(BfRuntimeTest): + # The setUp() method is used to prepare the test fixture. Typically + # you would use it to establich connection to the gRPC Server + # + # You can also put the initial device configuration there. However, + # if during this process an error is encountered, it will be considered + # as a test error (meaning the test is incorrect), + # rather than a test failure + # + # Here is the stuff we set up that is ready to use + # client_id + # p4_name + # bfrt_info + # dev + # dev_tgt + # allports + # tables -- the list of tables + # Individual tables of the program with short names + # ipv4_host + # ipv4_lpm + # nexthop + def setUp(self): + self.client_id = 0 + self.p4_name = "vlan_aggregation" + self.dev = 0 + self.dev_tgt = gc.Target(self.dev, pipe_id=0xFFFF) + + print("\n") + print("Test Setup") + print("==========") + + BfRuntimeTest.setUp(self, self.client_id, self.p4_name) + self.bfrt_info = self.interface.bfrt_info_get(self.p4_name) + + print(" Connected to Device: {}, Program: {}, ClientId: {}".format( + self.dev, self.p4_name, self.client_id)) + + # Since this class is not a test per se, we can use the setup method + # for common setup. For example, we can have our tables and annotations + # ready + self.vlan_port = self.bfrt_info.table_get("Ingress.vlan_port") + + self.smac = self.bfrt_info.table_get("Ingress.smac") + self.smac.info.key_field_annotation_add("hdr.ethernet.src_addr", "mac") + + self.smac_results = self.bfrt_info.table_get("Ingress.smac_results") + + self.dmac = self.bfrt_info.table_get("Ingress.dmac") + self.dmac.info.key_field_annotation_add("hdr.ethernet.dst_addr", "mac") + + self.subvlan_check = self.bfrt_info.table_get("Ingress.subvlan_check") + self.subvlan_check.info.key_field_annotation_add("meta.dst_ipv4", "ipv4") + + self.arp_reply = self.bfrt_info.table_get("Ingress.arp_reply") + self.arp_reply.info.data_field_annotation_add("gate_mac", "Ingress.send_arp_reply", "mac") + + self.ipv4_host = self.bfrt_info.table_get("Ingress.ipv4_host") + self.ipv4_host.info.key_field_annotation_add("hdr.ipv4.dst_addr", "ipv4") + + self.ipv4_lpm = self.bfrt_info.table_get("Ingress.ipv4_lpm") + self.ipv4_lpm.info.key_field_annotation_add("hdr.ipv4.dst_addr", "ipv4") + + self.nexthop = self.bfrt_info.table_get("Ingress.nexthop") + self.nexthop.info.data_field_annotation_add("new_mac_da", "Ingress.l3_switch", "mac") + self.nexthop.info.data_field_annotation_add("new_mac_sa", "Ingress.l3_switch", "mac") + + self.tables = [ self.vlan_port, self.smac, self.smac_results, self.dmac, self.subvlan_check, self.arp_reply, self.ipv4_host, self.ipv4_lpm, self.nexthop ] + + # Create a list of all ports available on the device + self.swports = [] + for (device, port, ifname) in ptf.config['interfaces']: + self.swports.append(port) + self.swports.sort() + + # Optional, but highly recommended + self.cleanUp() + + # Use Cleanup Method to clear the tables before and after the test starts + # (the latter is done as a part of tearDown() + def cleanUp(self): + print("\n") + print("Table Cleanup:") + print("==============") + + try: + for t in self.tables: + print(" Clearing Table {}".format(t.info.name_get())) + keys = [] + for (d, k) in t.entry_get(self.dev_tgt): + if k is not None: + keys.append(k) + t.entry_del(self.dev_tgt, keys) + # Not all tables support default entry + try: + t.default_entry_reset(self.dev_tgt) + except: + pass + except Exception as e: + print("Error cleaning up: {}".format(e)) + + # Use tearDown() method to return the DUT to the initial state by cleaning + # all the configuration and clearing up the connection + def tearDown(self): + print("\n") + print("Test TearDown:") + print("==============") + + self.cleanUp() + + # Call the Parent tearDown + BfRuntimeTest.tearDown(self) + +# +# Individual tests can now be subclassed from BaseProgramTest +# + +############################################################################ +################# I N D I V I D U A L T E S T S ######################### +############################################################################ +class ArpReply(BaseProgramTest): + def runTest(self): + #vlan-port->smac->smac_results->dmac->arp_reply + ingress_port = test_param_get("ingress_port", 1) + superid = test_param_get('superid', 1) + subid = test_param_get('subid', 1) + mac_src = test_param_get("mac_src", "00:00:01:00:00:01") #192.168.10.1's mac + is_static = test_param_get('is_static', 1) + is_to_l3 = test_param_get('is_to_l3', 1) + mac_dst = test_param_get("mac_dst", "ff:ff:ff:ff:ff:ff") #L3 gateway mac + gate_mac = test_param_get("gate_mac", "01:02:03:04:05:06") #L3 gateway mac + ipv4_dst = test_param_get("ipv4_dst", "192.168.10.16") + is_valid = test_param_get("is_valid", True) + is_opcode = test_param_get("is_opcode", 1) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in vlan_port: vlan_port --> get_sid_vid(superid, subid) + # + key = self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', ingress_port)]) + + data = self.vlan_port.make_data( + [gc.DataTuple('superid',superid), + gc.DataTuple('subid',subid)], + "Ingress.get_sid_vid") + + self.vlan_port.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to vlan_port: ingress_port{} --> get_sid_vid({},{})".format( + ingress_port, superid, subid)) + + # + # Program an entry in smac: subid, mac_src --> smac_hit(ingress_port, is_static) + # + key = self.smac.make_key([gc.KeyTuple('meta.vid', subid), + gc.KeyTuple('hdr.ethernet.src_addr', mac_src)]) + + data = self.smac.make_data([gc.DataTuple('port', ingress_port), gc.DataTuple('is_static',is_static)], + "Ingress.smac_hit") + + self.smac.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry smac: meta.vid:{} hdr.ethernet.src_addr{} --> smac_hit({},{})".format( + subid, mac_src, ingress_port, is_static)) + + # + # Program an entry in dmac: subid, mac_dst --> change_to_l3(to_l3) + # + key = self.dmac.make_key([ + gc.KeyTuple('meta.vid', subid), + gc.KeyTuple('hdr.ethernet.dst_addr', mac_dst)]) + + data = self.dmac.make_data( + [gc.DataTuple('to_l3', is_to_l3)], + "Ingress.change_to_l3") + + self.dmac.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry smac: meta.vid:{} hdr.ethernet.dst_addr{} --> change_to_l3({})".format( + subid, mac_dst,is_static)) + + # + # Program an entry in arp_reply: hdr.arp.isValid(),hdr.arp_payload.isValid(),hdr.arp.opcode --> send_arp_reply(gate_mac) + # + key = self.arp_reply.make_key([ + #gc.KeyTuple('hdr.arp.isValid()', is_valid), + #gc.KeyTuple('hdr.arp_payload.isValid()', is_valid), + gc.KeyTuple('hdr.arp.opcode', is_opcode)]) + + data = self.arp_reply.make_data( + [gc.DataTuple('gate_mac', gate_mac)], + "Ingress.send_arp_reply") + + self.arp_reply.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry in arp_reply: arp_reply: --> change_to_l3({})".format(is_to_l3)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + send_pkt = simple_arp_packet(ip_tgt=ipv4_dst, pktlen=100) + send_packet(self, ingress_port, send_pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the packet to be forwarded to port {}" + .format(ingress_port)) + + exp_pkt = copy.deepcopy(send_pkt) + exp_pkt[Ether].dst = send_pkt[ARP].hwsrc + exp_pkt[Ether].src = gate_mac + exp_pkt[ARP].op = 2 + exp_pkt[ARP].hwsrc = gate_mac + exp_pkt[ARP].psrc = ipv4_dst + exp_pkt[ARP].hwdst = send_pkt[ARP].hwsrc + exp_pkt[ARP].pdst = send_pkt[ARP].psrc + + verify_packet(self, exp_pkt, ingress_port) + print(" Packet received of port {}".format(ingress_port)) + + + +class SameSubVLANSend(BaseProgramTest): + def runTest(self): + #vlan-port->smac->smac_results->dmac->subvlan_check + port_1 = test_param_get("port_1", 1) + port_2 = test_param_get("port_2", 2) + superid = test_param_get('superid', 1) + subid_1 = test_param_get('subid_1', 1) + subid_2 = test_param_get('subid_2', 1) + mac_src = test_param_get("mac_src", "00:00:01:00:00:01") #192.168.10.1's mac + is_static = test_param_get('is_static', 1) + mac_dst = test_param_get("mac_dst", "00:00:02:00:00:02") #192.168.10.2's mac + #is_to_l3 = test_param_get('is_to_l3', 1) + ipv4_dst = test_param_get("ipv4_dst", "192.168.10.2") + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in vlan_port: vlan_port --> get_sid_vid(superid, src_subid) + # + key_list = [ + self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', port_1)]), + self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', port_2)]) + ] + + data_list = [ + self.vlan_port.make_data([gc.DataTuple('superid',superid), gc.DataTuple('subid',subid_1)], "Ingress.get_sid_vid"), + self.vlan_port.make_data([gc.DataTuple('superid',superid), gc.DataTuple('subid',subid_2)], "Ingress.get_sid_vid") + ] + + self.vlan_port.entry_add(self.dev_tgt, key_list, data_list); + print("Add vlan_port: ingress_port{} --> get_sid_vid({},{})".format(port_1, superid, subid_1)) + print("Add vlan_port: ingress_port{} --> get_sid_vid({},{})".format(port_2, superid, subid_2)) + + # + # Program an entry in smac: subid, mac_src --> smac_hit(ingress_port, is_static) + # + key_list = [ + self.smac.make_key([gc.KeyTuple('meta.vid', subid_1), gc.KeyTuple('hdr.ethernet.src_addr', mac_src)]), + self.smac.make_key([gc.KeyTuple('meta.vid', subid_2), gc.KeyTuple('hdr.ethernet.src_addr', mac_dst)]) + ] + + data_list = [ + self.smac.make_data([gc.DataTuple('port', port_1), gc.DataTuple('is_static',is_static)],"Ingress.smac_hit"), + self.smac.make_data([gc.DataTuple('port', port_2), gc.DataTuple('is_static',is_static)],"Ingress.smac_hit") + ] + + self.smac.entry_add(self.dev_tgt, key_list, data_list); + print("Add smac: subid:{}, mac_src:{} --> get_sid_vid({},{})".format( + subid_1, mac_src, port_1, is_static)) + print("Add smac: subid:{}, mac_src:{} --> get_sid_vid({},{})".format( + subid_2, mac_dst, port_2, is_static)) + + # + # Program an entry in subvlan_check: meta.dst_ipv4 --> subvlan_send(subid, port) + # + key_list = [ + self.subvlan_check.make_key([gc.KeyTuple('meta.dst_ipv4', ipv4_dst), gc.KeyTuple('meta.vid', subid_2)]) + ] + + data_list = [ + self.subvlan_check.make_data([gc.DataTuple('port',port_2)],"Ingress.subvlan_send") + ] + self.subvlan_check.entry_add(self.dev_tgt, key_list, data_list); + print("Add subvlan_check: meta.dst_ipv4:{} --> subvlan_send({},{})".format( + ipv4_dst, subid_2, port_2)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, port_1)) + pkt = simple_tcp_packet(eth_dst=mac_dst, + eth_src=mac_src, + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, port_1, pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the packet to be forwarded to port {}" + .format(port_2)) + verify_packet(self, pkt, port_2) + print(" Packet received of port {}".format(port_2)) + +class DiffSubVLANSend(BaseProgramTest): + def runTest(self): + #vlan_port->smac->smac_results->subvlan_check->ipv4_host->nexthop + #the entries in smac_results is static + port_1 = test_param_get("port_1", 1) + port_2 = test_param_get("port_2", 3) + superid = test_param_get('superid', 1) + subid_1 = test_param_get('subid_1', 1) + subid_2 = test_param_get('subid_2', 2) + mac_src = test_param_get("mac_src", "00:00:01:00:00:01") #192.168.10.1's mac + is_static = test_param_get('is_static', 1) + mac_dst = test_param_get("mac_dst", "00:00:03:00:00:03") #L3 gateway mac + #is_to_l3 = test_param_get('is_to_l3', 1) + ipv4_dst = test_param_get("ipv4_dst", "192.168.10.3") + nexthop_id = test_param_get("nexthop_id", 100) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in vlan_port: vlan_port --> get_sid_vid(superid, src_subid) + # + key_list = [ + self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', port_1)]), + self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', port_2)]) + ] + + data_list = [ + self.vlan_port.make_data([gc.DataTuple('superid',superid), gc.DataTuple('subid',subid_1)], "Ingress.get_sid_vid"), + self.vlan_port.make_data([gc.DataTuple('superid',superid), gc.DataTuple('subid',subid_2)], "Ingress.get_sid_vid") + ] + + self.vlan_port.entry_add(self.dev_tgt, key_list, data_list); + print("Add vlan_port: ingress_port{} --> get_sid_vid({},{})".format( + port_1, superid, subid_1)) + print("Add vlan_port: ingress_port{} --> get_sid_vid({},{})".format( + port_2, superid, subid_2)) + + # + # Program an entry in smac: subid, mac_src --> smac_hit(ingress_port, is_static) + # + key_list = [ + self.smac.make_key([gc.KeyTuple('meta.vid', subid_1), gc.KeyTuple('hdr.ethernet.src_addr', mac_src)]), + self.smac.make_key([gc.KeyTuple('meta.vid', subid_2), gc.KeyTuple('hdr.ethernet.src_addr', mac_dst)]) + ] + + data_list = [ + self.smac.make_data([gc.DataTuple('port', port_1), gc.DataTuple('is_static',is_static)],"Ingress.smac_hit"), + self.smac.make_data([gc.DataTuple('port', port_2), gc.DataTuple('is_static',is_static)],"Ingress.smac_hit") + ] + + self.smac.entry_add(self.dev_tgt, key_list, data_list); + print("Add smac: subid:{}, mac_src:{} --> get_sid_vid({},{})".format( + subid_1, mac_src, port_1, is_static)) + print("Add smac: subid:{}, mac_src:{} --> get_sid_vid({},{})".format( + subid_2, mac_dst, port_2, is_static)) + + # + # Program an entry in subvlan_check: meta.dst_ipv4 --> subvlan_send(subid, port) + # + key_list = [ + self.subvlan_check.make_key([gc.KeyTuple('meta.dst_ipv4', ipv4_dst),gc.KeyTuple('meta.vid', subid_2)]) + ] + + data_list = [ + self.subvlan_check.make_data([gc.DataTuple('port',port_2)],"Ingress.subvlan_send") + ] + self.subvlan_check.entry_add(self.dev_tgt, key_list, data_list); + print("Add subvlan_check: meta.dst_ipv4:{} --> subvlan_send({},{})".format( + ipv4_dst, subid_2, port_2)) + + # + # Program an entry in ipv4_host: ipv4_dst --> set_nexthop(nexthop_id) + # + key = self.ipv4_host.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', ipv4_dst)]) + + data = self.ipv4_host.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + + self.ipv4_host.entry_add(self.dev_tgt, [key], [data]) + print(" Added an entry to ipv4_host: {} --> set_nexthop({})". + format(ipv4_dst, nexthop_id)) + + # + # Program an entry in nexthop: nexthop_id --> send(egress_port) + # + key = self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + + data = self.nexthop.make_data([ + gc.DataTuple('port', port_2)], "Ingress.send") + + self.nexthop.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to nexthop: {} --> send({})".format( + nexthop_id, port_2)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, port_1)) + pkt = simple_tcp_packet(eth_dst="00:00:03:00:00:03", + eth_src='00:00:01:00:00:01', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, port_1, pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the packet to be forwarded to port {}" + .format(port_2)) + verify_packet(self, pkt, port_2) + print(" Packet received of port {}".format(port_2)) + + +class DiffSuperVLANSend(BaseProgramTest): + def runTest(self): + #vlan_port->smac->smac_results->dmac->ipv4_host->nexthop + #the entries in smac_results is static + ingress_port = test_param_get("ingress_port", 0) + superid = test_param_get('superid', 1) + subid = test_param_get('subid', 1) + mac_src = test_param_get("mac_src", "00:00:01:00:00:01") #192.168.10.1's mac + is_static = test_param_get('is_static', 1) + mac_dst = test_param_get("mac_dst", "01:02:03:04:05:06") #L3 gateway mac + is_to_l3 = test_param_get('is_to_l3', 1) + ipv4_dst = test_param_get("ipv4_dst", "192.168.3.8") + egress_port = test_param_get("egress_port", 4) + nexthop_id = test_param_get("nexthop_id", 100) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in vlan_port: vlan_port --> get_sid_vid(superid, subid) + # + key = self.vlan_port.make_key([ + gc.KeyTuple('ig_intr_md.ingress_port', ingress_port)]) + + data = self.vlan_port.make_data( + [gc.DataTuple('superid',superid), + gc.DataTuple('subid',subid)], + "Ingress.get_sid_vid") + + self.vlan_port.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to vlan_port: ingress_port{} --> get_sid_vid({},{})".format( + ingress_port, superid, subid)) + + # + # Program an entry in smac: subid, mac_src --> smac_hit(ingress_port, is_static) + # + key = self.smac.make_key([ + gc.KeyTuple('meta.vid', subid), + gc.KeyTuple('hdr.ethernet.src_addr', mac_src)]) + + data = self.smac.make_data( + [gc.DataTuple('port', ingress_port), gc.DataTuple('is_static',is_static)],"Ingress.smac_hit") + + self.smac.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry smac: meta.vid:{} hdr.ethernet.src_addr{} --> smac_hit({},{})".format( + subid, mac_src, ingress_port, is_static)) + + # + # Program an entry in dmac: subid, mac_dst --> change_to_l3(to_l3) + # + key = self.dmac.make_key([ + gc.KeyTuple('meta.vid', subid), + gc.KeyTuple('hdr.ethernet.dst_addr', mac_dst)]) + + data = self.dmac.make_data([gc.DataTuple('to_l3', is_to_l3)], "Ingress.change_to_l3") + + self.dmac.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry smac: meta.vid:{} hdr.ethernet.dst_addr{} --> change_to_l3({})".format( + subid, mac_dst,is_static)) + + # + # Program an entry in ipv4_host: ipv4_dst --> set_nexthop(nexthop_id) + # + key = self.ipv4_host.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', ipv4_dst)]) + + data = self.ipv4_host.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + + self.ipv4_host.entry_add(self.dev_tgt, [key], [data]) + print(" Added an entry to ipv4_host: {} --> set_nexthop({})". + format(ipv4_dst, nexthop_id)) + + # + # Program an entry in nexthop: nexthop_id --> send(egress_port) + # + key = self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + + data = self.nexthop.make_data([ + gc.DataTuple('port', egress_port)], "Ingress.send") + + self.nexthop.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to nexthop: {} --> send({})".format( + nexthop_id, egress_port)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="01:02:03:04:05:06", + eth_src='00:00:01:00:00:01', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the packet to be forwarded to port {}" + .format(egress_port)) + verify_packet(self, pkt, egress_port) + print(" Packet received of port {}".format(egress_port)) + +class HostSend(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + egress_port = test_param_get("egress_port", 4) + nexthop_id = test_param_get("nexthop_id", 100) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: nexthop_id --> send(egress_port) + # + key = self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + + data = self.nexthop.make_data([ + gc.DataTuple('port', egress_port)], "Ingress.send") + + self.nexthop.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to nexthop: {} --> send({})".format( + nexthop_id, egress_port)) + + # + # Program an entry in ipv4_host: ipv4_dst --> set_nexthop(nexthop_id) + # + key = self.ipv4_host.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', ipv4_dst)]) + + data = self.ipv4_host.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + + self.ipv4_host.entry_add(self.dev_tgt, [key], [data]) + print(" Added an entry to ipv4_host: {} --> set_nexthop({})". + format(ipv4_dst, nexthop_id)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the packet to be forwarded to port {}" + .format(egress_port)) + verify_packet(self, pkt, egress_port) + print(" Packet received of port {}".format(egress_port)) + + +class HostDrop(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + nexthop_id = test_param_get("nexthop_id", 100) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: nexthop_id --> drop() + # + key_list = [ + self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + ] + + data_list = [ + self.nexthop.make_data([], "Ingress.drop") + ] + + self.nexthop.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to nexthop: {} --> drop()". + format(nexthop_id)) + + # + # Program an entry in ipv4_host: ipv4_dst --> set_nexthop(nexthop_id) + # + key_list = [ + self.ipv4_host.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', ipv4_dst)]) + ] + + data_list = [ + self.ipv4_host.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + ] + + self.ipv4_host.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to ipv4_host: {} --> drop()".format( + ipv4_dst)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + print(" Expecting No packets anywhere") + + # + # Wait to make sure no packet egresses anywhere. self.swports is the + # list of all ports the test is using. It is set up in the setUp() + # method of the parent class + # + verify_no_packet_any(self, pkt, self.swports) + print(" No packets received") + +class HostL3Switch(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + new_mac_da = test_param_get("new_mac_da", "00:12:34:56:78:9A") + new_mac_sa = test_param_get("new_mac_sa", "00:00:AA:BB:CC:DD") + egress_port = test_param_get("egress_port", 4) + nexthop_id = test_param_get("nexthop_id", 100) + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: + # nexthop_id --> l3_switch(egress_port. new_mac_da, new_mac_da) + # + key = self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + + data = self.nexthop.make_data([ + gc.DataTuple('port', egress_port), + gc.DataTuple('new_mac_da', new_mac_da), + gc.DataTuple('new_mac_sa', new_mac_sa)], + "Ingress.l3_switch") + + self.nexthop.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to nexthop: {} --> l3_switch({}, {}, {})". + format(nexthop_id, egress_port, new_mac_da, new_mac_sa)) + + # + # Program an entry in IPv4: ipv4_dst --> set_nexthop(nexthop_id) + # + key = self.ipv4_host.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', ipv4_dst)]) + + data = self.ipv4_host.make_data([ + gc.DataTuple('id', nexthop_id)], + "Ingress.set_nexthop") + + self.ipv4_host.entry_add(self.dev_tgt, [key], [data]); + print(" Added an entry to ipv4_host: {} --> set_nexthop({})". + format(ipv4_dst, nexthop_id)) + + # + # Prepare the test packet and the expected packet + send_pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + + exp_pkt = copy.deepcopy(send_pkt) + exp_pkt[Ether].dst = new_mac_da + exp_pkt[Ether].src = new_mac_sa + exp_pkt[IP].ttl -= 1 + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + + send_packet(self, ingress_port, send_pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the modified packet to be forwarded to port {}" + .format(egress_port)) + + verify_packet(self, exp_pkt, egress_port) + print(" Modified Packet received of port {}" + .format(egress_port)) + +class LpmSend(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + lpm_addr = test_param_get("lpm_addr", "192.168.1.0/24") + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + egress_port = test_param_get("egress_port", 4) + nexthop_id = test_param_get("nexthop_id", 100) + + (lpm_ipv4, lpm_p_len) = lpm_addr.split("/") + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: nexthop_id --> send(egress_port) + # + key_list = [ + self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + ] + + data_list = [ + self.nexthop.make_data([ + gc.DataTuple('port', egress_port)], "Ingress.send") + ] + + self.nexthop.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to nexthop: {} --> send({})".format( + nexthop_id, egress_port)) + + # + # Program an entry in ipv4_lpm: + # lpm_addr/prefix --> set_nexthop(nexthop_id) + # Note the extra named argument (prefix_len) to gc.KeyTuple(). + # + key_list = [ + self.ipv4_lpm.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', + value=lpm_ipv4, prefix_len=int(lpm_p_len))]) + ] + + data_list = [ + self.ipv4_lpm.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + ] + + self.ipv4_lpm.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to ipv4_lpm: {}/{} --> set_nexthop({})".format( + lpm_ipv4, lpm_p_len, nexthop_id)) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}". + format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + + # + # Wait for the egress packet and verify it. Since we do not expect + # any modifications, we use the same pkt. + # + print(" Expecting the packet to be forwarded to port {}". + format(egress_port)) + + verify_packet(self, pkt, egress_port) + print(" Packet received on port {}".format(egress_port)) + +class LpmDrop(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + lpm_addr = test_param_get("lpm_addr", "192.168.1.0/24") + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + nexthop_id = test_param_get("nexthop_id", 100) + + (lpm_ipv4, lpm_p_len) = lpm_addr.split("/") + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: nexthop_id --> drop() + # + key_list = [ + self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + ] + + data_list = [ + self.nexthop.make_data([], "Ingress.drop") + ] + + self.nexthop.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to nexthop: {} --> drop()". + format(nexthop_id)) + + # + # Program an entry in ipv4_lpm: + # lpm_addr/prefix --> set_nexthop(nexthop_id) + # Note the extra named argument (prefix_len) to gc.KeyTuple(). + # + key_list = [ + self.ipv4_lpm.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', + value=lpm_ipv4, prefix_len=int(lpm_p_len))]) + ] + + data_list = [ + self.ipv4_lpm.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + ] + + self.ipv4_lpm.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to ipv4_lpm: {}/{} --> set_nexthop({})".format( + lpm_ipv4, lpm_p_len, nexthop_id)) + + # + # Send the Test Packet + # + print(" Sending packet with IPv4 DST ADDR{} into port {}". + format(ipv4_dst, ingress_port)) + + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + + print(" Expecting No packets anywhere") + verify_no_packet_any(self, pkt, self.swports) + print(" No packets received") + +class LpmL3Switch(BaseProgramTest): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + lpm_addr = test_param_get("lpm_addr", "192.168.1.0/24") + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + new_mac_da = test_param_get("new_mac_da", "00:12:34:56:78:9A") + new_mac_sa = test_param_get("new_mac_sa", "00:00:AA:BB:CC:DD") + egress_port = test_param_get("egress_port", 4) + nexthop_id = test_param_get("nexthop_id", 100) + + (lpm_ipv4, lpm_p_len) = lpm_addr.split("/") + + print("\n") + print("Test Run") + print("========") + + # + # Program an entry in nexthop: + # nexthop_id --> l3_switch(egress_port. new_mac_da, new_mac_da) + # + key_list = [ + self.nexthop.make_key([ + gc.KeyTuple('nexthop_id', nexthop_id)]) + ] + + data_list = [ + self.nexthop.make_data([gc.DataTuple('port', egress_port), + gc.DataTuple('new_mac_da', new_mac_da), + gc.DataTuple('new_mac_sa', new_mac_sa)], + "Ingress.l3_switch") + ] + + self.nexthop.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to nexthop: {} --> send({})". + format(nexthop_id, egress_port)) + + # + # Program an entry in ipv4_lpm: + # lpm_addr/prefix --> set_nexthop(nexthop_id) + # Note the extra named argument (prefix_len) to gc.KeyTuple(). + # + key_list = [ + self.ipv4_lpm.make_key([ + gc.KeyTuple('hdr.ipv4.dst_addr', + value=lpm_ipv4, prefix_len=int(lpm_p_len))]) + ] + + data_list = [ + self.ipv4_lpm.make_data([ + gc.DataTuple('id', nexthop_id)], "Ingress.set_nexthop") + ] + + self.ipv4_lpm.entry_add(self.dev_tgt, key_list, data_list); + print(" Added an entry to ipv4_lpm: {}/{} --> set_nexthop({})".format( + lpm_ipv4, lpm_p_len, nexthop_id)) + # + # Prepare the test packet and the expected packet + # + send_pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + + exp_pkt = copy.deepcopy(send_pkt) + exp_pkt[Ether].dst = new_mac_da + exp_pkt[Ether].src = new_mac_sa + exp_pkt[IP].ttl -= 1 + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}". + format(ipv4_dst, ingress_port)) + + send_packet(self, ingress_port, send_pkt) + + # + # Wait for the egress packet and verify it + # + print(" Expecting the modified packet to be forwarded to port {}". + format(egress_port)) + verify_packet(self, exp_pkt, egress_port) + print(" Modified Packet received of port {}".format(egress_port)) + +######################################################################## +######## Running Multiple Tests with the same setup ############### +######################################################################## + +# This new Base Class extends the setup method of Simple L3 by adding the +# desired network setup +class TestGroup1(BaseProgramTest): + + # + # This is a simple function that takes a list of entries and programs + # them in a specified table + # + # Each entry is a tuple, consisting of 3 elements: + # key -- a list of tuples for each element of the key + # action_name -- the action to use. Must use full name of the action + # data -- a list (may be empty) of the tuples for each action + # parameter + # + def programTable(self, table, entries, target=None): + if target is None: + target = self.dev_tgt + + key_list=[] + data_list=[] + for k, a, d in entries: + key_list.append(table.make_key([gc.KeyTuple(*f) for f in k])) + data_list.append(table.make_data([gc.DataTuple(*p) for p in d], a)) + table.entry_add(target, key_list, data_list) + + def setUp(self): + BaseProgramTest.setUp(self) + + print("\n") + print("Table Setup") + print("===========") + + # Nexthop programming + print(" nexthop") + self.programTable(self.nexthop, [ + ([("nexthop_id", 0)], "Ingress.send", [("port", 64)]), + ([("nexthop_id", 1)], "Ingress.drop", []), + ([("nexthop_id", 101)], "Ingress.l3_switch", [ + ("port", 1), + ("new_mac_da", "00:00:01:00:00:01"), + ("new_mac_sa", "00:00:FF:00:00:FE")]), + ([("nexthop_id", 102)], "Ingress.l3_switch", [ + ("port", 2), + ("new_mac_da", "00:00:02:00:00:01"), + ("new_mac_sa", "00:00:FF:00:00:FE")]), + ([("nexthop_id", 103)], "Ingress.l3_switch", [ + ("port", 4), + ("new_mac_da", "FF:FF:FF:FF:FF:FF"), + ("new_mac_sa", "00:12:34:56:78:9A")]) + ]) + + # ipv4_host programming + print(" ipv4_host") + self.programTable(self.ipv4_host, [ + ([("hdr.ipv4.dst_addr", "192.168.1.1")], + "Ingress.set_nexthop", [("id", 101)]), + ([("hdr.ipv4.dst_addr", "192.168.1.2")], + "Ingress.set_nexthop", [("id", 102)]), + ([("hdr.ipv4.dst_addr", "192.168.1.3")], + "Ingress.set_nexthop", [("id", 1)]), + ([("hdr.ipv4.dst_addr", "192.168.1.254")], + "Ingress.set_nexthop", [("id", 0)]), + ]) + + # ipv4_lpm programming + print(" ipv4_lpm") + self.programTable(self.ipv4_lpm, [ + ([("hdr.ipv4.dst_addr", "192.168.1.0", None, 24)], + "Ingress.set_nexthop", [("id", 0)]), + ([("hdr.ipv4.dst_addr", "192.168.3.0", None, 24)], + "Ingress.set_nexthop", [("id", 101)]), + ([("hdr.ipv4.dst_addr", "192.168.5.0", None, 24)], + "Ingress.set_nexthop", [("id", 101)]), + ([("hdr.ipv4.dst_addr", "192.168.7.0", None, 24)], + "Ingress.set_nexthop", [("id", 101)]), + ([("hdr.ipv4.dst_addr", "192.168.0.0", None, 16)], + "Ingress.set_nexthop", [("id", 1)]), + ([("hdr.ipv4.dst_addr", "0.0.0.0", None, 0)], + "Ingress.set_nexthop", [("id", 0)]), + ]) + +# +# The following are multiple tests that all use the same setup +# +# There are a lot of tests that can be run on this topology. Feel free to +# add more +# + +class BadTTL(TestGroup1): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + ttl = test_param_get("ttl", 1) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=ttl, + ip_ihl=5) + send_packet(self, ingress_port, pkt) + print(" Expecting No packets anywhere") + + # + # Wait to make sure no packet egresses anywhere. self.swports is the + # list of all ports the test is using. It is set up in the setUp() + # method of the parent class + # + verify_no_packet_any(self, pkt, self.swports) + print(" No packets received") + +class BadChecksum(TestGroup1): + def runTest(self): + ingress_port = test_param_get("ingress_port", 0) + ipv4_dst = test_param_get("ipv4_dst", "192.168.1.1") + chksum = test_param_get("chksum", 123) + + # + # Send a test packet + # + print(" Sending packet with IPv4 DST ADDR={} into port {}" + .format(ipv4_dst, ingress_port)) + pkt = simple_tcp_packet(eth_dst="00:98:76:54:32:10", + eth_src='00:55:55:55:55:55', + ip_dst=ipv4_dst, + ip_id=101, + ip_ttl=64, + ip_ihl=5) + # Corrupt checksum + pkt[IP].chksum = chksum + + send_packet(self, ingress_port, pkt) + print(" Expecting No packets anywhere") + + # + # Wait to make sure no packet egresses anywhere. self.swports is the + # list of all ports the test is using. It is set up in the setUp() + # method of the parent class + # + verify_no_packet_any(self, pkt, self.swports) + print(" No packets received") |
