Skip to content

Commit 3bb1dfc

Browse files
committed
Added Generic Segmentation Offload for UDP
1 parent 6f5512e commit 3bb1dfc

9 files changed

Lines changed: 217 additions & 12 deletions

File tree

‎docs/invoking.rst‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,14 @@ the executable.
388388
--rsa-public-key-path file
389389
path to the RSA public key used to encrypt authentication cre-
390390
dentials (if built with OpenSSL support)
391-
391+
392+
--udp-gso N
393+
enables Generic Segmentation Offload for UDP protocol. "N" is a
394+
segment size, it cannot be greater than MTU including headers.
395+
Use "--udp-lso 0" to use blocksize ("-l" option) as a segment
396+
size. Attention! Spliting into too many fragments can lead to a
397+
crash of "iperf" or statistics errors.
398+
392399
393400
EXAMPLES
394401
Authentication - RSA Keypair

‎src/iperf.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ struct iperf_settings
154154
EVP_PKEY *client_rsa_pubkey;
155155
#endif // HAVE_SSL
156156
int connect_timeout; /* socket connection timeout, in ms */
157+
int udp_gso; /* Generic Segmentation Offload for UDP protocol */
158+
uint16_t udp_gso_segsize; /* GSO segment size */
157159
};
158160

159161
struct iperf_test;

‎src/iperf_api.c‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
#include <sys/socket.h>
4545
#include <sys/types.h>
4646
#include <netinet/in.h>
47+
#include <netinet/ip.h>
48+
#include <netinet/ip6.h>
49+
#include <netinet/udp.h>
4750
#include <arpa/inet.h>
4851
#include <netdb.h>
4952
#ifdef HAVE_STDINT_H
@@ -57,6 +60,7 @@
5760
#include <sched.h>
5861
#include <setjmp.h>
5962
#include <stdarg.h>
63+
#include <linux/if_ether.h>
6064

6165
#if defined(HAVE_CPUSET_SETAFFINITY)
6266
#include <sys/param.h>
@@ -87,6 +91,10 @@
8791
#include "iperf_auth.h"
8892
#endif /* HAVE_SSL */
8993

94+
#ifndef UDP_SEGMENT
95+
#define UDP_SEGMENT 103 /* Set GSO segmentation size */
96+
#endif
97+
9098
/* Forwards. */
9199
static int send_parameters(struct iperf_test *test);
92100
static int get_parameters(struct iperf_test *test);
@@ -853,6 +861,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
853861
{"connect-timeout", required_argument, NULL, OPT_CONNECT_TIMEOUT},
854862
{"debug", no_argument, NULL, 'd'},
855863
{"help", no_argument, NULL, 'h'},
864+
{"udp-gso", required_argument, NULL, OPT_UDP_GSO},
856865
{NULL, 0, NULL, 0}
857866
};
858867
int flag;
@@ -1242,6 +1251,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
12421251
case 'h':
12431252
usage_long(stdout);
12441253
exit(0);
1254+
case OPT_UDP_GSO:
1255+
test->settings->udp_gso = 1;
1256+
test->settings->udp_gso_segsize = unit_atoi(optarg);
1257+
break;
12451258
default:
12461259
usage_long(stderr);
12471260
exit(1);
@@ -1376,6 +1389,46 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
13761389
warning("Debug output (-d) may interfere with JSON output (-J)");
13771390
}
13781391

