The Generic Network Virtualization Encapsulation protocol defined in RFC 8926 assumes end-to-end connectivity between tunnel endpoints. Due to widespread use of IPv4 and NAT this assumption is not always satisfied.
The long term solution to this problem is to phase out IPv4 in favor of IPv6. However administrators may find themselves in a situation where they haven't been given a mandate to require this for the entire end-to-end path. Thus a short term solution is needed.
Many NAT methodologies aim to prolong the life of IPv4 and thus can be seen as part of the problem. NAT64 is different in that regard since it requires use of IPv6 on part of the network path and increased adoption of NAT64 is a possible way to get IPv6 deployed to everyone. For those reasons NAT64 should be regarded as the only important NAT methodology to support GENEVE tunnels through.
Attempts to use the GENEVE protocol through NAT will encounter two problems. One problem concerns outgoing packets and one concerns incoming packets.
The GENEVE protocol uses the UDP source port number as entropy for use in ECMP. Identification of multiple endpoints on a single IP address is achieved through the VNI field inside the GENEVE header instead of relying on UDP port numbers. This use of UDP source port causes outgoing packets to create an excessive number of connection tracking entries in the NAT. In the worst case this can effectively cause a denial of service attack on the NAT from the inside.
The GENEVE protocol uses 6081 as destination port number. For incoming packets this number cannot be used to look up the correct connection tracking entry as those would have been created based on the entropy of outgoing packets. Moreover the connection tracking entry if found would have directed the packet to an unintended destination port rather than 6081.
This specification defines a new option which includes the information needed for correct routing of incoming GENEVE packets through a NAT64. Presence of this option will disable connection tracking of outgoing packets to enable stateless operation and avoid consuming connection tracking entries.
The option can also include an extra field for swapping port numbers. This will allow the extension to work when one side of the communication uses a NAT setup that does not implement this specification.
The option type used by this specification has the critical bit set to 1 because the option is required on all packets in both directions. If only one side knew of this option communication between the endpoints would not be possible.
The option has variable length. Depending on the option length some of the fields may be absent. Endpoints MUST include this as the first option in all GENEVE packets sent through the NAT64. The NAT64 gateways must validate option class, type, and length. The outgoing NAT64 gateway MUST update the entropy/port swap register when present. The NAT64 gateways must ignore all fields after the entropy/port swap register.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option Class = 0x0162 | Type = 0x80 |R|R|R| Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + Destination IPv6 address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Entropy/port swap register |Detection state| Flags | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | ~ 0 - 104 octets of NAT detection state information ~ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Value | Description |
---|---|
0 | Higher level protocol tracks NAT state |
1 | Server operating in minimal client-server mode |
2 - 255 | Reserved |
A NAT64 gateway implementing this specification MUST NOT allocate port 6081 for stateful connection tracking entries.
The NAT64 gateway MUST perform the following detection steps on incoming and outgoing UDP packets:
A packet which matches all of these criteria is considered to be a GENEVE through NAT64 packet and is treated according to this specification. A UDP packet which fails any of the criteria is treated as the NAT64 gateway would treat UDP packets for any other port.
Following is example code that can recognize GENEVE through NAT64 packets:
uint8_t geneve_through_nat64_option_length(const uint8_t *udp_hd) { uint8_t udp_length = udp_hd[4] ? 255 : udp_hd[5]; const uint8_t *geneve_hd = udp_hd + 8; const uint8_t *geneve_option = geneve_hd + 8; if (udp_length < 20) { return 0; } if (udp_hd[2] != 0x17) { return 0; } if (udp_hd[3] != 0xC1) { return 0; } if (geneve_hd[0] & 0xc0) { return 0; } if ((geneve_hd[1] & 0x40) == 0) { return 0; } if (geneve_option[0] != 0x1) { return 0; } if (geneve_option[1] != 0x62) { return 0; } if (geneve_option[2] != 0x80) { return 0; } uint8_t options_length = geneve_hd[0] << 2; uint8_t option_length = ((geneve_option[3] & 0x1f) + 1) << 2; if (option_length > options_length) { return 0; } if (options_length + 16 > udp_length) { return 0; } return option_length; }
When an outgoing GENEVE through NAT64 packet is detected it must be passed statelessly through the NAT64 gateway.
If the option length is 20 octets or less there is no entropy/port swap register in this packet. For such a packet source and destination port numbers MUST remain unchanged as packet is passed through the NAT64 gateway.
If the option length is 24 octets or greate there is an entropy/port swap register in this packet. The port numbers must be updated according to the following steps:
This rotation of port number fields will not alter the packet checksum. The NAT64 gateway MUST update the checksum to accomodate for the other changes it has made to the packet regardless of whether port numbers were rotated or not.
Following is example code that can handle outgoing GENEVE through NAT64 packets:
if ((ip6_hd[6] == IPPROTO_UDP) && (option_length = geneve_through_nat64_option_length(ip6_hd + 40))) { if (option_length >= 22) { // Perform the entropy/port swap ip6_hd[42] = ip6_hd[76]; ip6_hd[43] = ip6_hd[77]; ip6_hd[76] = ip6_hd[40]; ip6_hd[77] = ip6_hd[41]; ip6_hd[40] = 0x17; ip6_hd[41] = 0xC1; } port = (((uint16_t)ip6_hd[40]) << 8) | ip6_hd[41]; }
When an incoming GENEVE through NAT64 packet is detected it must be passed statelessly through the NAT64 gateway. Source and destination port numbers MUST remain unchanged as packet is passed through the NAT64 gateway. Destination IPv6 address contained within the option MUST be copied to the destination IPv6 address of the translated UDP packet.
If the option is 16 octets or shorter it cannot contain a full IPv6 address and it must be treated as having failed validation. UDP packets with destination port 6081 which did not pass validation must be treated as the NAT64 gateway would treat UDP packets for other port numbers. Since no connection tracking entry is allowed to exist for port 6081 this means the NAT64 gateway MUST drop the packet and MAY produce an ICMP error response.
Following is example code that can handle incoming GENEVE through NAT64 packets:
if ((ip_hd[9] == IPPROTO_UDP) && (geneve_through_nat64_option_length(transport_hd) >= 20)) { memcpy(destination_address_buffer, transport_hd + 20, 16); memcpy(destination_address_buffer+16, transport_hd + 2, 2); ip_and_port = destination_address_buffer; }
GENEVE through NAT64 endpoints MUST include the option on all GENEVE packets they send to the NAT64 gateway from either side.
Endpoints can be configured in different operating modes. The two endpoints of a connection must be configured in compatible operating modes.
Symmetric mode is applicable when both endpoints are using a NAT64 gateway which complies with this specification and no other NAT is present on the communication path between them.
Endpoints in symmetric mode operate statelessly. Endpoints MUST be configured in advance to know their own IPv6 address, the IPv6 address of the other endpoint, and the path IPv6 address constructed by combining the prefix of the local NAT64 with the IPv4 address of the remove NAT64.
Outgoing packets MUST use the path IPv6 address as destination address and MUST contain a 20 octet GENEVE through NAT64 option which contains the IPv6 address of the remote end of the tunnel.
The option will contain no entropy/port swap register thus the roation of port numbers is not used in symmetric mode. The GENEVE packets sent in symmetric mode will have unchanged port numbers on the entire communication path as it is converted from IPv6 to IPv4 and back. The source port will be entropy for ECMP and the destination port will be 6081.
Minimal client-server mode is applicable when the server is using a NAT64 gateway which complies with this specification and no other NAT. The client can use any combination of NAT as long as it does not include a NAT64 gateway which implements this specification.
The server SHOULD be configured to know its own IPv6 address and the IPv6 address of the client. The server MAY be configured to know the prefix of the local NAT64. The server SHOULD NOT be configured to know the IPv4 address of the client as that is assumed to be a dynamically changing addresss.
The client MUST be configured to know the IPv6 address of the server and the IPv4 address of the NAT64 gateway used by the server. This MAY involve the client resolving either or both IP addresses through DNS lookups. The client SHOULD be configured to know its own IPv6 address. The client SHOULD by default listen for incoming packets on port 6081. The client SHOULD have a configuration option to use an ephemeral port for incoming packets instead.
GENEVE packets sent by the client MUST be sent to port 6081 on the IPv4 address of the NAT64 gateway used by the server. The source port MUST be set to the listening port used for incoming packets. Entropy for ECMP is not possible in this mode.
GENEVE packets sent by the client MUST include the 20 octet version of the option containing the IPv6 address of the server.
The server MUST learn the path IPv6 address and mapped port number of the client from incoming packets. GENEVE packets sent by the server MUST be sent to port 6081 on the learned path IPv6 address. The source port SHOULD be used for entropy for ECMP.
GENEVE packets sent by the server MUST include the 24 octet version of the option containing the IPv6 address of the client if configured and otherwise a copy of the learned path IPv6 address. The entropy/port swap register MUST be set to the mapped port number of the client learned from incoming packets. Detection state MUST be set to 1 and flags MUST be set to 0.
Support for this option on a NAT64 gateway allows for incoming packets in scenarios where it was otherwise not possible. If a network has mistakenly been configured based on the assumption that NAT can be used in place of a firewall, that can lead to surprising results.
This risk is avoided for compliant implementations by requiring a critical GENEVE option. The NAT64 gateway MUST drop the incoming packets if the critical option is not present. RFC 8926 requires destination hosts on the local network which aren't configured to use this extension to drop incoming packets with this option. This means incoming packets which don't match a configured tunnel MUST be dropped by either the NAT64 gateway or the endpoint.