[Openvpn-devel] Add 'localhost' token to client-nat network option

Message ID CAAWBBYOr-ZKmr3754DqzkssCr04C=n75C4bOihN04nx-4-xVvw@mail.gmail.com
State New
Headers show
Series [Openvpn-devel] Add 'localhost' token to client-nat network option | expand

Commit Message

Rafael Gava Feb. 20, 2025, 7:48 p.m. UTC
Dear OpenVPN Community,

I am submitting this patch to introduce a 'localhost' token to the
`client-nat` network option, allowing OpenVPN clients to dynamically use
the IP address provided by the server. This enhancement is particularly
useful in scenarios where OpenVPN is deployed as a VPN gateway bridging a
local network and a VPN tunnel.

Currently, the `client-nat` option requires a static client IP, which
limits its flexibility when dealing with dynamically assigned IP addresses.
By introducing a 'localhost' token, OpenVPN can automatically substitute it
with the current client-assigned IP address, streamlining network
configurations in dynamic environments.

Example:

client-nat snat localhost 255.255.255.255 172.19.80.17

I believe this feature will be beneficial for many OpenVPN users, improving
automation and simplifying VPN gateway configurations. Please find the
patch inline for review and feedback.

Best regards,

Rafael Gava

----
From fbc7045d652b5f11e2aa043aa2af9ca14f36b604 Mon Sep 17 00:00:00 2001
From: Rafael Gava <gava100@gmail.com>
Date: Thu, 20 Feb 2025 19:19:39 +0000
Subject: [PATCH] Added the localhost token to the client-nat network option,
 enabling the application to dynamically use the client IP provided by the
 server.

Signed-off-by: Rafael Gava <gava100@gmail.com>
---
 src/openvpn/clinat.c  | 47 +++++++++++++++++++++++++++++++++++++++----
 src/openvpn/clinat.h  |  3 +++
 src/openvpn/init.c    |  2 ++
 src/openvpn/options.c |  1 +
 4 files changed, 49 insertions(+), 4 deletions(-)

     "--setenv name value : Set a custom environmental variable to pass to
script.\n"
     "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to
allow\n"

Comments

Frank Lichtenheld Feb. 21, 2025, 11:58 a.m. UTC | #1
On Thu, Feb 20, 2025 at 04:48:27PM -0300, Rafael Gava wrote:
> I believe this feature will be beneficial for many OpenVPN users, improving
> automation and simplifying VPN gateway configurations. Please find the
> patch inline for review and feedback.
> 

Thanks for your submission. Some general notes about the patch.
(Not a review of the actual change but more about the format of
submission)

> ----
> From fbc7045d652b5f11e2aa043aa2af9ca14f36b604 Mon Sep 17 00:00:00 2001
> From: Rafael Gava <gava100@gmail.com>
> Date: Thu, 20 Feb 2025 19:19:39 +0000
> Subject: [PATCH] Added the localhost token to the client-nat network option,
>  enabling the application to dynamically use the client IP provided by the
>  server.

The subject line should be concise and make sense on its own. The rest
of the commit message should be the longer description. You can achieve this
by adding an empty line after the first one.

