diff mbox series

[ovs-dev] ovs-monitor-ipsec: Add force-encapsulation option to force NAT-T

Message ID 20220120153312.989590-1-ak.karis@gmail.com
State Superseded
Headers show
Series [ovs-dev] ovs-monitor-ipsec: Add force-encapsulation option to force NAT-T | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed

Commit Message

Andreas Karis Jan. 20, 2022, 3:33 p.m. UTC
Both LibreSwan and OpenSwan allow administrators to unconditionally
force enable NAT-T for ESP. This may help to surmount restrictive
firewalls in scenarios where IP protocol number 50 is blocked, but where
NAT autodetection fails. Add a switch --force-encapsulation to expose
this feature to users of ovs-monitor-ipsec

Signed-off-by: Andreas Karis <ak.karis@gmail.com>
---
 ipsec/ovs-monitor-ipsec.in | 29 +++++++++++++++++++++--------
 utilities/ovs-ctl.in       |  7 +++++++
 2 files changed, 28 insertions(+), 8 deletions(-)

Comments

Mike Pattrick Feb. 25, 2022, 9:17 p.m. UTC | #1
On Thu, 2022-01-20 at 16:33 +0100, Andreas Karis wrote:
> Both LibreSwan and OpenSwan allow administrators to unconditionally
> force enable NAT-T for ESP. This may help to surmount restrictive
> firewalls in scenarios where IP protocol number 50 is blocked, but
> where
> NAT autodetection fails. Add a switch --force-encapsulation to expose
> this feature to users of ovs-monitor-ipsec
> 
> Signed-off-by: Andreas Karis <ak.karis@gmail.com>


Hello Andreas,

Looks great, but a few of the lines stretch past the max column length.

What do you think about this modification:

diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index c34a7acc0..b403ba559 100755
--- a/ipsec/ovs-monitor-ipsec.in
+++ b/ipsec/ovs-monitor-ipsec.in
@@ -279,7 +279,8 @@ conn prevent_unencrypted_vxlan
     def config_init(self):
         self.conf_file = open(self.IPSEC_CONF, "w")
         self.secrets_file = open(self.IPSEC_SECRETS, "w")
-        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
+        self.conf_file.write(self.CONF_HEADER %
+                            (FILE_HEADER, self.extra_params))
         self.secrets_file.write(FILE_HEADER)
 
     def config_global(self, monitor):
@@ -495,7 +496,8 @@ conn prevent_unencrypted_vxlan
     def config_init(self):
         self.conf_file = open(self.IPSEC_CONF, "w")
         self.secrets_file = open(self.IPSEC_SECRETS, "w")
-        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
+        self.conf_file.write(self.CONF_HEADER %
+                            (FILE_HEADER, self.extra_params))
         self.secrets_file.write(FILE_HEADER)
 
     def config_global(self, monitor):



