[Openvpn-devel,07/21] Add helper functions to calculate header/payload sizes

Message ID 20211207170211.3275837-8-arne@rfc2549.org
State Superseded
Headers show
Series Big buffer/frame refactoring patch set | expand

Commit Message

Arne Schwabe Dec. 7, 2021, 6:01 a.m. UTC
These functions are intended to lay the groundwork to later replace
the distributed frame calculations and centralise the calculation in
one place.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
 src/openvpn/crypto.c | 55 ++++++++++++++++++++++++++++++
 src/openvpn/crypto.h | 18 ++++++++++
 src/openvpn/mtu.c    | 80 ++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/mtu.h    | 54 ++++++++++++++++++++++++++++++
 4 files changed, 207 insertions(+)

Comments

Frank Lichtenheld Dec. 9, 2021, 2:10 a.m. UTC | #1
> Arne Schwabe <arne@rfc2549.org> hat am 07.12.2021 18:01 geschrieben:
[...]  
> diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c
> index 0ab716d7a..25b943722 100644
> --- a/src/openvpn/mtu.c
> +++ b/src/openvpn/mtu.c
[...]
> @@ -51,6 +52,85 @@ alloc_buf_sock_tun(struct buffer *buf,
>      ASSERT(buf_safe(buf, 0));
>  }
>  
> +size_t
> +frame_calculate_protocol_header_size(const struct key_type *kt,
> +                                     const struct options *options,
> +                                     unsigned int payload_size,
> +                                     bool occ)
> +{
> +    /* Sum of all the overhead that reduces the usable packet size */
> +    size_t header_size = 0;
> +
> +    /* A socks proxy adds 10 byte of extra header to each packet */

This is only true since we do not support IPv6 UDP over socks. Would it be worth
to point that out?

> +    if (options->ce.socks_proxy_server && proto_is_udp(options->ce.proto))
> +    {
> +        header_size += 10;
> +    }
> +
> +    /* TCP stream based packets have a 16 bit length field */
> +    if (proto_is_tcp(options->ce.proto))
> +    {
> +        header_size += 2;
> +    }
> +
> +    /* Add the opcode and peerid */
> +    header_size += options->use_peer_id ? 4 : 1;
> +
> +    /* Add the crypto overhead */
> +    bool packet_id = options->replay;
> +    bool packet_id_long_form = !tlsmode || cipher_kt_mode_ofb_cfb(kt->cipher);
> +
> +    /* For figuring out the crypto overhead, we need to use the real payload
> +     * including all extra headers that also get encrypted */

That comment sounds like header size should somehow be a input into
calculate_crypto_overhead, which it isn't. Is there an error here or
do I just misunderstand the comment?

> +    header_size += calculate_crypto_overhead(kt, packet_id,
> +                                             packet_id_long_form,
> +                                             payload_size, occ);
> +    return header_size;
> +}
> +
> +
> +size_t
> +frame_calculate_payload_overhead(const struct frame *frame,
> +                                 const struct options *options,
> +                                 bool extra_tun)
> +{
> +    size_t overhead = 0;
> +
> +    /* This is the overhead of tap device that is not included in the MTU itself
> +     * i.e. Ethernet header that we still need to transmit as part of the
> +     * payload*/
> +    if (extra_tun)
> +    {
> +        overhead += frame->extra_tun;
> +    }
> +
> +#if defined(USE_COMP)
> +    /* v1 Compression schemes add 1 byte header. V2 only adds a header when it
> +     * does not increase the packet length. We ignore the unlikely escaping
> +     * for tap here */
> +    if (options->comp.alg == COMP_ALG_LZ4 || options->comp.alg == COMP_ALG_STUB
> +        || options->comp.alg == COMP_ALG_LZO)
> +    {
> +        overhead += 1;
> +    }
> +#endif
> +#if defined(ENABLE_FRAGMENT)
> +    if (options->ce.fragment)
> +    {
> +        overhead += 4;

Every other statement has an explanation of the number, except this one. Maybe you could add
one here as well?

> +    }
> +#endif
> +    return overhead;
> +}
> +
> +size_t
> +frame_calculate_payload_size(const struct frame *frame, const struct options *options)
> +{
> +    size_t payload_size = options->ce.tun_mtu;
> +    payload_size += frame_calculate_payload_overhead(frame, options, true);
> +    return payload_size;
> +}
> +
>  void
>  frame_finalize(struct frame *frame,
>                 bool link_mtu_defined,

Regards,
  Frank

--
Frank Lichtenheld

Patch

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index cd791ab8a..249c4212d 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -667,6 +667,61 @@  openvpn_decrypt(struct buffer *buf, struct buffer work,
     return ret;
 }
 
+unsigned int
+calculate_crypto_overhead(const struct key_type *kt,
+                          bool packet_id,
+                          bool packet_id_long_form,
+                          unsigned int payload_size,
+                          bool occ)
+{
+    unsigned int crypto_overhead = 0;
+
+    /* No encryption */
+    if (packet_id)
+    {
+        crypto_overhead += packet_id_size(packet_id_long_form);
+    }
+
+    if (cipher_kt_mode_aead(kt->cipher))
+    {
+        /* For AEAD ciphers, we basically use a stream cipher/CTR for
+         * the encryption, so no overhead apart from the extra bytes
+         * we add */
+        crypto_overhead += cipher_kt_tag_size(kt->cipher);
+
+        if (occ)
+        {
+            /* the frame calculation of old clients adds these to the link-mtu
+             * even though they are not part of the actual packet */
+            crypto_overhead += cipher_kt_iv_size(kt->cipher);
+            crypto_overhead += cipher_kt_block_size(kt->cipher);
+        }
+    }
+    else
+    {
+        if (cipher_defined(kt->cipher))
+        {
+            /* CBC, OFB or CFB mode */
+            /* This is a worst case upper bound of needing to add
+             * a full extra block for padding when the payload
+             * is exactly a multiple of the block size */
+            if (occ || (cipher_kt_mode_cbc(kt->cipher) &&
+                (payload_size % cipher_kt_block_size(kt->cipher) == 0)))
+            {
+                crypto_overhead += cipher_kt_block_size(kt->cipher);
+            }
+            /* IV is always added (no-iv has been removed a while ago) */
+            crypto_overhead += cipher_kt_iv_size(kt->cipher);
+        }
+        if (md_defined(kt->digest))
+        {
+            crypto_overhead += md_kt_size(kt->digest);
+        }
+    }
+
+    return crypto_overhead;
+}
+
 void
 crypto_adjust_frame_parameters(struct frame *frame,
                                const struct key_type *kt,
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index ad3543c1c..5a67b7ac1 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -415,6 +415,24 @@  void crypto_adjust_frame_parameters(struct frame *frame,
                                     bool packet_id,
                                     bool packet_id_long_form);
 
+/** Calculate the maximum overhead that our encryption has
+ * on a packet. This does not include needed additional buffer size
+ *
+ * @param kt            Struct with the crypto algorithm to use
+ * @param packet_id     Whether packet_id is used
+ * @param packet_id_long_form Whether the packet id has the long form
+ * @param payload_size   payload size, only used if occ is false
+ * @param occ           if true calculates the overhead for crypto in the same
+ *                      incorrect way as all previous OpenVPN versions did, to
+ *                      end up with identical numbers for OCC compatibility
+ */
+unsigned int
+calculate_crypto_overhead(const struct key_type *kt,
+                           bool packet_id,
+                           bool packet_id_long_form,
+                           unsigned int payload_size,
+                           bool occ);
+
 /** Return the worst-case OpenVPN crypto overhead (in bytes) */
 unsigned int crypto_max_overhead(void);
 
diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c
index 0ab716d7a..25b943722 100644
--- a/src/openvpn/mtu.c
+++ b/src/openvpn/mtu.c
@@ -35,6 +35,7 @@ 
 #include "integer.h"
 #include "mtu.h"
 #include "options.h"
+#include "crypto.h"
 
 #include "memdbg.h"
 
@@ -51,6 +52,85 @@  alloc_buf_sock_tun(struct buffer *buf,
     ASSERT(buf_safe(buf, 0));
 }
 
+size_t
+frame_calculate_protocol_header_size(const struct key_type *kt,
+                                     const struct options *options,
+                                     unsigned int payload_size,
+                                     bool occ)
+{
+    /* Sum of all the overhead that reduces the usable packet size */
+    size_t header_size = 0;
+
+    /* A socks proxy adds 10 byte of extra header to each packet */
+    if (options->ce.socks_proxy_server && proto_is_udp(options->ce.proto))
+    {
+        header_size += 10;
+    }
+
+    /* TCP stream based packets have a 16 bit length field */
+    if (proto_is_tcp(options->ce.proto))
+    {
+        header_size += 2;
+    }
+
+    /* Add the opcode and peerid */
+    header_size += options->use_peer_id ? 4 : 1;
+
+    /* Add the crypto overhead */
+    bool packet_id = options->replay;
+    bool packet_id_long_form = !tlsmode || cipher_kt_mode_ofb_cfb(kt->cipher);
+
+    /* For figuring out the crypto overhead, we need to use the real payload
+     * including all extra headers that also get encrypted */
+    header_size += calculate_crypto_overhead(kt, packet_id,
+                                             packet_id_long_form,
+                                             payload_size, occ);
+    return header_size;
+}
+
+
+size_t
+frame_calculate_payload_overhead(const struct frame *frame,
+                                 const struct options *options,
+                                 bool extra_tun)
+{
+    size_t overhead = 0;
+
+    /* This is the overhead of tap device that is not included in the MTU itself
+     * i.e. Ethernet header that we still need to transmit as part of the
+     * payload*/
+    if (extra_tun)
+    {
+        overhead += frame->extra_tun;
+    }
+
+#if defined(USE_COMP)
+    /* v1 Compression schemes add 1 byte header. V2 only adds a header when it
+     * does not increase the packet length. We ignore the unlikely escaping
+     * for tap here */
+    if (options->comp.alg == COMP_ALG_LZ4 || options->comp.alg == COMP_ALG_STUB
+        || options->comp.alg == COMP_ALG_LZO)
+    {
+        overhead += 1;
+    }
+#endif
+#if defined(ENABLE_FRAGMENT)
+    if (options->ce.fragment)
+    {
+        overhead += 4;
+    }
+#endif
+    return overhead;
+}
+
+size_t
+frame_calculate_payload_size(const struct frame *frame, const struct options *options)
+{
+    size_t payload_size = options->ce.tun_mtu;
+    payload_size += frame_calculate_payload_overhead(frame, options, true);
+    return payload_size;
+}
+
 void
 frame_finalize(struct frame *frame,
                bool link_mtu_defined,
diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h
index c1148c317..5ad0931fd 100644
--- a/src/openvpn/mtu.h
+++ b/src/openvpn/mtu.h
@@ -221,6 +221,60 @@  void set_mtu_discover_type(socket_descriptor_t sd, int mtu_type, sa_family_t pro
 
 int translate_mtu_discover_type_name(const char *name);
 
+/**
+ * Calculates the size of the payload according to tun-mtu and tap overhead.
+ * This also includes compression and fragmentation overhead if they are
+ * enabled.
+ *
+ * *  [IP][UDP][OPENVPN PROTOCOL HEADER][ **PAYLOAD incl compression header** ]
+ * @param frame
+ * @param options
+ * @return
+ */
+size_t
+frame_calculate_payload_size(const struct frame *frame,
+                             const struct options *options);
+
+/**
+ * Calculates the size of the payload overhead according to tun-mtu and
+ * tap overhead. This all the overhead that is considered part of the payload
+ * itself. The compression and fragmentation header and extra header from tap
+ * are considered part of this overhead that increases the payload larger than
+ * tun-mtu.
+ *
+ * *  [IP][UDP][OPENVPN PROTOCOL HEADER][ **PAYLOAD incl compression header** ]
+ * @param frame
+ * @param options
+ * @param extra_tun
+ * @return
+ */
+size_t
+frame_calculate_payload_overhead(const struct frame *frame,
+                                 const struct options *options,
+                                 bool extra_tun);
+
+/* forward declaration of key_type */
+struct key_type;
+
+/**
+ * Calculates the size of the OpenVPN protocol header. This includes
+ * the crypto IV/tag/HMAC but does not include the IP encapsulation
+ *
+ *
+ *  [IP][UDP][ **OPENVPN PROTOCOL HEADER**][PAYLOAD incl compression header]
+ *
+ * @param kt            the key_type to use to calculate the crypto overhead
+ * @param options       the options struct to be used to calculate
+ * @param payload_size  the payload size, ignored if occ is true
+ * @param occ           if the calculation should be done for occ compatibility
+ * @return              size of the overhead in bytes
+ */
+size_t
+frame_calculate_protocol_header_size(const struct key_type *kt,
+                                     const struct options *options,
+                                     unsigned int payload_size,
+                                     bool occ);
+
 /*
  * frame_set_mtu_dynamic and flags
  */