> Signed-off-by: Rafael Gava <gava100@gmail.com>
> ---
>  src/openvpn/clinat.c  | 47 +++++++++++++++++++++++++++++++++++++++----
>  src/openvpn/clinat.h  |  3 +++
>  src/openvpn/init.c    |  2 ++
>  src/openvpn/options.c |  1 +
>  4 files changed, 49 insertions(+), 4 deletions(-)
> 
> diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
> index 2d3b359f..1c79b20d 100644
> --- a/src/openvpn/clinat.c
> +++ b/src/openvpn/clinat.c
> @@ -127,12 +127,19 @@ add_client_nat_to_option_list(struct
> client_nat_option_list *dest,
>          return;
>      }
> 
> -    e.network = getaddr(0, network, 0, &ok, NULL);
> -    if (!ok)
> +    if (network && !strcmp(network, "localhost"))
>      {
> -        msg(msglevel, "client-nat: bad network: %s", network);
> -        return;
> +        msg (M_INFO, "*** client-nat localhost detected...");
> +        e.network = 0xFFFFFFFF;
> +    } else {

This is not in line with our usual formatting. We always format
like this

}
else
{

There is an uncrustify config in dev-tools/uncrustify.conf that can
help to check the formatting. Or you submit the patch in Github or
Gerrit which can do automated checks of the formatting.

> +        e.network = getaddr(0, network, 0, &ok, NULL);
> +        if (!ok)
> +        {
> +            msg(msglevel, "client-nat: bad network: %s", network);
> +            return;
> +        }
>      }
> +
>      e.netmask = getaddr(0, netmask, 0, &ok, NULL);
>      if (!ok)
>      {
> @@ -274,3 +281,35 @@ client_nat_transform(const struct
> client_nat_option_list *list,
>          }
>      }
>  }
> +
> +/*
> +* Replaces the localhost token with the IP received from OpenVPN
> +*/
> +bool
> +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
> local_ip)
> +{
> +    int i;
> +    bool ret = false;
> +
> +    if (!dest) {
> +        return ret;
> +    }
> +
> +    for (i=0; i <= dest->n; i++)
> +    {
> +        struct client_nat_entry *nat_entry = &dest->entries[i];
> +        if (nat_entry && nat_entry->network == 0xFFFFFFFF)
> +        {
> +            struct in_addr addr;
> +
> +            nat_entry->network = ntohl(local_ip);
> +            addr.s_addr = nat_entry->network;
> +            char *dot_ip = inet_ntoa(addr);
> +
> +            msg (M_INFO, "CNAT - Updating NAT table from localhost to:
> %s", dot_ip);
> +            ret = true;
> +        }
> +    }
> +
> +    return ret;
> +}
> diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
> index 94141f51..06afa3b4 100644
> --- a/src/openvpn/clinat.h
> +++ b/src/openvpn/clinat.h
> @@ -64,4 +64,7 @@ void client_nat_transform(const struct
> client_nat_option_list *list,
>                            struct buffer *ipbuf,
>                            const int direction);
> 
> +bool update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
> local_ip);
> +
> +
>  #endif /* if !defined(CLINAT_H) */
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index b57e5f8a..dadc10dc 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -2052,6 +2052,8 @@ do_open_tun(struct context *c, int *error_flags)
>              *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
>          }
> 
> +        update_localhost_nat(c->options.client_nat, c->c1.tuntap->local);
> +
>          ret = true;
>          static_context = c;
>      }
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index 6b2dfa58..f7161931 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -254,6 +254,7 @@ static const char usage_message[] =
>      "                   (Server) Instead of forwarding IPv6 packets send\n"
>      "                   ICMPv6 host unreachable packets to the client.\n"
>      "--client-nat snat|dnat network netmask alias : on client add 1-to-1
> NAT rule.\n"
> +    "                  set the network to 'localhost' to use the client ip
> received from the server.\n"

Please also include an update to the documentation in
doc/man-sections/client-options.rst

>      "--push-peer-info : (client only) push client info to server.\n"
>      "--setenv name value : Set a custom environmental variable to pass to
> script.\n"
>      "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to
> allow\n"
> -- 
> 2.39.5

Regards,
Arne Schwabe Feb. 21, 2025, 12:05 p.m. UTC | #2
Am 20.02.25 um 20:48 schrieb Rafael Gava:
> Dear OpenVPN Community,
> 
> I am submitting this patch to introduce a 'localhost' token to the 
> `client-nat` network option, allowing OpenVPN clients to dynamically use 
> the IP address provided by the server. This enhancement is particularly 
> useful in scenarios where OpenVPN is deployed as a VPN gateway bridging 
> a local network and a VPN tunnel.
> 
> Currently, the `client-nat` option requires a static client IP, which 
> limits its flexibility when dealing with dynamically assigned IP 
> addresses. By introducing a 'localhost' token, OpenVPN can automatically 
> substitute it with the current client-assigned IP address, streamlining 
> network configurations in dynamic environments.
> 
> Example:
> 
> client-nat snat localhost 255.255.255.255 172.19.80.17

I would assume that localhost here matches only 127.0.0.1. I would 
prefer something like assigned-ip or something that actually matches the 
real behaviour instead of reusing something that already has a pretty 
established meaning.

>=
> diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
> index 2d3b359f..1c79b20d 100644
> --- a/src/openvpn/clinat.c
> +++ b/src/openvpn/clinat.c
> @@ -127,12 +127,19 @@ add_client_nat_to_option_list(struct 
> client_nat_option_list *dest,
>           return;
>       }
> 
> -    e.network = getaddr(0, network, 0, &ok, NULL);
> -    if (!ok)
> +    if (network && !strcmp(network, "localhost"))
>       {
> -        msg(msglevel, "client-nat: bad network: %s", network);
> -        return;
> +        msg (M_INFO, "*** client-nat localhost detected...");

This looks like a left over debug message.

> +        e.network = 0xFFFFFFFF;
> +    } else {
> +        e.network = getaddr(0, network, 0, &ok, NULL);
> +        if (!ok)
> +        {
> +            msg(msglevel, "client-nat: bad network: %s", network);
> +            return;
> +        }
>       }
> +
>       e.netmask = getaddr(0, netmask, 0, &ok, NULL);
>       if (!ok)
>       {
> @@ -274,3 +281,35 @@ client_nat_transform(const struct 
> client_nat_option_list *list,
>           }
>       }
>   }
> +
> +/*
> +* Replaces the localhost token with the IP received from OpenVPN
> +*/
> +bool
> +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t 
> local_ip)
> +{
> +    int i;
> +    bool ret = false;
> +
> +    if (!dest) {
> +        return ret;
> +    }
> +
> +    for (i=0; i <= dest->n; i++)
> +    {
> +        struct client_nat_entry *nat_entry = &dest->entries[i];
> +        if (nat_entry && nat_entry->network == 0xFFFFFFFF)
> +        {
> +            struct in_addr addr;
> +
> +            nat_entry->network = ntohl(local_ip);
> +            addr.s_addr = nat_entry->network;
> +            char *dot_ip = inet_ntoa(addr);
> +
> +            msg (M_INFO, "CNAT - Updating NAT table from localhost to: 
> %s", dot_ip);
> +            ret = true;
> +        }
> +    }
> +
> +    return ret;
> +}
> diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
> index 94141f51..06afa3b4 100644
> --- a/src/openvpn/clinat.h
> +++ b/src/openvpn/clinat.h
> @@ -64,4 +64,7 @@ void client_nat_transform(const struct 
> client_nat_option_list *list,
>                             struct buffer *ipbuf,
>                             const int direction);
> 
> +bool update_localhost_nat(struct client_nat_option_list *dest, 
> in_addr_t local_ip);
> +
> +
>   #endif /* if !defined(CLINAT_H) */
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index b57e5f8a..dadc10dc 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -2052,6 +2052,8 @@ do_open_tun(struct context *c, int *error_flags)
>               *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
>           }
> 
> +        update_localhost_nat(c->options.client_nat, c->c1.tuntap->local);
> +
>           ret = true;
>           static_context = c;
>       }
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index 6b2dfa58..f7161931 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -254,6 +254,7 @@ static const char usage_message[] =
>       "                   (Server) Instead of forwarding IPv6 packets 
> send\n"
>       "                   ICMPv6 host unreachable packets to the client.\n"
>       "--client-nat snat|dnat network netmask alias : on client add 1- 
> to-1 NAT rule.\n"
> +    "                  set the network to 'localhost' to use the client 
> ip received from the server.\n"
>       "--push-peer-info : (client only) push client info to server.\n"
>       "--setenv name value : Set a custom environmental variable to pass 
> to script.\n"
>       "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking 
> to allow\n"


OpenVPN also needs to work in configuration that are pure p2p without 
--pull (or --client with is --tls-client +  --pull). Have you tested 
that it works in that configuration as well?

