From a408d7a8036e532bd414e92003119b621c296d3c Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sat, 23 Mar 2019 16:20:13 +0100 Subject: [PATCH] + reorg +add simple nat64 w/o protocol specific translations --- doc/plan.org | 103 ++++++++++++++++++++++------------------ p4app/controller.py | 57 ++++++++++++---------- p4src/static-mapping.p4 | 57 ++++++++++++++++------ 3 files changed, 130 insertions(+), 87 deletions(-) diff --git a/doc/plan.org b/doc/plan.org index 6eebd90..b5c81b4 100644 --- a/doc/plan.org +++ b/doc/plan.org @@ -1161,69 +1161,78 @@ check whether tables are applied correctly (type conversion problems) - start tcpdump - start test program - stop tcpdump - add pcap to git repo - git add-commit-push - git pull - start wireshark - debug packets - analyse code - goto 1 +**** Static NAT64 +Asymmetric maps: v6->v4 can match whole IPv4 Internet (/96) +But v4->v6 can only map sub range! +Using /24s (for convience) in IPv4 + *** Performance comparison -*** Feature/Functionality difference / overview / Challenges / limitations in P4 +*** Feature/Functionality difference / overview / CHALLENGES / LIMITATIONS in P4 **** P4: cannot read key from table +***** log Key and mask for matching destination is in table. We need this -information in the action. However this information is not exposed, so -we need to specify another parameter with the same information as in -the key(s). + information in the action. However this information is not exposed, so + we need to specify another parameter with the same information as in + the key(s). -Log from slack: (2019-03-14) + Log from slack: (2019-03-14) -nico [1:55 PM] -If I use LPM for matching, can I easily get the network address from P4 or do I have to use a bitmask myself? In the latter case it is not exactly clear how to get the mask from the table + nico [1:55 PM] + If I use LPM for matching, can I easily get the network address from P4 or do I have to use a bitmask myself? In the latter case it is not exactly clear how to get the mask from the table -Nate Foster [1:58 PM] -You want to retrieve the address in the packet? In a table? -And do you want to do the retrieving from the data plane or the control plane? (edited) + Nate Foster [1:58 PM] + You want to retrieve the address in the packet? In a table? + And do you want to do the retrieving from the data plane or the control plane? (edited) -nico [2:00 PM] -If I have a match in a table that matches on LPM, it can be any IP address in a network -For calculating the NAT64/NAT46 translation, I will need the base address, i.e. network address to do subtractions/additions -So it is fully data plane, what I would like to do -I'll commit sample code to show the use case more clearly -https://gitlab.ethz.ch/nicosc/master-thesis/blob/master/p4src/static-mapping.p4#L73 -GitLab -p4src/static-mapping.p4 · master · nicosc / master-thesis -gitlab.ethz.ch -So the action nat64_static() is used in the table v6_networks. -In v6_networks I use a match on `hdr.ipv6.dst_addr: lpm;` -What I would like to be able is to get the network address ; I can do that manually, if I have the mask -I can also re-inject this parameter by another action argument, but I'd assume that I can somewhere read this out from the table / match + nico [2:00 PM] + If I have a match in a table that matches on LPM, it can be any IP address in a network + For calculating the NAT64/NAT46 translation, I will need the base address, i.e. network address to do subtractions/additions + So it is fully data plane, what I would like to do + I'll commit sample code to show the use case more clearly + https://gitlab.ethz.ch/nicosc/master-thesis/blob/master/p4src/static-mapping.p4#L73 + GitLab + p4src/static-mapping.p4 · master · nicosc / master-thesis + gitlab.ethz.ch + So the action nat64_static() is used in the table v6_networks. + In v6_networks I use a match on `hdr.ipv6.dst_addr: lpm;` + What I would like to be able is to get the network address ; I can do that manually, if I have the mask + I can also re-inject this parameter by another action argument, but I'd assume that I can somewhere read this out from the table / match -Nate Foster [2:15 PM] -To make sure I understand, in the data plane, you want to retrieve the address in the lpm pattern? (edited) + Nate Foster [2:15 PM] + To make sure I understand, in the data plane, you want to retrieve the address in the lpm pattern? (edited) -nico [2:16 PM] -I want to retrieve the key + nico [2:16 PM] + I want to retrieve the key -Nate Foster [2:16 PM] -Wait. The value `hdr.ipv6.dst_addr` is the thing used in the match. -So you have that. -What you don’t have is the IPv6 address and mask put into the table by the control plane. -I assume you want the latter, right? + Nate Foster [2:16 PM] + Wait. The value `hdr.ipv6.dst_addr` is the thing used in the match. + So you have that. + What you don’t have is the IPv6 address and mask put into the table by the control plane. + I assume you want the latter, right? -nico [2:17 PM] -For example, if my matching key is 2001:db8::/32 and the real address is 2001:db8::f00, then I would like to retrieve 2001:db8:: and 32 from the table -exactly :slightly_smiling_face: -I can "fix" this by adding another argument, but it feels somewhat wrong to do that -Because the table already knows this information + nico [2:17 PM] + For example, if my matching key is 2001:db8::/32 and the real address is 2001:db8::f00, then I would like to retrieve 2001:db8:: and 32 from the table + exactly :slightly_smiling_face: + I can "fix" this by adding another argument, but it feels somewhat wrong to do that + Because the table already knows this information -Nate Foster [2:26 PM] -I can’t think of a way other than the action parameter hack. + Nate Foster [2:26 PM] + I can’t think of a way other than the action parameter hack. -nico [2:26 PM] -Oh, ok -Is it because the information is "lost in hardware"? + nico [2:26 PM] + Oh, ok + Is it because the information is "lost in hardware"? -Nate Foster [2:31 PM] -No you’re right that most implementations have the value in memory. And one can imagine a different table API that allowed one to retrieve it in the data plane. -But unless I am missing something obvious, P4 hides it… + Nate Foster [2:31 PM] + No you’re right that most implementations have the value in memory. And one can imagine a different table API that allowed one to retrieve it in the data plane. + But unless I am missing something obvious, P4 hides it… -**** ICMP6: checksum over payload +***** Result +Need to duplicate information + +**** DONE ICMP6: checksum over payload - variable length, up to 65k -Exists: +Exists! **** Synchronisation with the controller - Double data type definition -> might differ - TYPE_CPU for ethernet diff --git a/p4app/controller.py b/p4app/controller.py index 8ad2593..f062ffd 100755 --- a/p4app/controller.py +++ b/p4app/controller.py @@ -115,16 +115,17 @@ class L2Controller(object): for mode in self.modes: self.nat64_map[mode] = [] - # specific settings -- only need the address (=offset), no mask + # specific settings - mapping 256 IPv6 IPs max statically (based on /24) for mode in ["range_router"]: - for net in self.v6_routes[mode]: - v6_net = net['net'] - v4_net = self.info['v4_nat64_map'].next() + for v6_net in self.v6_routes[mode]: + for v4_net in self.v4_routes[mode]: + v4_dst = self.info['v4_nat64_map'].next() self.nat64_map[mode].append({ - "v6_network": v6_net, - "v4_network": v4_net, - "nat64_prefix": self.info['nat64_prefix'] + "v6_src": v6_net['net'], + "v6_dst": self.info['nat64_prefix'] # static + "v4_src": v4_net['net'], + "v4_dst": v4_dst }) self.init_boilerplate(sw_name) @@ -201,6 +202,10 @@ class L2Controller(object): for v6route in self.v6_routes[self.mode]: self.controller.table_add("v6_networks", "set_egress_port", [str(v6route['net'])], [str(v6route['port'])]) + self.controller.table_clear("v4_networks") + for v4route in self.v4_routes[self.mode]: + self.controller.table_add("v4_networks", "set_egress_port", [str(v4route['net'])], [str(v4route['port'])]) + if self.args.multicast_to_controller: self.listen_to_icmp6_multicast() @@ -209,10 +214,10 @@ class L2Controller(object): self.init_ndp_in_switch(v6addr) self.init_icmp6_echo_in_switch(v6addr) - - self.controller.table_clear("v4_networks") - for v4route in self.v4_routes[self.mode]: - self.controller.table_add("v4_networks", "set_egress_port", [str(v4route['net'])], [str(v4route['port'])]) + self.controller.table_clear("nat64") + self.controller.table_clear("nat46") + for nat64map in self.nat64_map[self.mode]: + self.static_nat64_mapping(**nat64map) # Disable icmp handling in the controller @@ -230,23 +235,25 @@ class L2Controller(object): # # Experimental: controller does NDP, switch does ICMP6 echo reply # self.controller.table_add("v6_addresses", "controller_reply", [str(another_addr_ns)], [str(self.task['ICMP6_NS'])]) + def static_nat64_mapping(self, v6_src, v6_dst, v4_src, v4_dst): + log.info("NAT64 map: ({} -> {} => {}), ({} -> {} -> {} (only /24)))".format( + v6_src, v6_dst, v4_dst, + v4_src, v4_dst, v6_src) - for nat64map in self.nat64_map[self.mode]: - self.static_nat64_mapping(**nat64map) - - def static_nat64_mapping(self, nat64_prefix, v6_network, v4_network): - log.info("NAT64 map: {} -> {} -> {}".format(nat64_prefix, v6_network, v4_network)) - - self.controller.table_add("v6_networks", "nat64_static", [str(nat64_prefix)], - [str(v6_network.network_address), - str(v4_network.network_address), - str(nat64_prefix.network_address)] + self.controller.table_add("nat64", "nat64_static", + [str(v6_src) + str(v6_dst)], + [str(v6_src.network_address), + str(v4_dst.network_address), + str(v6_dst.network_address)] ) - self.controller.table_add("v4_networks", "nat46_static", [str(v4_network)], - [str(v6_network.network_address), - str(v4_network.network_address), - str(nat64_prefix.network_address)] + self.controller.table_add("nat46", "nat46_static", + [str(v4_src) + str(v4_dst)], + [str(v6_src.network_address), + str(v4_dst.network_address), + str(v6_dst.network_address)] ) diff --git a/p4src/static-mapping.p4 b/p4src/static-mapping.p4 index c89252d..5ba3d59 100644 --- a/p4src/static-mapping.p4 +++ b/p4src/static-mapping.p4 @@ -52,7 +52,7 @@ control MyIngress(inout headers hdr, hdr.ipv4.diff_serv = (bit<6>)0; // no ToS hdr.ipv4.ecn = (bit<2>)0; // unsupported - hdr.ipv4.ihl = (bit<4>) 5; // internet header length -- needs to be dynamic! + hdr.ipv4.ihl = (bit<4>) 5; // internet header length -- needs to be dynamic!? hdr.ipv4.totalLen = (bit<16>) hdr.ipv6.payload_length + 5; // should probably also dynamic hdr.ipv4.identification = (bit<16>) 0; // no support for fragments @@ -93,25 +93,58 @@ control MyIngress(inout headers hdr, /* nat64_prefix is the same as the matching key, but without the mask */ - action nat64_static(ipv6_addr_t v6_network, ipv4_addr_t v4_network, ipv6_addr_t nat64_prefix) { - ipv6_addr_t src_offset = hdr.ipv6.src_addr - v6_network; - ipv4_addr_t src = v4_network + (ipv4_addr_t) src_offset; + 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); + + /* fix the protocol specific translations */ + // switch() ... } /* matching key: v4_network specified again */ - action nat46_static(ipv6_addr_t v6_network, ipv4_addr_t v4_network, ipv6_addr_t nat64_prefix ) { + 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_network; - ipv6_addr_t dst = v6_network + (ipv6_addr_t) dst_offset; + 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); + + /* fix the protocol specific translations */ + // switch() ... } + table nat64 { + key = { + hdr.ipv6.src_addr: lpm; + hdr.ipv6.dst_addr: lpm; + } + actions = { + controller_debug; + nat64_static; + NoAction; + } + size = NAT64_TABLE_SIZE; + default_action = controller_debug; + } + + table nat46 { + key = { + hdr.ipv4.src_addr: lpm; + hdr.ipv4.dst_addr: lpm; + } + actions = { + controller_debug; + nat46_static; + NoAction; + } + size = NAT64_TABLE_SIZE; + default_action = controller_debug; + } /********************** ICMP6 ***********************************/ @@ -272,8 +305,6 @@ control MyIngress(inout headers hdr, set_egress_port; controller_debug; controller_reply; - nat64_static; - icmp6_neighbor_solicitation; NoAction; } size = ROUTING_TABLE_SIZE; @@ -287,7 +318,6 @@ control MyIngress(inout headers hdr, } actions = { set_egress_port; - nat46_static; NoAction; } size = ROUTING_TABLE_SIZE; @@ -297,12 +327,9 @@ control MyIngress(inout headers hdr, /********************** APPLYING TABLES ***********************************/ apply { if(hdr.ipv6.isValid()) { - /* FIXME: structure / use .hit to do logic */ - // ndp_answer.apply(); - //ndp.apply(); /* flood or if it is us - answer */ - icmp6.apply(); - v6_networks.apply(); + icmp6.apply(); /* icmp6 echo, icmp6 ndp */ + v6_networks.apply(); /* routing, egress */ } if(hdr.ipv4.isValid()) { v4_networks.apply();