1392+
if (test->settings->udp_gso) {
1393+
int prev_blksize = test->settings->blksize;
1394+
uint16_t iphdr_len = 0;
1395+
switch (test->settings->domain) {
1396+
case PF_UNSPEC:
1397+
warning("Domain is unspecified! Using IPv4 by default.");
1398+
case PF_INET:
1399+
iphdr_len = sizeof(struct iphdr);
1400+
break;
1401+
case PF_INET6:
1402+
iphdr_len = sizeof(struct ip6_hdr);
1403+
break;
1404+
default:
1405+
i_errno = IEDOMAIN;
1406+
perror("Current domain is unsupported by iperf GSO now. Disabling UDP GSO");
1407+
test->settings->udp_gso = 0;
1408+
test->settings->udp_gso_segsize = 0;
1409+
break;
1410+
}
1411+
1412+
if (iphdr_len) {
1413+
if (!test->settings->udp_gso_segsize) {
1414+
test->settings->udp_gso_segsize = test->settings->blksize;
1415+
test->settings->blksize = ETH_MAX_MTU - iphdr_len - sizeof(struct udphdr);
1416+
}
1417+
1418+
if (test->settings->udp_gso_segsize > ETH_DATA_LEN - iphdr_len - sizeof(struct udphdr)) {
1419+
warning("UDP GSO segment size exceeds maximum. Using maximum value instead of current.");
1420+
test->settings->udp_gso_segsize = ETH_DATA_LEN - iphdr_len - sizeof(struct udphdr);
1421+
}
1422+
1423+
if (test->settings->udp_gso_segsize >= test->settings->blksize) {
1424+
warning("UDP GSO segment size >= block size. The use of UDP GSO is impractival and amy cause errors. Disabling UDP GSO.");
1425+
test->settings->blksize = prev_blksize;
1426+
test->settings->udp_gso = 0;
1427+
test->settings->udp_gso_segsize = 0;
1428+
}
1429+
}
1430+
}
1431+
13791432
return 0;
13801433
}
13811434

‎src/iperf_api.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct iperf_time;
7373
#define OPT_REPEATING_PAYLOAD 18
7474
#define OPT_EXTRA_DATA 19
7575
#define OPT_BIDIRECTIONAL 20
76+
#define OPT_UDP_GSO 25
7677

7778
/* states */
7879
#define TEST_START 1
@@ -345,6 +346,8 @@ enum {
345346
IEBADFORMAT = 24, // Bad format argument to -f
346347
IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional
347348
IEBADPORT = 26, // Bad port number
349+
IEDOMAIN = 27, // The specified domain doesn't supported by some functions (check perror)
350+
IEUDPGSO = 28, // The configuration doesn't LSO for UDP protocol
348351
/* Test errors */
349352
IENEWTEST = 100, // Unable to create a new test (check perror)
350353
IEINITTEST = 101, // Test initialization failed (check perror)

‎src/iperf_error.c‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ iperf_strerror(int int_errno)
385385
case IEREVERSEBIDIR:
386386
snprintf(errstr, len, "cannot be both reverse and bidirectional");
387387
break;
388+
case IEDOMAIN:
389+
snprintf(errstr, len, "invalid domain");
390+
perr = 1;
391+
break;
392+
case IEUDPGSO:
393+
snprintf(errstr, len, "your configuration doesn't support GSO for UDP protocol");
394+
break;
388395

389396
}
390397

‎src/iperf_locale.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
181181
" --rsa-public-key-path path to the RSA public key used to encrypt\n"
182182
" authentication credentials\n"
183183
#endif //HAVESSL
184+
" --udp-gso N enables Generic Segmentation Offload for UDP protocol. \"N\" is a\n"
185+
" segment size, it cannot be greater than MTU including headers.\n"
186+
" Use \"--udp-gso 0\" to use blocksize (\"-l\" option) as a segment\n"
187+
" size. Attention! Splitting into too many fragments can lead to a\n"
188+
" crash of \"iperf\" or statistics errors.\n"
184189

185190
#ifdef NOT_YET_SUPPORTED /* still working on these */
186191
#endif

‎src/iperf_udp.c‎

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <sys/socket.h>
3535
#include <sys/types.h>
3636
#include <netinet/in.h>
37+
#include <netinet/udp.h>
3738
#ifdef HAVE_STDINT_H
3839
#include <stdint.h>
3940
#endif
@@ -55,6 +56,10 @@
5556
# define PRIu64 "llu"
5657
#endif
5758