Arne
Antonio Quartulli Feb. 21, 2025, 1:08 p.m. UTC | #3
On 21/02/2025 12:58, Frank Lichtenheld wrote:
> On Thu, Feb 20, 2025 at 04:48:27PM -0300, Rafael Gava wrote:
>> +bool
>> +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
>> local_ip)
>> +{
>> +    int i;
>> +    bool ret = false;
>> +
>> +    if (!dest) {

Also, the opening '{' should go on its own line, like you did for all 
other blocks.

Regards,
Rafael Gava Feb. 23, 2025, 6:26 p.m. UTC | #4
Hi Frank,

Thank you for your feedback. I'll make the changes as per your suggestions
and resubmit the patch later. My apologies for not realizing that you were
using a formatting check tool. I installed Uncrustify, and it worked
perfectly. :-)

I also wanted to ask about the automated tests on GitHub. Should I fork the
project and submit the patch there, or should I submit it directly to the
main project? What is the recommended approach?

Looking forward to your guidance.

Best regards,
Rafael


On Fri, Feb 21, 2025 at 8:58 AM Frank Lichtenheld <frank@lichtenheld.com>
wrote:

> On Thu, Feb 20, 2025 at 04:48:27PM -0300, Rafael Gava wrote:
> > I believe this feature will be beneficial for many OpenVPN users,
> improving
> > automation and simplifying VPN gateway configurations. Please find the
> > patch inline for review and feedback.
> >
>
> Thanks for your submission. Some general notes about the patch.
> (Not a review of the actual change but more about the format of
> submission)
>
> > ----
> > From fbc7045d652b5f11e2aa043aa2af9ca14f36b604 Mon Sep 17 00:00:00 2001
> > From: Rafael Gava <gava100@gmail.com>
> > Date: Thu, 20 Feb 2025 19:19:39 +0000
> > Subject: [PATCH] Added the localhost token to the client-nat network
> option,
> >  enabling the application to dynamically use the client IP provided by
> the
> >  server.
>
> The subject line should be concise and make sense on its own. The rest
> of the commit message should be the longer description. You can achieve
> this
> by adding an empty line after the first one.
>
> > Signed-off-by: Rafael Gava <gava100@gmail.com>
> > ---
> >  src/openvpn/clinat.c  | 47 +++++++++++++++++++++++++++++++++++++++----
> >  src/openvpn/clinat.h  |  3 +++
> >  src/openvpn/init.c    |  2 ++
> >  src/openvpn/options.c |  1 +
> >  4 files changed, 49 insertions(+), 4 deletions(-)
> >
> > diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
> > index 2d3b359f..1c79b20d 100644
> > --- a/src/openvpn/clinat.c
> > +++ b/src/openvpn/clinat.c
> > @@ -127,12 +127,19 @@ add_client_nat_to_option_list(struct
> > client_nat_option_list *dest,
> >          return;
> >      }
> >
> > -    e.network = getaddr(0, network, 0, &ok, NULL);
> > -    if (!ok)
> > +    if (network && !strcmp(network, "localhost"))
> >      {
> > -        msg(msglevel, "client-nat: bad network: %s", network);
> > -        return;
> > +        msg (M_INFO, "*** client-nat localhost detected...");
> > +        e.network = 0xFFFFFFFF;
> > +    } else {
>
> This is not in line with our usual formatting. We always format
> like this
>
> }
> else
> {
>
> There is an uncrustify config in dev-tools/uncrustify.conf that can
> help to check the formatting. Or you submit the patch in Github or
> Gerrit which can do automated checks of the formatting.
>
> > +        e.network = getaddr(0, network, 0, &ok, NULL);
> > +        if (!ok)
> > +        {
> > +            msg(msglevel, "client-nat: bad network: %s", network);
> > +            return;
> > +        }
> >      }
> > +
> >      e.netmask = getaddr(0, netmask, 0, &ok, NULL);
> >      if (!ok)
> >      {
> > @@ -274,3 +281,35 @@ client_nat_transform(const struct
> > client_nat_option_list *list,
> >          }
> >      }
> >  }
> > +
> > +/*
> > +* Replaces the localhost token with the IP received from OpenVPN
> > +*/
> > +bool
> > +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
> > local_ip)
> > +{
> > +    int i;
> > +    bool ret = false;
> > +
> > +    if (!dest) {
> > +        return ret;
> > +    }
> > +
> > +    for (i=0; i <= dest->n; i++)
> > +    {
> > +        struct client_nat_entry *nat_entry = &dest->entries[i];
> > +        if (nat_entry && nat_entry->network == 0xFFFFFFFF)
> > +        {
> > +            struct in_addr addr;
> > +
> > +            nat_entry->network = ntohl(local_ip);
> > +            addr.s_addr = nat_entry->network;
> > +            char *dot_ip = inet_ntoa(addr);
> > +
> > +            msg (M_INFO, "CNAT - Updating NAT table from localhost to:
> > %s", dot_ip);
> > +            ret = true;
> > +        }
> > +    }
> > +
> > +    return ret;
> > +}
> > diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
> > index 94141f51..06afa3b4 100644
> > --- a/src/openvpn/clinat.h
> > +++ b/src/openvpn/clinat.h
> > @@ -64,4 +64,7 @@ void client_nat_transform(const struct
> > client_nat_option_list *list,
> >                            struct buffer *ipbuf,
> >                            const int direction);
> >
> > +bool update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
> > local_ip);
> > +
> > +
> >  #endif /* if !defined(CLINAT_H) */
> > diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> > index b57e5f8a..dadc10dc 100644
> > --- a/src/openvpn/init.c
> > +++ b/src/openvpn/init.c
> > @@ -2052,6 +2052,8 @@ do_open_tun(struct context *c, int *error_flags)
> >              *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
> >          }
> >
> > +        update_localhost_nat(c->options.client_nat,
> c->c1.tuntap->local);
> > +
> >          ret = true;
> >          static_context = c;
> >      }
> > diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> > index 6b2dfa58..f7161931 100644
> > --- a/src/openvpn/options.c
> > +++ b/src/openvpn/options.c
> > @@ -254,6 +254,7 @@ static const char usage_message[] =
> >      "                   (Server) Instead of forwarding IPv6 packets
> send\n"
> >      "                   ICMPv6 host unreachable packets to the
> client.\n"
> >      "--client-nat snat|dnat network netmask alias : on client add 1-to-1
> > NAT rule.\n"
> > +    "                  set the network to 'localhost' to use the client
> ip
> > received from the server.\n"
>
> Please also include an update to the documentation in
> doc/man-sections/client-options.rst
>
> >      "--push-peer-info : (client only) push client info to server.\n"
> >      "--setenv name value : Set a custom environmental variable to pass
> to
> > script.\n"
> >      "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking
> to
> > allow\n"
> > --
> > 2.39.5
>
> Regards,
> --
>   Frank Lichtenheld
>
Rafael Gava Feb. 23, 2025, 7:05 p.m. UTC | #5
Hi Arne,

