IPFlowPortHash() — Selecting an Ephemeral Port with f2568()
If you are interested and somewhat into C — here’s a closer look. This is the IPFlowPortHash() function in place with BNG V7 RNAT. It returns a port within the ephemeral port range 49152–65535 (RFC 6335 and IANA) given the well known 5-tuple as the input (IP version independent).
Here’s the excerpt from RFC 6335 (6. Port Number Ranges):
TCP, UDP, UDP-Lite, SCTP, and DCCP use 16-bit namespaces for their
port number registries. The port registries for all of these
transport protocols are subdivided into three ranges of numbers
[RFC1340], and Section 8.1.2 describes the IANA procedures for each
range in detail:
o the System Ports, also known as the Well Known Ports, from 0-1023
(assigned by IANA)
o the User Ports, also known as the Registered Ports, from 1024-
49151 (assigned by IANA)
o the Dynamic Ports, also known as the Private or Ephemeral Ports,
from 49152-65535 (never assigned)A nice side effect: Selecting an ephemeral port with IPFlowPortHash() makes the synchronization of NAT states between VRRP master and backup unnecessary (this is why this part of the BNG bngsync protocol is no longer in use).
For ICMP echo requests and replies the ICMP_IDENTIFIER is used instead (since a port is missing here). The host-to-network and network-to-host conversions make the result independent from CPU endianness. Theoretical possible collisions are detected (and reported) within the calling RNAT module (module_rnat) — the resulting IP flow is created anyway.
Get Thomas G. Obermair’s stories in your inbox
Join Medium for free to get updates from this writer.
One thing to mention: The so called “modulo bias” is just ignored here (but existent).
Here’s the source code from BalanceNG V7 as it is (with the lower bound parametrised) — overall a nice application of the f2568() tabular hashing function:
uint16_t IPFlowPortHash(struct in6_addr inside_ip,
uint16_t inside_port, uint16_t proto, struct in6_addr outside_ip) {
uint64_t f2568_hash;
uint64_t f2568_hash_net;
uint16_t inside_port_net = htons(inside_port);
uint16_t proto_net = htons(proto);
uint32_t h_net;
uint32_t h;
uint16_t rc;
f2568_hash = f2568((uint8_t *)&inside_ip, sizeof(inside_ip));
f2568_hash = f2568resume((uint8_t *)&inside_port_net, sizeof(inside_port_net), 0, f2568_hash);
f2568_hash = f2568resume((uint8_t *)&proto_net, sizeof(proto_net), 0, f2568_hash);
f2568_hash = f2568resume((uint8_t *)&outside_ip, sizeof(outside_ip), 0, f2568_hash);
f2568_hash_net = htonll(f2568_hash);
memcpy((uint8_t *)&h_net, (uint8_t *)&f2568_hash_net, sizeof(h_net));
h = ntohl(h_net);
rc = (h % (IPFLOW_EPHEMERAL_END - PARA_EPHEMERALSTART)) + PARA_EPHEMERALSTART;
return (rc);
}