59+
#ifndef UDP_SEGMENT
60+
#define UDP_SEGMENT 103 /* Set GSO segmentation size */
61+
#endif
62+
5863
/* iperf_udp_recv
5964
*
6065
* receives the data for UDP
@@ -64,6 +69,7 @@ iperf_udp_recv(struct iperf_stream *sp)
6469
{
6570
uint32_t sec, usec;
6671
uint64_t pcount;
72+
uint16_t udp_gso_seg;
6773
int r;
6874
int size = sp->settings->blksize;
6975
double transit = 0, d = 0;
@@ -88,9 +94,11 @@ iperf_udp_recv(struct iperf_stream *sp)
8894
memcpy(&sec, sp->buffer, sizeof(sec));
8995
memcpy(&usec, sp->buffer+4, sizeof(usec));
9096
memcpy(&pcount, sp->buffer+8, sizeof(pcount));
97+
memcpy(&udp_gso_seg, sp->buffer+16, sizeof(udp_gso_seg));
9198
sec = ntohl(sec);
9299
usec = ntohl(usec);
93100
pcount = be64toh(pcount);
101+
udp_gso_seg = ntohs(udp_gso_seg);
94102
sent_time.secs = sec;
95103
sent_time.usecs = usec;
96104
}
@@ -99,9 +107,11 @@ iperf_udp_recv(struct iperf_stream *sp)
99107
memcpy(&sec, sp->buffer, sizeof(sec));
100108
memcpy(&usec, sp->buffer+4, sizeof(usec));
101109
memcpy(&pc, sp->buffer+8, sizeof(pc));
110+
memcpy(&udp_gso_seg, sp->buffer+12, sizeof(udp_gso_seg));
102111
sec = ntohl(sec);
103112
usec = ntohl(usec);
104113
pcount = ntohl(pc);
114+
udp_gso_seg = ntohs(udp_gso_seg);
105115
sent_time.secs = sec;
106116
sent_time.usecs = usec;
107117
}
@@ -122,6 +132,7 @@ iperf_udp_recv(struct iperf_stream *sp)
122132
* far (so we're expecting to see the packet with sequence number
123133
* sp->packet_count + 1 arrive next).
124134
*/
135+
if (!udp_gso_seg) {
125136
if (pcount >= sp->packet_count + 1) {
126137

127138
/* Forward, but is there a gap in sequence numbers? */
@@ -152,6 +163,7 @@ iperf_udp_recv(struct iperf_stream *sp)
152163
if (sp->test->debug)
153164
fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", pcount, sp->packet_count, sp->socket);
154165
}
166+
}
155167

156168
/*
157169
* jitter measurement
@@ -202,31 +214,96 @@ iperf_udp_send(struct iperf_stream *sp)
202214

203215
uint32_t sec, usec;
204216
uint64_t pcount;
217+
uint16_t udp_gso_seg = 0;
205218

206219
sec = htonl(before.secs);
207220
usec = htonl(before.usecs);
208221
pcount = htobe64(sp->packet_count);
222+
udp_gso_seg = htons(udp_gso_seg);
209223

210-
memcpy(sp->buffer, &sec, sizeof(sec));
211-
memcpy(sp->buffer+4, &usec, sizeof(usec));
212-
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
224+
if (sp->settings->udp_gso) {
225+
/* Protection from lose segment with counters. */
226+
int i = 0;
227+
int counters_len = sizeof(sec) + sizeof(usec) + sizeof(pcount) + sizeof(udp_gso_seg);
228+
char* buf = sp->buffer;
229+
do {
230+
memcpy(buf, &sec, sizeof(sec));
231+
memcpy(buf+4, &usec, sizeof(usec));
232+
memcpy(buf+8, &pcount, sizeof(pcount));
233+
memcpy(buf+16, &udp_gso_seg, sizeof(udp_gso_seg));
234+
235+
buf += sp->settings->udp_gso_segsize;
236+
i += sp->settings->udp_gso_segsize;
237+
udp_gso_seg = htons(ntohs(udp_gso_seg) + 1);
238+
} while (i + counters_len <= size);
239+
} else {
240+
memcpy(sp->buffer, &sec, sizeof(sec));
241+
memcpy(sp->buffer+4, &usec, sizeof(usec));
242+
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
243+
memcpy(sp->buffer+16, &udp_gso_seg, sizeof(udp_gso_seg));
244+
}
213245