Thank you for your feedback.

I agree with your suggestion and will replace the localhost token with
'assigned-ip' before resubmitting the patch.

Regarding your question about testing P2P, I haven't tested it yet. Do you
happen to have a configuration I could use for this scenario?

Best regards,
Rafael

On Fri, Feb 21, 2025 at 9:05 AM Arne Schwabe <arne@rfc2549.org> wrote:

> Am 20.02.25 um 20:48 schrieb Rafael Gava:
> > Dear OpenVPN Community,
> >
> > I am submitting this patch to introduce a 'localhost' token to the
> > `client-nat` network option, allowing OpenVPN clients to dynamically use
> > the IP address provided by the server. This enhancement is particularly
> > useful in scenarios where OpenVPN is deployed as a VPN gateway bridging
> > a local network and a VPN tunnel.
> >
> > Currently, the `client-nat` option requires a static client IP, which
> > limits its flexibility when dealing with dynamically assigned IP
> > addresses. By introducing a 'localhost' token, OpenVPN can automatically
> > substitute it with the current client-assigned IP address, streamlining
> > network configurations in dynamic environments.
> >
> > Example:
> >
> > client-nat snat localhost 255.255.255.255 172.19.80.17
>
> I would assume that localhost here matches only 127.0.0.1. I would
> prefer something like assigned-ip or something that actually matches the
> real behaviour instead of reusing something that already has a pretty
> established meaning.
>
> >=
> > diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
> > index 2d3b359f..1c79b20d 100644
> > --- a/src/openvpn/clinat.c
> > +++ b/src/openvpn/clinat.c
> > @@ -127,12 +127,19 @@ add_client_nat_to_option_list(struct
> > client_nat_option_list *dest,
> >           return;
> >       }
> >
> > -    e.network = getaddr(0, network, 0, &ok, NULL);
> > -    if (!ok)
> > +    if (network && !strcmp(network, "localhost"))
> >       {
> > -        msg(msglevel, "client-nat: bad network: %s", network);
> > -        return;
> > +        msg (M_INFO, "*** client-nat localhost detected...");
>
> This looks like a left over debug message.
>
> > +        e.network = 0xFFFFFFFF;
> > +    } else {
> > +        e.network = getaddr(0, network, 0, &ok, NULL);
> > +        if (!ok)
> > +        {
> > +            msg(msglevel, "client-nat: bad network: %s", network);
> > +            return;
> > +        }
> >       }
> > +
> >       e.netmask = getaddr(0, netmask, 0, &ok, NULL);
> >       if (!ok)
> >       {
> > @@ -274,3 +281,35 @@ client_nat_transform(const struct
> > client_nat_option_list *list,
> >           }
> >       }
> >   }
> > +
> > +/*
> > +* Replaces the localhost token with the IP received from OpenVPN
> > +*/
> > +bool
> > +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
> > local_ip)
> > +{
> > +    int i;
> > +    bool ret = false;
> > +
> > +    if (!dest) {
> > +        return ret;
> > +    }
> > +
> > +    for (i=0; i <= dest->n; i++)
> > +    {
> > +        struct client_nat_entry *nat_entry = &dest->entries[i];
> > +        if (nat_entry && nat_entry->network == 0xFFFFFFFF)
> > +        {
> > +            struct in_addr addr;
> > +
> > +            nat_entry->network = ntohl(local_ip);
> > +            addr.s_addr = nat_entry->network;
> > +            char *dot_ip = inet_ntoa(addr);
> > +
> > +            msg (M_INFO, "CNAT - Updating NAT table from localhost to:
> > %s", dot_ip);
> > +            ret = true;
> > +        }
> > +    }
> > +
> > +    return ret;
> > +}
> > diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
> > index 94141f51..06afa3b4 100644
> > --- a/src/openvpn/clinat.h
> > +++ b/src/openvpn/clinat.h
> > @@ -64,4 +64,7 @@ void client_nat_transform(const struct
> > client_nat_option_list *list,
> >                             struct buffer *ipbuf,
> >                             const int direction);
> >
> > +bool update_localhost_nat(struct client_nat_option_list *dest,
> > in_addr_t local_ip);
> > +
> > +
> >   #endif /* if !defined(CLINAT_H) */
> > diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> > index b57e5f8a..dadc10dc 100644
> > --- a/src/openvpn/init.c
> > +++ b/src/openvpn/init.c
> > @@ -2052,6 +2052,8 @@ do_open_tun(struct context *c, int *error_flags)
> >               *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
> >           }
> >
> > +        update_localhost_nat(c->options.client_nat,
> c->c1.tuntap->local);
> > +
> >           ret = true;
> >           static_context = c;
> >       }
> > diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> > index 6b2dfa58..f7161931 100644
> > --- a/src/openvpn/options.c
> > +++ b/src/openvpn/options.c
> > @@ -254,6 +254,7 @@ static const char usage_message[] =
> >       "                   (Server) Instead of forwarding IPv6 packets
> > send\n"
> >       "                   ICMPv6 host unreachable packets to the
> client.\n"
> >       "--client-nat snat|dnat network netmask alias : on client add 1-
> > to-1 NAT rule.\n"
> > +    "                  set the network to 'localhost' to use the client
> > ip received from the server.\n"
> >       "--push-peer-info : (client only) push client info to server.\n"
> >       "--setenv name value : Set a custom environmental variable to pass
> > to script.\n"
> >       "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking
> > to allow\n"
>
>
> OpenVPN also needs to work in configuration that are pure p2p without
> --pull (or --client with is --tls-client +  --pull). Have you tested
> that it works in that configuration as well?
>
> Arne
>
>
>
Arne Schwabe Feb. 24, 2025, 1:38 p.m. UTC | #6
Am 23.02.25 um 20:05 schrieb Rafael Gava:
> Hi Arne,
> 
> Thank you for your feedback.
> 
> I agree with your suggestion and will replace the localhost token with 
> 'assigned-ip' before resubmitting the patch.
> 
> Regarding your question about testing P2P, I haven't tested it yet. Do 
> you happen to have a configuration I could use for this scenario?
> 

