--- /dev/null
+/*
+ * 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;
+}
#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;
(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;
+ }
}
}
}