> ---
>  ipsec/ovs-monitor-ipsec.in | 29 +++++++++++++++++++++--------
>  utilities/ovs-ctl.in       |  7 +++++++
>  2 files changed, 28 insertions(+), 8 deletions(-)
> 
> diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
> index 89a36fe17..3421adcdb 100755
> --- a/ipsec/ovs-monitor-ipsec.in
> +++ b/ipsec/ovs-monitor-ipsec.in
> @@ -171,8 +171,9 @@ conn %%default
>      auto=route
>      ike=aes256gcm16-sha256-modp2048
>      esp=aes256gcm16-modp2048
> +    %s
>  
> -""" % (FILE_HEADER)
> +"""
>  
>      CA_SECTION = """ca ca_auth
>      cacert=%s
> @@ -219,13 +220,17 @@ conn prevent_unencrypted_vxlan
>      rightid=$remote_name
>      leftcert=$certificate""")}
>  
> -    def __init__(self, root_prefix):
> +    def __init__(self, root_prefix, args):
>          self.CHARON_CONF = root_prefix +
> "/etc/strongswan.d/ovs.conf"
>          self.IPSEC = root_prefix + "/usr/sbin/ipsec"
>          self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"
>          self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"
>          self.conf_file = None
>          self.secrets_file = None
> +        if args.force_encapsulation:
> +            self.extra_params = "forceencaps=yes"
> +        else:
> +            self.extra_params = ""
>  
>      def restart_ike_daemon(self):
>          """This function restarts StrongSwan."""
> @@ -234,7 +239,7 @@ conn prevent_unencrypted_vxlan
>          f.close()
>  
>          f = open(self.IPSEC_CONF, "w")
> -        f.write(self.CONF_HEADER)
> +        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
>          f.close()
>  
>          f = open(self.IPSEC_SECRETS, "w")
> @@ -274,7 +279,7 @@ conn prevent_unencrypted_vxlan
>      def config_init(self):
>          self.conf_file = open(self.IPSEC_CONF, "w")
>          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> -        self.conf_file.write(self.CONF_HEADER)
> +        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> self.extra_params))
>          self.secrets_file.write(FILE_HEADER)
>  
>      def config_global(self, monitor):
> @@ -387,8 +392,9 @@ conn %%default
>      ike=aes_gcm256-sha2_256
>      esp=aes_gcm256
>      ikev2=insist
> +    %s
>  
> -""" % (FILE_HEADER)
> +"""
>  
>      SHUNT_POLICY = """conn prevent_unencrypted_gre
>      type=drop
> @@ -452,6 +458,10 @@ conn prevent_unencrypted_vxlan
>                          else "/etc/ipsec.secrets")
>          ipsec_ctl = (args.ipsec_ctl if args.ipsec_ctl
>                          else "/run/pluto/pluto.ctl")
> +        if args.force_encapsulation:
> +            self.extra_params = "encapsulation=yes"
> +        else:
> +            self.extra_params = ""
>  
>          self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec"
>          self.IPSEC_CONF = libreswan_root_prefix + ipsec_conf
> @@ -472,7 +482,7 @@ conn prevent_unencrypted_vxlan
>          self._nss_clear_database()
>  
>          f = open(self.IPSEC_CONF, "w")
> -        f.write(self.CONF_HEADER)
> +        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
>          f.close()
>  
>          f = open(self.IPSEC_SECRETS, "w")
> @@ -485,7 +495,7 @@ conn prevent_unencrypted_vxlan
>      def config_init(self):
>          self.conf_file = open(self.IPSEC_CONF, "w")
>          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> -        self.conf_file.write(self.CONF_HEADER)
> +        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> self.extra_params))
>          self.secrets_file.write(FILE_HEADER)
>  
>      def config_global(self, monitor):
> @@ -1012,7 +1022,7 @@ class IPsecMonitor(object):
>  
>          # Choose to either use StrongSwan or LibreSwan as IKE daemon
>          if ike_daemon == "strongswan":
> -            self.ike_helper = StrongSwanHelper(root_prefix)
> +            self.ike_helper = StrongSwanHelper(root_prefix, args)
>          elif ike_daemon == "libreswan":
>              self.ike_helper = LibreSwanHelper(root_prefix, args)
>          else:
> @@ -1284,6 +1294,9 @@ def main():
>      parser.add_argument("--ipsec-ctl", metavar="IPSEC-CTL",
>                          help="Use DIR/IPSEC-CTL as location for "
>                          " pluto ctl socket (libreswan only).")
> +    parser.add_argument("--force-encapsulation",
> action='store_true',
> +                        help="Unconditionally enable ESP NAT-T
> encapsulation."
> +                        " (either libreswan or strongswan).")
>  
>      ovs.vlog.add_args(parser)
>      ovs.daemon.add_args(parser)
> diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in
> index e6e07f476..deb715ae5 100644
> --- a/utilities/ovs-ctl.in
> +++ b/utilities/ovs-ctl.in
> @@ -240,11 +240,15 @@ start_ovs_ipsec () {
>      if test X$RESTART_IKE_DAEMON = Xno; then
>          no_restart="--no-restart-ike-daemon"
>      fi
> +    if test X$FORCE_ENCAPSULATION = Xyes; then
> +        force_encapsulation="--force-encapsulation"
> +    fi
>  
>      ${datadir}/scripts/ovs-monitor-ipsec \
>          --pidfile=${rundir}/ovs-monitor-ipsec.pid \
>          --ike-daemon=$IKE_DAEMON \
>          $no_restart \
> +        $force_encapsulation \
>          --log-file --detach --monitor unix:${rundir}/db.sock ||
> return 1
>      return 0
>  }
> @@ -354,6 +358,7 @@ set_defaults () {
>  
>      IKE_DAEMON=
>      RESTART_IKE_DAEMON=yes
> +    FORCE_ENCAPSULATION=no
>  
>      type_file=$etcdir/system-type.conf
>      version_file=$etcdir/system-version.conf
> @@ -448,6 +453,8 @@ Option for "start-ovs-ipsec":
>        the IKE daemon for ipsec tunnels (either libreswan or
> strongswan)
>    --no-restart-ike-daemon
>        do not restart the IKE daemon on startup
> +  --force-encapsulation
> +      Unconditionally force ESP NAT-T (ESP over udp/4500)
>  
>  Other options:
>    -h, --help                  display this help message
Andreas Karis March 1, 2022, 7:03 p.m. UTC | #2
Thank you, I posted a new version.

On Fri, Feb 25, 2022 at 10:17 PM Mike Pattrick <mkp@redhat.com> wrote:

> On Thu, 2022-01-20 at 16:33 +0100, Andreas Karis wrote:
> > Both LibreSwan and OpenSwan allow administrators to unconditionally
> > force enable NAT-T for ESP. This may help to surmount restrictive
> > firewalls in scenarios where IP protocol number 50 is blocked, but
> > where
> > NAT autodetection fails. Add a switch --force-encapsulation to expose
> > this feature to users of ovs-monitor-ipsec
> >
> > Signed-off-by: Andreas Karis <ak.karis@gmail.com>
>
>
> Hello Andreas,
>
> Looks great, but a few of the lines stretch past the max column length.
>
> What do you think about this modification:
>
> diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
> index c34a7acc0..b403ba559 100755
> --- a/ipsec/ovs-monitor-ipsec.in
> +++ b/ipsec/ovs-monitor-ipsec.in
> @@ -279,7 +279,8 @@ conn prevent_unencrypted_vxlan
>      def config_init(self):
>          self.conf_file = open(self.IPSEC_CONF, "w")
>          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> -        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> self.extra_params))
> +        self.conf_file.write(self.CONF_HEADER %
> +                            (FILE_HEADER, self.extra_params))
>          self.secrets_file.write(FILE_HEADER)
>
>      def config_global(self, monitor):
> @@ -495,7 +496,8 @@ conn prevent_unencrypted_vxlan
>      def config_init(self):
>          self.conf_file = open(self.IPSEC_CONF, "w")
>          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> -        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> self.extra_params))
> +        self.conf_file.write(self.CONF_HEADER %
> +                            (FILE_HEADER, self.extra_params))
>          self.secrets_file.write(FILE_HEADER)
>
>      def config_global(self, monitor):
>
>
>
> > ---
> >  ipsec/ovs-monitor-ipsec.in | 29 +++++++++++++++++++++--------
> >  utilities/ovs-ctl.in       |  7 +++++++
> >  2 files changed, 28 insertions(+), 8 deletions(-)
> >
> > diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
> > index 89a36fe17..3421adcdb 100755
> > --- a/ipsec/ovs-monitor-ipsec.in
> > +++ b/ipsec/ovs-monitor-ipsec.in
> > @@ -171,8 +171,9 @@ conn %%default
> >      auto=route
> >      ike=aes256gcm16-sha256-modp2048
> >      esp=aes256gcm16-modp2048
> > +    %s
> >
> > -""" % (FILE_HEADER)
> > +"""
> >
> >      CA_SECTION = """ca ca_auth
> >      cacert=%s
> > @@ -219,13 +220,17 @@ conn prevent_unencrypted_vxlan
> >      rightid=$remote_name
> >      leftcert=$certificate""")}
> >
> > -    def __init__(self, root_prefix):
> > +    def __init__(self, root_prefix, args):
> >          self.CHARON_CONF = root_prefix +
> > "/etc/strongswan.d/ovs.conf"
> >          self.IPSEC = root_prefix + "/usr/sbin/ipsec"
> >          self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"
> >          self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"
> >          self.conf_file = None
> >          self.secrets_file = None
> > +        if args.force_encapsulation:
> > +            self.extra_params = "forceencaps=yes"
> > +        else:
> > +            self.extra_params = ""
> >
> >      def restart_ike_daemon(self):
> >          """This function restarts StrongSwan."""
> > @@ -234,7 +239,7 @@ conn prevent_unencrypted_vxlan
> >          f.close()
> >
> >          f = open(self.IPSEC_CONF, "w")
> > -        f.write(self.CONF_HEADER)
> > +        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
> >          f.close()
> >
> >          f = open(self.IPSEC_SECRETS, "w")
> > @@ -274,7 +279,7 @@ conn prevent_unencrypted_vxlan
> >      def config_init(self):
> >          self.conf_file = open(self.IPSEC_CONF, "w")
> >          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> > -        self.conf_file.write(self.CONF_HEADER)
> > +        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> > self.extra_params))
> >          self.secrets_file.write(FILE_HEADER)
> >
> >      def config_global(self, monitor):
> > @@ -387,8 +392,9 @@ conn %%default
> >      ike=aes_gcm256-sha2_256
> >      esp=aes_gcm256
> >      ikev2=insist
> > +    %s
> >
> > -""" % (FILE_HEADER)
> > +"""
> >
> >      SHUNT_POLICY = """conn prevent_unencrypted_gre
> >      type=drop
> > @@ -452,6 +458,10 @@ conn prevent_unencrypted_vxlan
> >                          else "/etc/ipsec.secrets")
> >          ipsec_ctl = (args.ipsec_ctl if args.ipsec_ctl
> >                          else "/run/pluto/pluto.ctl")
> > +        if args.force_encapsulation:
> > +            self.extra_params = "encapsulation=yes"
> > +        else:
> > +            self.extra_params = ""
> >
> >          self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec"
> >          self.IPSEC_CONF = libreswan_root_prefix + ipsec_conf
> > @@ -472,7 +482,7 @@ conn prevent_unencrypted_vxlan
> >          self._nss_clear_database()
> >
> >          f = open(self.IPSEC_CONF, "w")
> > -        f.write(self.CONF_HEADER)
> > +        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
> >          f.close()
> >
> >          f = open(self.IPSEC_SECRETS, "w")
> > @@ -485,7 +495,7 @@ conn prevent_unencrypted_vxlan
> >      def config_init(self):
> >          self.conf_file = open(self.IPSEC_CONF, "w")
> >          self.secrets_file = open(self.IPSEC_SECRETS, "w")
> > -        self.conf_file.write(self.CONF_HEADER)
> > +        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER,
> > self.extra_params))
> >          self.secrets_file.write(FILE_HEADER)
> >
> >      def config_global(self, monitor):
> > @@ -1012,7 +1022,7 @@ class IPsecMonitor(object):
> >
> >          # Choose to either use StrongSwan or LibreSwan as IKE daemon
> >          if ike_daemon == "strongswan":
> > -            self.ike_helper = StrongSwanHelper(root_prefix)
> > +            self.ike_helper = StrongSwanHelper(root_prefix, args)
> >          elif ike_daemon == "libreswan":
> >              self.ike_helper = LibreSwanHelper(root_prefix, args)
> >          else:
> > @@ -1284,6 +1294,9 @@ def main():
> >      parser.add_argument("--ipsec-ctl", metavar="IPSEC-CTL",
> >                          help="Use DIR/IPSEC-CTL as location for "
> >                          " pluto ctl socket (libreswan only).")
> > +    parser.add_argument("--force-encapsulation",
> > action='store_true',
> > +                        help="Unconditionally enable ESP NAT-T
> > encapsulation."
> > +                        " (either libreswan or strongswan).")
> >
> >      ovs.vlog.add_args(parser)
> >      ovs.daemon.add_args(parser)
> > diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in
> > index e6e07f476..deb715ae5 100644
> > --- a/utilities/ovs-ctl.in
> > +++ b/utilities/ovs-ctl.in
> > @@ -240,11 +240,15 @@ start_ovs_ipsec () {
> >      if test X$RESTART_IKE_DAEMON = Xno; then
> >          no_restart="--no-restart-ike-daemon"
> >      fi
> > +    if test X$FORCE_ENCAPSULATION = Xyes; then
> > +        force_encapsulation="--force-encapsulation"
> > +    fi
> >
> >      ${datadir}/scripts/ovs-monitor-ipsec \
> >          --pidfile=${rundir}/ovs-monitor-ipsec.pid \
> >          --ike-daemon=$IKE_DAEMON \
> >          $no_restart \
> > +        $force_encapsulation \
> >          --log-file --detach --monitor unix:${rundir}/db.sock ||
> > return 1
> >      return 0
> >  }
> > @@ -354,6 +358,7 @@ set_defaults () {
> >
> >      IKE_DAEMON=
> >      RESTART_IKE_DAEMON=yes
> > +    FORCE_ENCAPSULATION=no
> >
> >      type_file=$etcdir/system-type.conf
> >      version_file=$etcdir/system-version.conf
> > @@ -448,6 +453,8 @@ Option for "start-ovs-ipsec":
> >        the IKE daemon for ipsec tunnels (either libreswan or
> > strongswan)
> >    --no-restart-ike-daemon
> >        do not restart the IKE daemon on startup
> > +  --force-encapsulation
> > +      Unconditionally force ESP NAT-T (ESP over udp/4500)
> >
> >  Other options:
> >    -h, --help                  display this help message
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
diff mbox series

Patch

diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index 89a36fe17..3421adcdb 100755
--- a/ipsec/ovs-monitor-ipsec.in
+++ b/ipsec/ovs-monitor-ipsec.in
@@ -171,8 +171,9 @@  conn %%default
     auto=route
     ike=aes256gcm16-sha256-modp2048
     esp=aes256gcm16-modp2048
+    %s
 
-""" % (FILE_HEADER)
+"""
 
     CA_SECTION = """ca ca_auth
     cacert=%s
@@ -219,13 +220,17 @@  conn prevent_unencrypted_vxlan
     rightid=$remote_name
     leftcert=$certificate""")}
 
