+ reorg +add simple nat64 w/o protocol specific translations
This commit is contained in:
		
					parent
					
						
							
								4972f550d8
							
						
					
				
			
			
				commit
				
					
						a408d7a803
					
				
			
		
					 3 changed files with 130 additions and 87 deletions
				
			
		
							
								
								
									
										103
									
								
								doc/plan.org
									
										
									
									
									
								
							
							
						
						
									
										103
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue