Skip to content

Stateful NAT: Implement (most of) session manager #613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 23, 2025

Conversation

qmonnet
Copy link
Member

@qmonnet qmonnet commented Jun 19, 2025

This PR contains various changes to the code for stateful NAT, leading to the (partial) implementation of a session manager based on dashmap.

Best reviewed per-commit.

Missing components:

  • Create corresponding reverse sessions when we create new sessions
  • Port/IP allocator (future work)
  • Garbage collector to clean up stale sessions (future work)

Fixes: #181

@qmonnet qmonnet added this to the GW R1 milestone Jun 19, 2025
@qmonnet qmonnet requested a review from a team as a code owner June 19, 2025 16:39
@qmonnet qmonnet added the area/nat Related to Network Address Translation (NAT) label Jun 19, 2025
@qmonnet qmonnet self-assigned this Jun 19, 2025
@qmonnet qmonnet force-pushed the pr/qmonnet/nat-session-manager branch from 9e02626 to f02902c Compare June 19, 2025 16:41
qmonnet added 3 commits June 23, 2025 16:18
Add a method to the Packet struct to return the total length of the
packet (and not the payload or header lengths only). This is in
prevision of support for metrics as part as the stateful NAT sessions
data.

Signed-off-by: Quentin Monnet <[email protected]>
Add a .next_header() method to struct Net, in order to return the
protocol/next header number for the packet, independently of the IP
version of the packet.

To implement this method, we need to convert ip.protocol() into a
NextHeader object. To that end, implement From<IpNumber> for NextHeader.

Derive the Debug trait for struct NextHeader, to make it possible to
embed it in structs that derive the trait, in future commits.

Signed-off-by: Quentin Monnet <[email protected]>
In preparation for more work in the NAT module, make stateful_nat() and
stateless_nat() return an Option<()> and take a pointer to a Packet
(rather than simply the Net header) as an argument.

Signed-off-by: Quentin Monnet <[email protected]>
@qmonnet qmonnet force-pushed the pr/qmonnet/nat-session-manager branch from f02902c to 135952e Compare June 23, 2025 16:20
qmonnet added 12 commits June 23, 2025 17:23
To make it easier to test alternative session managers, turn struct
NatSession into a trait so that implementations of the NatSessionManager
trait are not dependent on a particular NatSession structure. Trait
NatSessionManager becomes generic, able to work with any underlying
struct that implements trait NatSession.

Provide a default implementation NatDefaultSession, to be used with the
NatDefaultSessionManager. Implementation will be completed in future
commits.

Signed-off-by: Quentin Monnet <[email protected]>
Rename and modify the methods for trait NatSessionManager:

- Add a constructor
- Update arguments and return types
- Make session creation return a SessionError
- Add a method to start the garbage collector for the manager

Note that lookup() becomes lookup_v4_mut(), and that the insertion and
removal methods to manage sessions take a "_v4" prefix, too. Equivalent
methods for IPv6 will need to be added to the trait and implemented, in
time.

Signed-off-by: Quentin Monnet <[email protected]>
Rather than a u8, use a NextHeader object in the NatTuple. Also
implement a constructor for the type.

Signed-off-by: Quentin Monnet <[email protected]>
Having stateful_nat() generic means we either need a generic Nat
pipeline, or to handle the different IP version cases prior to calling
the function, which is not great - it would mean having one table per IP
versions combination. Let's make stateful_nat() non-generic, but call
generic code to process packets inside of that function.

This commit also reorganises some of the code in the stateful NAT
logics, with no functional changes.

Signed-off-by: Quentin Monnet <[email protected]>
Increment counters to store the length and number of packets for each
session processed by the stateful NAT pipeline stage.

Signed-off-by: Quentin Monnet <[email protected]>
We don't need or plan to use the implementation. Remove it.

Signed-off-by: Quentin Monnet <[email protected]>
And have it generic.

Signed-off-by: Quentin Monnet <[email protected]>
Add a NatDefaultSessionManager object to the Nat pipeline stage, and
implement lookup and (partial) session creation for this session
manager - partial because the reverse session creation is missing at
this point.

In the future, the Nat object could trivially be made generic related to
the NatSessionManager implementation in use. For now, we only have the
default one, so use it unconditionally.

Signed-off-by: Quentin Monnet <[email protected]>
Implement set_source_port() and set_destination_port() for the Nat
object. This requires creating TCP and UDP ports from NatPort objects
which we do by implementing TryFrom<NatPort> for TcpPort and UdpPort
(and the reverse translation, while at it).

This requires exposing the "port" submodule for net::tcp as public,
which makes it consistent with its UDP counterpart.

This commit also contains a drive-by fix where we make port allocation
return an Option<NatPort> rather than an unconditional port. ICMP NAT
won't use ports, for example.

Signed-off-by: Quentin Monnet <[email protected]>
Use dashmap to implement our NatDefaultSessionManager. We use two maps
to store entries, one with IPv4 and one with IPv6 for NatTuple lookups,
and the values are IpAddr so that we can look retrieve either an IPv4 or
an IPv6 for any given address, depending on whether we're doing NAT44 or
another variant in the future.

Implement the different functions for this session manager. Note that
because of the concurrency aspects for the dashmap entries, we need to
add some lifetime parameters to several objects. We also make our
NatDefaultSession generic related to the IP version of the associated
NatTuple, because we need to make the reference to the dashmap generic,
and this reference contains the whole map entry - the NatState and the
associated NatTuple<I: NatIp>.

Signed-off-by: Quentin Monnet <[email protected]>
The unit tests don't currently cover all the code and features for
stateful NAT, but it's a start.

Signed-off-by: Quentin Monnet <[email protected]>
@qmonnet qmonnet force-pushed the pr/qmonnet/nat-session-manager branch from 135952e to e3501e8 Compare June 23, 2025 16:23
@qmonnet qmonnet enabled auto-merge June 23, 2025 16:26
@qmonnet qmonnet added this pull request to the merge queue Jun 23, 2025
Merged via the queue into main with commit 1830734 Jun 23, 2025
16 checks passed
@qmonnet qmonnet deleted the pr/qmonnet/nat-session-manager branch June 23, 2025 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/nat Related to Network Address Translation (NAT)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NAT session manager: Create new flows into flow manager
2 participants