-    def __init__(self, root_prefix):
+    def __init__(self, root_prefix, args):
         self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"
         self.IPSEC = root_prefix + "/usr/sbin/ipsec"
         self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"
         self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"
         self.conf_file = None
         self.secrets_file = None
+        if args.force_encapsulation:
+            self.extra_params = "forceencaps=yes"
+        else:
+            self.extra_params = ""
 
     def restart_ike_daemon(self):
         """This function restarts StrongSwan."""
@@ -234,7 +239,7 @@  conn prevent_unencrypted_vxlan
         f.close()
 
         f = open(self.IPSEC_CONF, "w")
-        f.write(self.CONF_HEADER)
+        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
         f.close()
 
         f = open(self.IPSEC_SECRETS, "w")
@@ -274,7 +279,7 @@  conn prevent_unencrypted_vxlan
     def config_init(self):
         self.conf_file = open(self.IPSEC_CONF, "w")
         self.secrets_file = open(self.IPSEC_SECRETS, "w")
-        self.conf_file.write(self.CONF_HEADER)
+        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
         self.secrets_file.write(FILE_HEADER)
 
     def config_global(self, monitor):
@@ -387,8 +392,9 @@  conn %%default
     ike=aes_gcm256-sha2_256
     esp=aes_gcm256
     ikev2=insist
