diff --git a/netpfga/minip4/src/minip4_solution-nat64.p4 b/netpfga/minip4/src/minip4_solution-nat64.p4 new file mode 100644 index 0000000..1864c88 --- /dev/null +++ b/netpfga/minip4/src/minip4_solution-nat64.p4 @@ -0,0 +1,636 @@ +#include +#include +#include "headers.p4" + +/******************************************************************************** + * Header + */ + +typedef bit<48> EthAddr_t; +header Ethernet_h { + EthAddr_t dstAddr; + EthAddr_t srcAddr; + bit<16> etherType; +} + +struct Parsed_packet { + Ethernet_h ethernet; +} + +// user defined metadata: can be used to share information between +// TopParser, TopPipe, and TopDeparser +struct user_metadata_t { + bit<8> unused; +} + +// digest_data, MUST be 256 bits -- what is this used for? +struct digest_data_t { + bit<256> unused; +} + + +/******************************************************************************** + * Parser + */ + +@Xilinx_MaxPacketRegion(1024) +parser TopParser(packet_in b, + out Parsed_packet p, + out user_metadata_t user_metadata, + out digest_data_t digest_data, + inout sume_metadata_t sume_metadata) { + state start { + b.extract(p.ethernet); + user_metadata.unused = 0; + digest_data.unused = 0; + + transition accept; + } +} + +/******************************************************************************** + * Main + */ +control TopPipe(inout Parsed_packet p, + inout user_metadata_t user_metadata, + inout digest_data_t digest_data, + inout sume_metadata_t sume_metadata) { + + + action drop() { + mark_to_drop(); + } + + action set_egress_port (port_t out_port) { + standard_metadata.egress_spec = out_port; + } + + action set_egress_port_and_mac (port_t out_port, mac_addr_t mac_addr) { + hdr.ethernet.dst_addr = mac_addr; + standard_metadata.egress_spec = out_port; + } + + action controller_reply(task_t task) { + meta.task = task; + meta.ingress_port = standard_metadata.ingress_port; + clone3(CloneType.I2E, 100, meta); + } + + action controller_debug_table_id(table_t table_id) { + meta.table_id = table_id; + controller_reply(TASK_DEBUG); + } + + action controller_debug() { + controller_reply(TASK_DEBUG); + } + + action multicast_pkg(mcast_t mcast_grp) { /* Output PKG on correct ports (plural) */ + standard_metadata.mcast_grp = mcast_grp; + } + + + /********************** NAT64 / NAT46 ACTIONS GENERIC ***********************************/ + + /* changes for icmp6 -> icmp */ + action nat64_icmp6_generic() + { + hdr.icmp.setValid(); + hdr.ipv4.protocol = PROTO_ICMP; // overwrite generic same protocol assumption + + /* trigger checksumming */ + meta.switch_task = TASK_CHECKSUM_ICMP; + + meta.chk_icmp = 1; + + hdr.icmp6.setInvalid(); + + /* not needed, as we don't translate them (yet/ever) */ + hdr.icmp6_na_ns.setInvalid(); + hdr.icmp6_option_link_layer_addr.setInvalid(); + } + + /* NAT64 protocol unspecific changes */ + action nat64_generic(ipv4_addr_t src, ipv4_addr_t dst) { + hdr.ipv4.setValid(); + meta.chk_ipv4 = 1; /* need to calculate the hdrchecksum */ + + /* Stuff that might need to be fixed */ + hdr.ipv4.version = (bit<4>)4; + hdr.ipv4.diff_serv = (bit<6>)0; // no ToS + hdr.ipv4.ecn = (bit<2>)0; // unsupported + + /* 5 is ok as long as we don't use options / padding / + anything after the destination address */ + hdr.ipv4.ihl = (bit<4>) 5; // internet header length: 4*5 = 20 + hdr.ipv4.totalLen = (bit<16>) hdr.ipv6.payload_length + 20; // ok under above constraints + + hdr.ipv4.identification = (bit<16>) 0; // no support for fragments + hdr.ipv4.flags = (bit<3>) 0; // DF bit and more fragments + hdr.ipv4.fragOffset = (bit<13>) 0; // 0 as there are no fragments + + /* Stuff that should be fine */ + hdr.ethernet.ethertype = TYPE_IPV4; + + hdr.ipv4.dst_addr = dst; + hdr.ipv4.src_addr = src; + + hdr.ipv4.ttl = hdr.ipv6.hop_limit; + + hdr.ipv4.protocol = hdr.ipv6.next_header; + + hdr.ipv6.setInvalid(); + } + + /* nat64_prefix is the same as the matching key, but without the mask */ + action nat64_static(ipv6_addr_t v6_src, ipv4_addr_t v4_dst, ipv6_addr_t nat64_prefix) { + ipv6_addr_t src_offset = hdr.ipv6.src_addr - v6_src; + ipv4_addr_t src = v4_dst + (ipv4_addr_t) src_offset; + + ipv4_addr_t dst = (ipv4_addr_t) (hdr.ipv6.dst_addr - nat64_prefix); + + nat64_generic(src, dst); + } + + /* From https://tools.ietf.org/html/rfc792 (IPv4) + +Echo or Echo Reply Message + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- + + + From https://tools.ietf.org/html/rfc4443#section-4.1 + +4.1. Echo Request Message + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- + +4.2. Echo Reply Message + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- + + + Type / code are different in ICMP4 and ICMP6! + + */ + + + /* changes for icmp6 -> icmp */ + action nat46_icmp_generic() + { + hdr.icmp6.setValid(); + hdr.ipv6.next_header = PROTO_ICMP6; + + meta.chk_icmp6 = 1; + meta.cast_length = (bit<32>) hdr.ipv6.payload_length; + + hdr.icmp.setInvalid(); + } + + /* NAT46: protocol unspecific changes */ + action nat46_generic(ipv6_addr_t src, ipv6_addr_t dst) { + hdr.ipv6.setValid(); + hdr.ipv4.setInvalid(); + + hdr.ethernet.ethertype = TYPE_IPV6; + + hdr.ipv6.dst_addr = dst; + hdr.ipv6.src_addr = src; + + hdr.ipv6.version = (bit<4>)6; + hdr.ipv6.traffic_class = (bit<8>) hdr.ipv4.diff_serv; + hdr.ipv6.flow_label = (bit<20>) 0; + hdr.ipv6.payload_length = (bit<16>) hdr.ipv4.totalLen - 20; + + hdr.ipv6.next_header = hdr.ipv4.protocol; + hdr.ipv6.hop_limit = hdr.ipv4.ttl; + + } + + + /* matching key: v4_network specified again */ + action nat46_static(ipv6_addr_t v6_src, ipv4_addr_t v4_dst, ipv6_addr_t nat64_prefix) { + ipv6_addr_t src = nat64_prefix + (ipv6_addr_t) hdr.ipv4.src_addr; + + ipv4_addr_t dst_offset = hdr.ipv4.dst_addr - v4_dst; + ipv6_addr_t dst = v6_src + (ipv6_addr_t) dst_offset; + + nat46_generic(src, dst); + } + + table nat64 { + key = { + hdr.ipv6.dst_addr: lpm; + } + actions = { + controller_debug; + nat64_static; + controller_debug_table_id; + NoAction; + } + size = NAT64_TABLE_SIZE; +// default_action = controller_debug_table_id(TABLE_NAT64); + default_action = NoAction; + } + + table nat46 { + key = { + hdr.ipv4.dst_addr: lpm; + } + actions = { + controller_debug; + nat46_static; + controller_debug_table_id; + NoAction; + } + size = NAT64_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_NAT46); + } + + /********************** NAT64 sessions ***********************************/ + + /* automatic translations */ + action nat64_tcp_session_translate( + ipv4_addr_t src_addr, + bit<16> src_port, + ipv4_addr_t dst_addr, + bit<16> dst_port) + { + hdr.ipv4.setValid(); + + hdr.tcp.src_port = src_port; + hdr.tcp.dst_port = dst_port; + + nat64_generic(src_addr, dst_addr); + } + + action nat46_tcp_session_translate( + ipv6_addr_t src_addr, + bit<16> src_port, + ipv6_addr_t dst_addr, + bit<16> dst_port) + { + hdr.ipv6.setValid(); + + hdr.tcp.src_port = src_port; + hdr.tcp.dst_port = dst_port; + + nat46_generic(src_addr, dst_addr); + } + + /* We are in the right range, need to create a session entry */ + action nat64_tcp_session_create() + { + controller_reply(TASK_NAT64_TCP_SESSION); + } + + + /* Used for detecting traffic that should have a session */ + table nat64_session { + key = { + hdr.ipv6.dst_addr: lpm; + } + actions = { + controller_debug_table_id; + NoAction; + } + size = NAT64_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_NAT64_SESSION); + } + + + table nat64_tcp_session { + key = { + hdr.ipv6.src_addr: exact; + hdr.ipv6.dst_addr: exact; + hdr.tcp.src_port: exact; + hdr.tcp.dst_port: exact; + } + actions = { + controller_debug_table_id; + nat64_tcp_session_create; + nat64_tcp_session_translate; + NoAction; + } + size = NAT64_TABLE_SIZE; + default_action = nat64_tcp_session_create; + } + + table nat46_tcp_session { + key = { + hdr.ipv6.src_addr: exact; + hdr.ipv6.dst_addr: exact; + hdr.tcp.src_port: exact; + hdr.tcp.dst_port: exact; + } + actions = { + controller_debug_table_id; + nat46_tcp_session_translate; + NoAction; + } + size = NAT64_TABLE_SIZE; + //default_action = controller_debug_table_id(TABLE_NAT64_TCP); + default_action = NoAction; + } + + + + /********************** ICMP6 + NDP + ICMP ***********************************/ + + /* old/unused action -- is it??*/ + action icmp6_answer() { + if(hdr.icmp6.isValid()) { + if(hdr.icmp6.code == ICMP6_ECHO_REQUEST) { + ipv6_addr_t tmp = hdr.ipv6.src_addr; + hdr.ipv6.src_addr = hdr.ipv6.dst_addr; + hdr.ipv6.dst_addr = tmp; + hdr.icmp6.code = ICMP6_ECHO_REPLY; + } + } + } + + action icmp6_neighbor_solicitation(ipv6_addr_t addr, mac_addr_t mac_addr) { + /* egress = ingress */ + standard_metadata.egress_spec = standard_metadata.ingress_port; + + /* 1. IPv6 changes */ + hdr.ipv6.dst_addr = hdr.ipv6.src_addr; + hdr.ipv6.src_addr = addr; + + /* 2. ICMP6 changes */ + hdr.icmp6.type = ICMP6_NA; + hdr.icmp6.code = 0; + hdr.icmp6.checksum = 42; // checksum is calculated in deparser - marking with 42 to see whether it is calculated + + /* 3. icmp6/neighbor advertisement: values taken from real world answers */ + hdr.icmp6_na_ns.router = 0; + hdr.icmp6_na_ns.solicitated = 1; + hdr.icmp6_na_ns.override = 1; + hdr.icmp6_na_ns.reserved = 0; + hdr.icmp6_na_ns.target_addr = addr; + + /* 4. Link layer options */ + hdr.icmp6_option_link_layer_addr.type = ICMP6_NDP_OPT_TARGET_LL; + hdr.icmp6_option_link_layer_addr.ll_length = 1; /* 1* 64 bit */ + hdr.icmp6_option_link_layer_addr.mac_addr = mac_addr; + + /* 5. Checksum trigger/info */ + meta.chk_icmp6_na_ns = 1; + meta.cast_length = (bit<32>) hdr.ipv6.payload_length; + } + + action icmp6_echo_reply() { + mac_addr_t mac_tmp = hdr.ethernet.dst_addr; + hdr.ethernet.dst_addr = hdr.ethernet.src_addr; + hdr.ethernet.src_addr = mac_tmp; + + ipv6_addr_t addr_tmp = hdr.ipv6.dst_addr; + hdr.ipv6.dst_addr = hdr.ipv6.src_addr; + hdr.ipv6.src_addr = addr_tmp; + + hdr.icmp6.type = ICMP6_ECHO_REPLY; + + meta.chk_icmp6 = 1; + + meta.cast_length = (bit<32>) hdr.ipv6.payload_length; + } + + table icmp6 { + key = { + hdr.ipv6.dst_addr: lpm; + hdr.icmp6.type: exact; + } + actions = { + controller_debug; + icmp6_neighbor_solicitation; + icmp6_echo_reply; + controller_debug_table_id; + NoAction; + } + size = ICMP6_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_ICMP6); + } + + action icmp_echo_reply() { + mac_addr_t mac_tmp = hdr.ethernet.dst_addr; + ipv4_addr_t ipv4_tmp = hdr.ipv4.src_addr; + + /* swap ethernet addresses */ + hdr.ethernet.dst_addr = hdr.ethernet.src_addr; + hdr.ethernet.src_addr = mac_tmp; + + /* swap ipv4 addresses */ + hdr.ipv4.src_addr = hdr.ipv4.dst_addr; + hdr.ipv4.dst_addr = ipv4_tmp; + + /* set correct type */ + hdr.icmp.type = ICMP_ECHO_REPLY; + + meta.chk_icmp = 1; + } + + table icmp { + key = { + hdr.ipv4.dst_addr: lpm; + hdr.icmp.type: exact; + } + actions = { + icmp_echo_reply; + controller_debug_table_id; + NoAction; + } + size = ICMP_TABLE_SIZE; +// default_action = controller_debug_table_id(TABLE_ICMP); + default_action = NoAction; /* do not clone on miss */ + } + + /********************** ARP ***********************************/ + + action arp_reply(mac_addr_t mac_addr) { + /* swap */ + ipv4_addr_t ipv4_src = hdr.arp.dst_ipv4_addr; + ipv4_addr_t ipv4_dst = hdr.arp.src_ipv4_addr; + + /* swap/add */ + mac_addr_t mac_src = mac_addr; + mac_addr_t mac_dst = hdr.arp.src_mac_addr; + + /* fill the ethernet header */ + hdr.ethernet.dst_addr = mac_dst; + hdr.ethernet.src_addr = mac_src; + + /* fill the arp header */ + hdr.arp.dst_mac_addr = mac_dst; + hdr.arp.src_mac_addr = mac_src; + + /* swapping */ + hdr.arp.dst_ipv4_addr = ipv4_dst; + hdr.arp.src_ipv4_addr = ipv4_src; + + hdr.arp.opcode = ARP_REPLY; + } + + table v4_arp { + key = { + hdr.ethernet.dst_addr: exact; + hdr.arp.opcode: exact; + hdr.arp.dst_ipv4_addr: lpm; + } + actions = { + controller_debug_table_id; + arp_reply; + NoAction; + } + size = ICMP6_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_ARP); + } + + table v4_arp_egress { + key = { + hdr.arp.dst_ipv4_addr: lpm; + } + actions = { + controller_debug_table_id; + set_egress_port; + NoAction; + } + size = ICMP6_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_ARP_EGRESS); + } + + + /********************** ROUTING (egress definiton) TABLES ***********************************/ + + table v6_networks { + key = { + hdr.ipv6.dst_addr: lpm; + } + actions = { + set_egress_port; + set_egress_port_and_mac; + controller_debug; + controller_reply; + controller_debug_table_id; + NoAction; + } + size = ROUTING_TABLE_SIZE; + + default_action = controller_debug_table_id(TABLE_V6_NETWORKS); + } + + table v4_networks { + key = { + hdr.ipv4.dst_addr: lpm; + } + actions = { + set_egress_port; + set_egress_port_and_mac; + controller_debug; + controller_debug_table_id; + NoAction; + } + size = ROUTING_TABLE_SIZE; + default_action = controller_debug_table_id(TABLE_V4_NETWORKS); + } + + + + action swap_eth_addresses() { + EthAddr_t temp = p.ethernet.dstAddr; + p.ethernet.dstAddr = p.ethernet.srcAddr; + p.ethernet.srcAddr = temp; + + /* set egress port */ + sume_metadata.dst_port = sume_metadata.src_port; + } + + action send_to_port1() { + sume_metadata.dst_port = 1; + } + + action send_to_all_ports() { + /* Taken from commands.txt of the "int" project: + table_cam_add_entry forward set_output_port 0xffffffffffff => 0b01010101 + + python convert: + >>> 0b01010101 + 85 + + */ + sume_metadata.dst_port = 85; + } + + action do_nothing() { + EthAddr_t temp = p.ethernet.dstAddr; + } + + table lookup_table { + key = { + p.ethernet.dstAddr: exact; + } + + actions = { + swap_eth_addresses; + do_nothing; + send_to_port1; + send_to_all_ports; + } + size = 64; +// default_action = swap_eth_addresses; // test_mirror(): in gen_testdata.py + default_action = send_to_port1; // test_port1() +// default_action = send_to_all_ports; // test_allports(): + } + + apply { + lookup_table.apply(); + } +} + +/******************************************************************************** + * Deparser + */ + +@Xilinx_MaxPacketRegion(1024) +control TopDeparser(packet_out b, + in Parsed_packet p, + in user_metadata_t user_metadata, + inout digest_data_t digest_data, + inout sume_metadata_t sume_metadata) { + apply { + b.emit(p.ethernet); + } +} + +/******************************************************************************** + * Switch + */ + +SimpleSumeSwitch( + TopParser(), + TopPipe(), + TopDeparser() +) main;