IPFlowPortHash() - selecting ephemeral ports for reverse NAT
 
 

IPFlowPortHash() - selecting ephemeral ports for reverse NAT

June 23, 2026
development, projects
BalanceNG, algorithms, Tabulation Hashing, f2568, C

IPFlowPortHash() #

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).

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.

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 parametrized) - overall a nice application of the f2568() tabular hashing function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
}