214-
}
215-
else {
246+
}
247+
else {
216248

217249
uint32_t sec, usec, pcount;
250+
uint16_t udp_gso_seg = 0;
218251

219252
sec = htonl(before.secs);
220253
usec = htonl(before.usecs);
221254
pcount = htonl(sp->packet_count);
255+
udp_gso_seg = htons(udp_gso_seg);
256+
257+
if (sp->settings->udp_gso) {
258+
/* Protection from lose segment with counters. */
259+
int i = 0;
260+
int counters_len = sizeof(sec) + sizeof(usec) + sizeof(pcount) + sizeof(udp_gso_seg);
261+
char* buf = sp->buffer;
262+
do {
263+
memcpy(buf, &sec, sizeof(sec));
264+
memcpy(buf+4, &usec, sizeof(usec));
265+
memcpy(buf+8, &pcount, sizeof(pcount));
266+
memcpy(buf+12, &udp_gso_seg, sizeof(udp_gso_seg));
267+
268+
buf += sp->settings->udp_gso_segsize;
269+
i += sp->settings->udp_gso_segsize;
270+
udp_gso_seg = htons(ntohs(udp_gso_seg) + 1);
271+
} while (i + counters_len <= size);
272+
} else {
273+
memcpy(sp->buffer, &sec, sizeof(sec));
274+
memcpy(sp->buffer+4, &usec, sizeof(usec));
275+
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
276+
memcpy(sp->buffer+12, &udp_gso_seg, sizeof(udp_gso_seg));
277+
}
222278

223-
memcpy(sp->buffer, &sec, sizeof(sec));
224-
memcpy(sp->buffer+4, &usec, sizeof(usec));
225-
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
226-
227-
}
279+
}
280+
281+
if (sp->settings->udp_gso) {
282+
char control[CMSG_SPACE(sizeof(sp->settings->udp_gso_segsize))] = { 0 };
283+
struct msghdr msg = { 0 };
284+
struct iovec iov = { 0 };
285+
struct cmsghdr *cm = NULL;
286+
287+
iov.iov_base = sp->buffer;
288+
iov.iov_len = size;
289+
290+
msg.msg_iov = &iov;
291+
msg.msg_iovlen = 1;
292+
293+
msg.msg_control = control;
294+
msg.msg_controllen = sizeof(control);
295+
296+
cm = CMSG_FIRSTHDR(&msg);
297+
cm->cmsg_level = SOL_UDP;
298+
cm->cmsg_type = UDP_SEGMENT;
299+
cm->cmsg_len = CMSG_LEN(sizeof(sp->settings->udp_gso_segsize));
300+
uint16_t *valp = (uint16_t *)CMSG_DATA(cm);
301+
*valp = sp->settings->udp_gso_segsize;
228302

229-
r = Nwrite(sp->socket, sp->buffer, size, Pudp);
303+
r = Nsendmsg(sp->socket, &msg, Pudp);
304+
} else {
305+
r = Nwrite(sp->socket, sp->buffer, size, Pudp);
306+
}
230307

231308
if (r < 0)
232309
return r;
@@ -553,6 +630,17 @@ iperf_udp_connect(struct iperf_test *test)
553630
return -1;
554631
}
555632

633+
if (test->settings->udp_gso) {
634+
uint16_t t_udp_gso_value;
635+
socklen_t t_udp_gso_size;
636+
if (getsockopt(s, SOL_UDP, UDP_SEGMENT, &t_udp_gso_value, &t_udp_gso_size)) {
637+
i_errno = IEUDPGSO;
638+
test->settings->udp_gso = 0;
639+
test->settings->udp_gso_segsize = 0;
640+
return -1;
641+
}
642+
}
643+
556644
return s;
557645
}
558646

0 commit comments

Comments
 (0)