From d04c7bd0a4fb3e6446c4bdd8e0174d241a9efaa2 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 12 Dec 2013 09:21:36 -0800 Subject: [PATCH] Support setting the IPv6 flow label on Linux systems (only). This functionality uses some setsockopt(2) calls that unfortunately don't seem to have an analog on other platforms. Slightly tweaked version of a patch that was... Submitted by: ssahani@redhat.com Issue: 40 (Option for setting Flow Label field in IPv6 header) --- src/iperf3.1 | 6 +++--- src/iperf_api.c | 10 +++++----- src/iperf_tcp.c | 32 +++++++++++++++++++++++++++++++- src/locale.c | 4 +++- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/iperf3.1 b/src/iperf3.1 index 1c9c7b5..4e610aa 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -109,9 +109,9 @@ only use IPv6 .TP .BR -S ", " --tos " \fIn\fR" set the IP 'type of service' -.\" .TP -.\" .BR -L ", " --flowlabel " \fIn\fR" -.\" set the IPv6 'flow label' +.TP +.BR -L ", " --flowlabel " \fIn\fR" +set the IPv6 flow label (currently only supported on Linux) .TP .BR -Z ", " --zerocopy " " Use a "zero copy" method of sending data, such as sendfile(2), diff --git a/src/iperf_api.c b/src/iperf_api.c index aa056ef..60791b7 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -655,17 +655,17 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) client_flag = 1; break; case 'L': -#ifdef notdef +#if defined(linux) test->settings->flowlabel = strtol(optarg, NULL, 0); if (test->settings->flowlabel < 1 || test->settings->flowlabel > 0xfffff) { i_errno = IESETFLOW; return -1; } client_flag = 1; -#else /* notdef */ - i_errno = IEUNIMP; - return -1; -#endif /* notdef */ +#else /* linux */ + i_errno = IEUNIMP; + return -1; +#endif /* linux */ break; case 'Z': if (!has_sendfile()) { diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index cf7bf8d..4292f84 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -25,6 +25,9 @@ #include "iperf_tcp.h" #include "net.h" +#if defined(linux) +#include "flowlabel.h" +#endif /* iperf_tcp_recv * @@ -291,6 +294,7 @@ iperf_tcp_connect(struct iperf_test *test) return -1; } } +#if defined(linux) if (test->settings->flowlabel) { if (server_res->ai_addr->sa_family != AF_INET6) { close(s); @@ -299,9 +303,35 @@ iperf_tcp_connect(struct iperf_test *test) return -1; } else { struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr; - sa6P->sin6_flowinfo = htonl(test->settings->flowlabel); + char freq_buf[sizeof(struct in6_flowlabel_req)]; + struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; + int freq_len = sizeof(*freq); + + memset(freq, 0, sizeof(*freq)); + freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL); + freq->flr_action = IPV6_FL_A_GET; + freq->flr_flags = IPV6_FL_F_CREATE; + freq->flr_share = IPV6_FL_F_CREATE | IPV6_FL_S_EXCL; + memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16); + + if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) { + close(s); + freeaddrinfo(server_res); + i_errno = IESETFLOW; + return -1; + } + sa6P->sin6_flowinfo = freq->flr_label; + + opt = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) { + close(s); + freeaddrinfo(server_res); + i_errno = IESETFLOW; + return -1; + } } } +#endif if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { close(s); diff --git a/src/locale.c b/src/locale.c index 301b1bd..cccd937 100644 --- a/src/locale.c +++ b/src/locale.c @@ -99,7 +99,9 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n" " -4, --version4 only use IPv4\n" " -6, --version6 only use IPv6\n" " -S, --tos N set the IP 'type of service'\n" - /* " -L, --flowlabel N set the IPv6 'flow label'\n" */ +#if defined(linux) + " -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n" +#endif " -Z, --zerocopy use a 'zero copy' method of sending data\n" " -O, --omit N omit the first n seconds\n" " -T, --title str prefix every output line with this string\n"