openvpn-examples man page:

    Example 2: A tunnel with self-signed certificates and fingerprint

Arne
Frank Lichtenheld Feb. 24, 2025, 1:48 p.m. UTC | #7
On Sun, Feb 23, 2025 at 03:26:36PM -0300, Rafael Gava wrote:
> Hi Frank,
> 
> Thank you for your feedback. I'll make the changes as per your suggestions
> and resubmit the patch later. My apologies for not realizing that you were
> using a formatting check tool. I installed Uncrustify, and it worked
> perfectly. :-)
> 
> I also wanted to ask about the automated tests on GitHub. Should I fork the
> project and submit the patch there, or should I submit it directly to the
> main project? What is the recommended approach?

If you want to use Github, then fork the repository. Note that we do not
merge Github PRs directly. Actual patch approval only happens in
Gerrit or here on the list (see also 
https://community.openvpn.net/openvpn/wiki/DeveloperDocumentation#PatchsubmissionviaGerrit).
So using Github is not required, but it allows you to use the Github
actions basically instantly to get some cross-platform verification
of your work.

Regards,

Patch

diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
index 2d3b359f..1c79b20d 100644
--- a/src/openvpn/clinat.c
+++ b/src/openvpn/clinat.c
@@ -127,12 +127,19 @@  add_client_nat_to_option_list(struct
client_nat_option_list *dest,
         return;
     }

-    e.network = getaddr(0, network, 0, &ok, NULL);
-    if (!ok)
+    if (network && !strcmp(network, "localhost"))
     {
-        msg(msglevel, "client-nat: bad network: %s", network);
-        return;
+        msg (M_INFO, "*** client-nat localhost detected...");
+        e.network = 0xFFFFFFFF;
+    } else {
+        e.network = getaddr(0, network, 0, &ok, NULL);
+        if (!ok)
+        {
+            msg(msglevel, "client-nat: bad network: %s", network);
+            return;
+        }
     }
+
     e.netmask = getaddr(0, netmask, 0, &ok, NULL);
     if (!ok)
     {
@@ -274,3 +281,35 @@  client_nat_transform(const struct
client_nat_option_list *list,
         }
     }
 }
