OpenVPN
Structure of the VPN tunnel state storage

This section describes how OpenVPN stores its VPN tunnel state during operation.

OpenVPN uses several data structures as storage containers for state information of active VPN tunnels. These are described in this section, together with a little bit of history to help understand the origin of the current architecture.

Whether an OpenVPN process is running in client-mode or server-mode determines whether it can support only one or multiple simultaneously active VPN tunnels. This consequently also determines how the associated state information is wrapped up internally. This section gives an overview of the differences.

Historic developments

In the old v1.x series, an OpenVPN process managed only one single VPN tunnel. This allowed the VPN tunnel state to be stored together with process-global information in one single context structure.

This changed, however, in the v2.x series, as new OpenVPN versions running in server-mode can support multiple simultaneously active VPN tunnels. This necessitated a redesign of the VPN tunnel state container structures, and modification of the External Multiplexer and Internal Multiplexer systems. The majority of these changes are only relevant for OpenVPN processes running in server-mode, and the client-mode structure has remained very similar to the v1.x single-tunnel form.

Client-mode state

An OpenVPN process running in client-mode can manage at most one single VPN tunnel at any one time. The state information for a client's VPN tunnel is stored in a context structure.

The context structure is created in the main() function. That is also where process-wide initialization takes place, such as parsing command line options and reading configuration files. The context is then passed to tunnel_point_to_point() which drives OpenVPN's main event processing loop. These functions are both part of the Main Event Loop module.

Initialization and cleanup

Because there is only one context structure present, it can be initialized and cleaned up from the client's main event processing function. Before the tunnel_point_to_point() function enters its event loop, it calls init_instance_handle_signals() which calls init_instance() to initialize the single context structure. After the event loop stops, it calls close_instance() to clean up the context.

Event processing

When the main event processing loop activates the external or internal multiplexer to handle a network event, it is not necessary to determine which VPN tunnel the event is associated with, because there is only one VPN tunnel active.

Server-mode state

An OpenVPN process running in server-mode can manage multiple simultaneously active VPN tunnels. For every VPN tunnel active, in other words for every OpenVPN client which is connected to a server, the OpenVPN server has one context structure in which it stores that particular VPN tunnel's state information.

Multi_context and multi_instance structures

To support multiple context structures, each is wrapped in a multi_instance structure, and all the multi_instance structures are registered in one single multi_context structure. The External Multiplexer and Internal Multiplexer then use the multi_context to retrieve the correct multi_instance and context associated with a given network address.

Startup and initialization

An OpenVPN process running in server-mode starts in the same main() function as it would in client-mode. The same process-wide initialization is performed, and the resulting state and configuration is stored in a context structure. The server-mode and client-mode processes diverge when the main() function calls one of tunnel_point_to_point() or tunnel_server().

In server-mode, main() calls the tunnel_server() function, which transfers control to tunnel_server_udp_single_threaded() or tunnel_server_tcp() depending on the external transport protocol.

These functions receive the context created in main(). This object has a special status in server-mode, as it does not represent an active VPN tunnel, but does contain process-wide configuration parameters. In the source code, it is often stored in "top" variables. To distinguish this object from other instances of the same type, its context.mode value is set to CM_TOP. Other context objects, which do represent active VPN tunnels, have a context.mode set to CM_CHILD_UDP or CM_CHILD_TCP, depending on the external transport protocol.

Both tunnel_server_udp_single_threaded() and tunnel_server_tcp() perform similar initialization. In either case, a multi_context structure is created, and it is initialized according to the configuration stored in the top context by the multi_init() and multi_top_init() functions.

Creating and destroying VPN tunnels

When an OpenVPN client makes a new connection to a server, the server creates a new context and multi_instance. The latter is registered in the multi_context, which makes it possible for the external and internal multiplexers to retrieve the correct multi_instance and context when a network event occurs.

Final cleanup

After the main event loop exits, both tunnel_server_udp_single_threaded() and tunnel_server_tcp() perform similar cleanup. They call multi_uninit() followed by multi_top_free() to clean up the multi_context structure.