+ reorg +add simple nat64 w/o protocol specific translations

This commit is contained in:
Nico Schottelius 2019-03-23 16:20:13 +01:00
parent 4972f550d8
commit a408d7a803
3 changed files with 130 additions and 87 deletions

View file

@ -1161,69 +1161,78 @@ check whether tables are applied correctly (type conversion
problems) - start tcpdump - start test program - stop tcpdump - add problems) - start tcpdump - start test program - stop tcpdump - add
pcap to git repo - git add-commit-push - git pull - start wireshark - pcap to git repo - git add-commit-push - git pull - start wireshark -
debug packets - analyse code - goto 1 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 *** Performance comparison
*** Feature/Functionality difference / overview / Challenges / limitations in P4 *** Feature/Functionality difference / overview / CHALLENGES / LIMITATIONS in P4
**** P4: cannot read key from table **** P4: cannot read key from table
***** log
Key and mask for matching destination is in table. We need this Key and mask for matching destination is in table. We need this
information in the action. However this information is not exposed, so information in the action. However this information is not exposed, so
we need to specify another parameter with the same information as in we need to specify another parameter with the same information as in
the key(s). the key(s).
Log from slack: (2019-03-14) Log from slack: (2019-03-14)
nico [1:55 PM] 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 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] Nate Foster [1:58 PM]
You want to retrieve the address in the packet? In a table? 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) And do you want to do the retrieving from the data plane or the control plane? (edited)
nico [2:00 PM] 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 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 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 So it is fully data plane, what I would like to do
I'll commit sample code to show the use case more clearly 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 https://gitlab.ethz.ch/nicosc/master-thesis/blob/master/p4src/static-mapping.p4#L73
GitLab GitLab
p4src/static-mapping.p4 · master · nicosc / master-thesis p4src/static-mapping.p4 · master · nicosc / master-thesis
gitlab.ethz.ch gitlab.ethz.ch
So the action nat64_static() is used in the table v6_networks. 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;` 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 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 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] 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) To make sure I understand, in the data plane, you want to retrieve the address in the lpm pattern? (edited)
nico [2:16 PM] nico [2:16 PM]
I want to retrieve the key I want to retrieve the key
Nate Foster [2:16 PM] Nate Foster [2:16 PM]
Wait. The value `hdr.ipv6.dst_addr` is the thing used in the match. Wait. The value `hdr.ipv6.dst_addr` is the thing used in the match.
So you have that. So you have that.
What you dont have is the IPv6 address and mask put into the table by the control plane. What you dont have is the IPv6 address and mask put into the table by the control plane.
I assume you want the latter, right? I assume you want the latter, right?
nico [2:17 PM] 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 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: exactly :slightly_smiling_face:
I can "fix" this by adding another argument, but it feels somewhat wrong to do that I can "fix" this by adding another argument, but it feels somewhat wrong to do that
Because the table already knows this information Because the table already knows this information
Nate Foster [2:26 PM] Nate Foster [2:26 PM]
I cant think of a way other than the action parameter hack. I cant think of a way other than the action parameter hack.
nico [2:26 PM] nico [2:26 PM]
Oh, ok Oh, ok
Is it because the information is "lost in hardware"? Is it because the information is "lost in hardware"?
Nate Foster [2:31 PM] Nate Foster [2:31 PM]
No youre 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. No youre 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… 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 - variable length, up to 65k
Exists: Exists!
**** Synchronisation with the controller **** Synchronisation with the controller
- Double data type definition -> might differ - Double data type definition -> might differ
- TYPE_CPU for ethernet - TYPE_CPU for ethernet

View file

@ -115,16 +115,17 @@ class L2Controller(object):
for mode in self.modes: for mode in self.modes:
self.nat64_map[mode] = [] 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 mode in ["range_router"]:
for net in self.v6_routes[mode]: for v6_net in self.v6_routes[mode]:
v6_net = net['net'] for v4_net in self.v4_routes[mode]:
v4_net = self.info['v4_nat64_map'].next() v4_dst = self.info['v4_nat64_map'].next()
self.nat64_map[mode].append({ self.nat64_map[mode].append({
"v6_network": v6_net, "v6_src": v6_net['net'],
"v4_network": v4_net, "v6_dst": self.info['nat64_prefix'] # static
"nat64_prefix": self.info['nat64_prefix'] "v4_src": v4_net['net'],
"v4_dst": v4_dst
}) })
self.init_boilerplate(sw_name) self.init_boilerplate(sw_name)
@ -201,6 +202,10 @@ class L2Controller(object):
for v6route in self.v6_routes[self.mode]: 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_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: if self.args.multicast_to_controller:
self.listen_to_icmp6_multicast() self.listen_to_icmp6_multicast()
@ -209,10 +214,10 @@ class L2Controller(object):
self.init_ndp_in_switch(v6addr) self.init_ndp_in_switch(v6addr)
self.init_icmp6_echo_in_switch(v6addr) self.init_icmp6_echo_in_switch(v6addr)
self.controller.table_clear("nat64")
self.controller.table_clear("v4_networks") self.controller.table_clear("nat46")
for v4route in self.v4_routes[self.mode]: for nat64map in self.nat64_map[self.mode]:
self.controller.table_add("v4_networks", "set_egress_port", [str(v4route['net'])], [str(v4route['port'])]) self.static_nat64_mapping(**nat64map)
# Disable icmp handling in the controller # Disable icmp handling in the controller
@ -230,23 +235,25 @@ class L2Controller(object):
# # Experimental: controller does NDP, switch does ICMP6 echo reply # # 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'])]) # 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.controller.table_add("nat64", "nat64_static",
self.static_nat64_mapping(**nat64map) [str(v6_src)
str(v6_dst)],
def static_nat64_mapping(self, nat64_prefix, v6_network, v4_network): [str(v6_src.network_address),
log.info("NAT64 map: {} -> {} -> {}".format(nat64_prefix, v6_network, v4_network)) str(v4_dst.network_address),
str(v6_dst.network_address)]
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("v4_networks", "nat46_static", [str(v4_network)], self.controller.table_add("nat46", "nat46_static",
[str(v6_network.network_address), [str(v4_src)
str(v4_network.network_address), str(v4_dst)],
str(nat64_prefix.network_address)] [str(v6_src.network_address),
str(v4_dst.network_address),
str(v6_dst.network_address)]
) )

View file

@ -52,7 +52,7 @@ control MyIngress(inout headers hdr,
hdr.ipv4.diff_serv = (bit<6>)0; // no ToS hdr.ipv4.diff_serv = (bit<6>)0; // no ToS
hdr.ipv4.ecn = (bit<2>)0; // unsupported 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.totalLen = (bit<16>) hdr.ipv6.payload_length + 5; // should probably also dynamic
hdr.ipv4.identification = (bit<16>) 0; // no support for fragments 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 */ /* 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) { 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_network; ipv6_addr_t src_offset = hdr.ipv6.src_addr - v6_src;
ipv4_addr_t src = v4_network + (ipv4_addr_t) src_offset; ipv4_addr_t src = v4_dst + (ipv4_addr_t) src_offset;
ipv4_addr_t dst = (ipv4_addr_t) (hdr.ipv6.dst_addr - nat64_prefix); ipv4_addr_t dst = (ipv4_addr_t) (hdr.ipv6.dst_addr - nat64_prefix);
nat64_generic(src, dst); nat64_generic(src, dst);
/* fix the protocol specific translations */
// switch() ...
} }
/* matching key: v4_network specified again */ /* 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; ipv6_addr_t src = nat64_prefix + (ipv6_addr_t) hdr.ipv4.src_addr;
ipv4_addr_t dst_offset = hdr.ipv4.dst_addr - v4_network; ipv4_addr_t dst_offset = hdr.ipv4.dst_addr - v4_dst;
ipv6_addr_t dst = v6_network + (ipv6_addr_t) dst_offset; ipv6_addr_t dst = v6_src + (ipv6_addr_t) dst_offset;
nat46_generic(src, dst); 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 ***********************************/ /********************** ICMP6 ***********************************/
@ -272,8 +305,6 @@ control MyIngress(inout headers hdr,
set_egress_port; set_egress_port;
controller_debug; controller_debug;
controller_reply; controller_reply;
nat64_static;
icmp6_neighbor_solicitation;
NoAction; NoAction;
} }
size = ROUTING_TABLE_SIZE; size = ROUTING_TABLE_SIZE;
@ -287,7 +318,6 @@ control MyIngress(inout headers hdr,
} }
actions = { actions = {
set_egress_port; set_egress_port;
nat46_static;
NoAction; NoAction;
} }
size = ROUTING_TABLE_SIZE; size = ROUTING_TABLE_SIZE;
@ -297,12 +327,9 @@ control MyIngress(inout headers hdr,
/********************** APPLYING TABLES ***********************************/ /********************** APPLYING TABLES ***********************************/
apply { apply {
if(hdr.ipv6.isValid()) { 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(); icmp6.apply(); /* icmp6 echo, icmp6 ndp */
v6_networks.apply(); v6_networks.apply(); /* routing, egress */
} }
if(hdr.ipv4.isValid()) { if(hdr.ipv4.isValid()) {
v4_networks.apply(); v4_networks.apply();