+    %s
 
-""" % (FILE_HEADER)
+"""
 
     SHUNT_POLICY = """conn prevent_unencrypted_gre
     type=drop
@@ -452,6 +458,10 @@  conn prevent_unencrypted_vxlan
                         else "/etc/ipsec.secrets")
         ipsec_ctl = (args.ipsec_ctl if args.ipsec_ctl
                         else "/run/pluto/pluto.ctl")
+        if args.force_encapsulation:
+            self.extra_params = "encapsulation=yes"
+        else:
+            self.extra_params = ""
 
         self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec"
         self.IPSEC_CONF = libreswan_root_prefix + ipsec_conf
@@ -472,7 +482,7 @@  conn prevent_unencrypted_vxlan
         self._nss_clear_database()
 
         f = open(self.IPSEC_CONF, "w")
-        f.write(self.CONF_HEADER)
+        f.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
         f.close()
 
         f = open(self.IPSEC_SECRETS, "w")
@@ -485,7 +495,7 @@  conn prevent_unencrypted_vxlan
     def config_init(self):
         self.conf_file = open(self.IPSEC_CONF, "w")
         self.secrets_file = open(self.IPSEC_SECRETS, "w")
-        self.conf_file.write(self.CONF_HEADER)
+        self.conf_file.write(self.CONF_HEADER % (FILE_HEADER, self.extra_params))
         self.secrets_file.write(FILE_HEADER)
 
     def config_global(self, monitor):
@@ -1012,7 +1022,7 @@  class IPsecMonitor(object):
 
         # Choose to either use StrongSwan or LibreSwan as IKE daemon
         if ike_daemon == "strongswan":
-            self.ike_helper = StrongSwanHelper(root_prefix)
+            self.ike_helper = StrongSwanHelper(root_prefix, args)
         elif ike_daemon == "libreswan":
             self.ike_helper = LibreSwanHelper(root_prefix, args)
         else:
@@ -1284,6 +1294,9 @@  def main():
     parser.add_argument("--ipsec-ctl", metavar="IPSEC-CTL",
                         help="Use DIR/IPSEC-CTL as location for "
                         " pluto ctl socket (libreswan only).")
+    parser.add_argument("--force-encapsulation", action='store_true',
+                        help="Unconditionally enable ESP NAT-T encapsulation."
+                        " (either libreswan or strongswan).")
 
     ovs.vlog.add_args(parser)
     ovs.daemon.add_args(parser)
diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in
index e6e07f476..deb715ae5 100644
--- a/utilities/ovs-ctl.in
+++ b/utilities/ovs-ctl.in
@@ -240,11 +240,15 @@  start_ovs_ipsec () {
     if test X$RESTART_IKE_DAEMON = Xno; then
         no_restart="--no-restart-ike-daemon"
     fi
+    if test X$FORCE_ENCAPSULATION = Xyes; then
+        force_encapsulation="--force-encapsulation"
+    fi
 
     ${datadir}/scripts/ovs-monitor-ipsec \
         --pidfile=${rundir}/ovs-monitor-ipsec.pid \
         --ike-daemon=$IKE_DAEMON \
         $no_restart \
+        $force_encapsulation \
         --log-file --detach --monitor unix:${rundir}/db.sock || return 1
     return 0
 }
@@ -354,6 +358,7 @@  set_defaults () {
 
     IKE_DAEMON=
     RESTART_IKE_DAEMON=yes
+    FORCE_ENCAPSULATION=no
 
     type_file=$etcdir/system-type.conf
     version_file=$etcdir/system-version.conf
@@ -448,6 +453,8 @@  Option for "start-ovs-ipsec":
       the IKE daemon for ipsec tunnels (either libreswan or strongswan)
   --no-restart-ike-daemon
       do not restart the IKE daemon on startup
+  --force-encapsulation
+      Unconditionally force ESP NAT-T (ESP over udp/4500)
 
 Other options:
   -h, --help                  display this help message