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
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