]> git.proxmox.com Git - mirror_kronosnet.git/commitdiff
[PMTUd] add ability to manually override MTU and disable PMTUd
authorFabio M. Di Nitto <fdinitto@redhat.com>
Tue, 20 Aug 2019 04:57:45 +0000 (06:57 +0200)
committerFabio M. Di Nitto <fdinitto@redhat.com>
Tue, 20 Aug 2019 04:57:45 +0000 (06:57 +0200)
Signed-off-by: Fabio M. Di Nitto <fdinitto@redhat.com>
libknet/handle.c
libknet/internals.h
libknet/libknet.h
libknet/tests/api-check.mk
libknet/tests/api_knet_handle_pmtud_set.c [new file with mode: 0644]
libknet/threads_pmtud.c
man/Makefile.am

index 3cfc1e5fb3fec569247271a098bcd43a15d9ef4c..fcb129a7cdb4a30fc87b7b2a492067b125e6c554 100644 (file)
@@ -1360,6 +1360,41 @@ int knet_handle_enable_pmtud_notify(knet_handle_t knet_h,
        return 0;
 }
 
+int knet_handle_pmtud_set(knet_handle_t knet_h,
+                         unsigned int iface_mtu)
+{
+       int savederrno = 0;
+
+       if (!knet_h) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (iface_mtu > KNET_PMTUD_SIZE_V4) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
+       if (savederrno) {
+               log_err(knet_h, KNET_SUB_PMTUD, "Unable to get read lock: %s",
+                       strerror(savederrno));
+               errno = savederrno;
+               return -1;
+       }
+
+       log_info(knet_h, KNET_SUB_PMTUD, "MTU manually set to: %u", iface_mtu);
+
+       knet_h->manual_mtu = iface_mtu;
+
+       force_pmtud_run(knet_h, KNET_SUB_PMTUD, 0);
+
+       pthread_rwlock_unlock(&knet_h->global_rwlock);
+
+       errno = 0;
+       return 0;
+}
+
 int knet_handle_pmtud_get(knet_handle_t knet_h,
                          unsigned int *data_mtu)
 {
index 82c3d8195e0873b18bd117ed611f004391037ded..3911b84781770c01a1c170e528b16ff1167b3d3c 100644 (file)
@@ -166,6 +166,7 @@ struct knet_handle {
        int dst_link_handler_epollfd;
        uint8_t use_access_lists; /* set to 0 for disable, 1 for enable */
        unsigned int pmtud_interval;
+       unsigned int manual_mtu;
        unsigned int data_mtu;  /* contains the max data size that we can send onwire
                                 * without frags */
        struct knet_host *host_head;
index 0fc26d15123f437022d3cdc726544e3ceff87dee..68724db39a862b5a66da375942e29a5b7f7a5307 100644 (file)
@@ -663,6 +663,32 @@ int knet_handle_enable_pmtud_notify(knet_handle_t knet_h,
                                                void *private_data,
                                                unsigned int data_mtu));
 
+/**
+ * knet_handle_pmtud_set
+ *
+ * @brief Set the current interface MTU
+ *
+ * knet_h    - pointer to knet_handle_t
+ *
+ * iface_mtu - current interface MTU, value 0 to 65535. 0 will
+ *             re-enable automatic MTU discovery.
+ *             In a setup with multiple interfaces, please specify
+ *             the lowest MTU between the selected intefaces.
+ *             knet will automatically adjust this value for
+ *             all headers overhead and set the correct data_mtu.
+ *             data_mtu can be retrivied with knet_handle_pmtud_get(3)
+ *             or applications will receive a pmtud_nofity event
+ *             if enabled via knet_handle_enable_pmtud_notify(3).
+ *
+ * @return
+ * knet_handle_pmtud_set returns
+ * 0 on success
+ * -1 on error and errno is set.
+ */
+
+int knet_handle_pmtud_set(knet_handle_t knet_h,
+                         unsigned int iface_mtu);
+
 /**
  * knet_handle_pmtud_get
  *
index a632451715f256906deef4c96eb4d291691919ca..6c17699c619280be4e6f42bc4432155a42e5201d 100644 (file)
@@ -38,6 +38,7 @@ api_checks            = \
                          api_knet_handle_pmtud_getfreq_test \
                          api_knet_handle_enable_pmtud_notify_test \
                          api_knet_handle_pmtud_get_test \
+                         api_knet_handle_pmtud_set_test \
                          api_knet_host_add_test \
                          api_knet_host_remove_test \
                          api_knet_host_set_name_test \
@@ -173,6 +174,9 @@ api_knet_handle_enable_pmtud_notify_test_SOURCES = api_knet_handle_enable_pmtud_
 api_knet_handle_pmtud_get_test_SOURCES = api_knet_handle_pmtud_get.c \
                                         test-common.c
 
+api_knet_handle_pmtud_set_test_SOURCES = api_knet_handle_pmtud_set.c \
+                                        test-common.c
+
 api_knet_host_add_test_SOURCES = api_knet_host_add.c \
                                 test-common.c
 
diff --git a/libknet/tests/api_knet_handle_pmtud_set.c b/libknet/tests/api_knet_handle_pmtud_set.c
new file mode 100644 (file)
index 0000000..7a7ffb3
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2016-2019 Red Hat, Inc.  All rights reserved.
+ *
+ * Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
+ *
+ * This software licensed under GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libknet.h"
+
+#include "internals.h"
+#include "test-common.h"
+
+static int private_data;
+
+static void sock_notify(void *pvt_data,
+                       int datafd,
+                       int8_t channel,
+                       uint8_t tx_rx,
+                       int error,
+                       int errorno)
+{
+       return;
+}
+
+static void test(void)
+{
+       knet_handle_t knet_h;
+       int logfds[2];
+       unsigned int iface_mtu = 0, data_mtu;
+       int datafd = 0;
+       int8_t channel = 0;
+       struct sockaddr_storage lo;
+
+       if (make_local_sockaddr(&lo, 0) < 0) {
+               printf("Unable to convert loopback to sockaddr: %s\n", strerror(errno));
+               exit(FAIL);
+       }
+
+       printf("Test knet_handle_pmtud_set incorrect knet_h\n");
+
+       if ((!knet_handle_pmtud_set(NULL, iface_mtu)) || (errno != EINVAL)) {
+               printf("knet_handle_pmtud_set accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno));
+               exit(FAIL);
+       }
+
+       setup_logpipes(logfds);
+
+       knet_h = knet_handle_start(logfds, KNET_LOG_DEBUG);
+
+       flush_logs(logfds[0], stdout);
+
+       iface_mtu = KNET_PMTUD_SIZE_V4 + 1;
+       printf("Test knet_handle_pmtud_set with wrong iface_mtu\n");
+       if ((!knet_handle_pmtud_set(knet_h, iface_mtu)) || (errno != EINVAL)) {
+               printf("knet_handle_pmtud_set accepted invalid data_mtu or returned incorrect error: %s\n", strerror(errno));
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (knet_handle_enable_sock_notify(knet_h, &private_data, sock_notify) < 0) {
+               printf("knet_handle_enable_sock_notify failed: %s\n", strerror(errno));
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+        }
+
+       datafd = 0;
+       channel = -1;
+
+       if (knet_handle_add_datafd(knet_h, &datafd, &channel) < 0) {
+               printf("knet_handle_add_datafd failed: %s\n", strerror(errno));
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (knet_host_add(knet_h, 1) < 0) {
+               printf("knet_host_add failed: %s\n", strerror(errno));
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (knet_link_set_config(knet_h, 1, 0, KNET_TRANSPORT_UDP, &lo, &lo, 0) < 0) {
+               printf("Unable to configure link: %s\n", strerror(errno));
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (knet_link_set_pong_count(knet_h, 1, 0, 1) < 0) {
+               printf("knet_link_set_pong_count failed: %s\n", strerror(errno));
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (knet_link_set_enable(knet_h, 1, 0, 1) < 0) {
+               printf("knet_link_set_enable failed: %s\n", strerror(errno));
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       if (wait_for_host(knet_h, 1, 4, logfds[0], stdout) < 0) {
+               printf("timeout waiting for host to be reachable");
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       flush_logs(logfds[0], stdout);
+
+       if (knet_handle_pmtud_get(knet_h, &data_mtu) < 0) {
+               printf("knet_handle_pmtud_get failed error: %s\n", strerror(errno));
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       /*
+        * 28 = IP (20) + UDP (8)
+        */
+       iface_mtu = data_mtu + 28 + KNET_HEADER_ALL_SIZE - 64;
+       printf("Test knet_handle_pmtud_set with iface_mtu %u\n", iface_mtu);
+
+       if (knet_handle_pmtud_set(knet_h, iface_mtu) < 0) {
+               printf("knet_handle_pmtud_set failed error: %s\n", strerror(errno));
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       /*
+        * wait for PMTUd to pick up the change
+        */
+       sleep(1);
+       flush_logs(logfds[0], stdout);
+
+       if (knet_h->data_mtu != data_mtu - 64) {
+               printf("knet_handle_pmtud_set failed to set the value\n");
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       printf("Test knet_handle_pmtud_set with iface_mtu 0\n");
+       if (knet_handle_pmtud_set(knet_h, 0) < 0) {
+               printf("knet_handle_pmtud_set failed error: %s\n", strerror(errno));
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       /*
+        * wait for PMTUd to pick up the change
+        */
+       sleep(1);
+       flush_logs(logfds[0], stdout);
+
+       if (knet_h->data_mtu != data_mtu) {
+               printf("knet_handle_pmtud_set failed to redetect MTU\n");
+               knet_link_set_enable(knet_h, 1, 0, 0);
+               knet_link_clear_config(knet_h, 1, 0);
+               knet_host_remove(knet_h, 1);
+               knet_handle_free(knet_h);
+               flush_logs(logfds[0], stdout);
+               close_logpipes(logfds);
+               exit(FAIL);
+       }
+
+       knet_link_set_enable(knet_h, 1, 0, 0);
+       knet_link_clear_config(knet_h, 1, 0);
+       knet_host_remove(knet_h, 1);
+       knet_handle_free(knet_h);
+       flush_logs(logfds[0], stdout);
+       close_logpipes(logfds);
+}
+
+int main(int argc, char *argv[])
+{
+       test();
+
+       return PASS;
+}
index 54aa15272218939ae2928d9ab8bbc56735369ad9..d22eb2ffc20503eeeaad62c6e9e68d4e779d4fdb 100644 (file)
 #include "threads_common.h"
 #include "threads_pmtud.h"
 
+static int _calculate_manual_mtu(knet_handle_t knet_h, struct knet_link *dst_link)
+{
+       size_t ipproto_overhead_len;    /* onwire packet overhead (protocol based) */
+
+       switch (dst_link->dst_addr.ss_family) {
+               case AF_INET6:
+                       ipproto_overhead_len = KNET_PMTUD_OVERHEAD_V6 + dst_link->proto_overhead;
+                       break;
+               case AF_INET:
+                       ipproto_overhead_len = KNET_PMTUD_OVERHEAD_V4 + dst_link->proto_overhead;
+                       break;
+               default:
+                       log_debug(knet_h, KNET_SUB_PMTUD, "unknown protocol");
+                       return 0;
+                       break;
+       }
+
+       dst_link->status.mtu = calc_max_data_outlen(knet_h, knet_h->manual_mtu - ipproto_overhead_len);
+
+       return 1;
+}
+
 static int _handle_check_link_pmtud(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_link *dst_link)
 {
        int err, ret, savederrno, mutex_retry_limit, failsafe, use_kernel_mtu, warn_once;
@@ -544,14 +566,24 @@ void *_handle_pmtud_link_thread(void *data)
                                     (dst_link->status.dynconnected != 1)))
                                        continue;
 
-                               link_has_mtu = _handle_check_pmtud(knet_h, dst_host, dst_link, force_run);
-                               if (errno == EDEADLK) {
-                                       goto out_unlock;
-                               }
-                               if (link_has_mtu) {
-                                       have_mtu = 1;
-                                       if (dst_link->status.mtu < lower_mtu) {
-                                               lower_mtu = dst_link->status.mtu;
+                               if (!knet_h->manual_mtu) {
+                                       link_has_mtu = _handle_check_pmtud(knet_h, dst_host, dst_link, force_run);
+                                       if (errno == EDEADLK) {
+                                               goto out_unlock;
+                                       }
+                                       if (link_has_mtu) {
+                                               have_mtu = 1;
+                                               if (dst_link->status.mtu < lower_mtu) {
+                                                       lower_mtu = dst_link->status.mtu;
+                                               }
+                                       }
+                               } else {
+                                       link_has_mtu = _calculate_manual_mtu(knet_h, dst_link);
+                                       if (link_has_mtu) {
+                                               have_mtu = 1;
+                                               if (dst_link->status.mtu < lower_mtu) {
+                                                       lower_mtu = dst_link->status.mtu;
+                                               }
                                        }
                                }
                        }
index be3ab31b211d87a09f4cb77670b3ff2d47c3c4be..d8ac865164d6d42a8ae81d6a4b8dc8ba1409dc54 100644 (file)
@@ -57,6 +57,7 @@ knet_man3_MANS = \
                knet_handle_get_transport_reconnect_interval.3 \
                knet_handle_new.3 \
                knet_handle_pmtud_get.3 \
+               knet_handle_pmtud_set.3 \
                knet_handle_pmtud_getfreq.3 \
                knet_handle_pmtud_setfreq.3 \
                knet_handle_remove_datafd.3 \