+
+/*
+* Replaces the localhost token with the IP received from OpenVPN
+*/
+bool
+update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
local_ip)
+{
+    int i;
+    bool ret = false;
+
+    if (!dest) {
+        return ret;
+    }
+
+    for (i=0; i <= dest->n; i++)
+    {
+        struct client_nat_entry *nat_entry = &dest->entries[i];
+        if (nat_entry && nat_entry->network == 0xFFFFFFFF)
+        {
+            struct in_addr addr;
+
+            nat_entry->network = ntohl(local_ip);
+            addr.s_addr = nat_entry->network;
+            char *dot_ip = inet_ntoa(addr);
+
+            msg (M_INFO, "CNAT - Updating NAT table from localhost to:
%s", dot_ip);
+            ret = true;
+        }
+    }
+
+    return ret;
+}
diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
index 94141f51..06afa3b4 100644
--- a/src/openvpn/clinat.h
+++ b/src/openvpn/clinat.h
@@ -64,4 +64,7 @@  void client_nat_transform(const struct
client_nat_option_list *list,
                           struct buffer *ipbuf,
                           const int direction);

+bool update_localhost_nat(struct client_nat_option_list *dest, in_addr_t
local_ip);
+
+
 #endif /* if !defined(CLINAT_H) */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index b57e5f8a..dadc10dc 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2052,6 +2052,8 @@  do_open_tun(struct context *c, int *error_flags)
             *error_flags |= (status ? 0 : ISC_ROUTE_ERRORS);
         }

+        update_localhost_nat(c->options.client_nat, c->c1.tuntap->local);
+
         ret = true;
         static_context = c;
     }
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 6b2dfa58..f7161931 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -254,6 +254,7 @@  static const char usage_message[] =
     "                   (Server) Instead of forwarding IPv6 packets send\n"
     "                   ICMPv6 host unreachable packets to the client.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1
NAT rule.\n"
+    "                  set the network to 'localhost' to use the client ip
received from the server.\n"
     "--push-peer-info : (client only) push client info to server.\n"