• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * iperf, Copyright (c) 2014-2018, The Regents of the University of
3  * California, through Lawrence Berkeley National Laboratory (subject
4  * to receipt of any required approvals from the U.S. Dept. of
5  * Energy).  All rights reserved.
6  *
7  * If you have questions about your rights to use or distribute this
8  * software, please contact Berkeley Lab's Technology Transfer
9  * Department at TTD@lbl.gov.
10  *
11  * NOTICE.  This software is owned by the U.S. Department of Energy.
12  * As such, the U.S. Government has been granted for itself and others
13  * acting on its behalf a paid-up, nonexclusive, irrevocable,
14  * worldwide license in the Software to reproduce, prepare derivative
15  * works, and perform publicly and display publicly.  Beginning five
16  * (5) years after the date permission to assert copyright is obtained
17  * from the U.S. Department of Energy, and subject to any subsequent
18  * five (5) year renewals, the U.S. Government is granted for itself
19  * and others acting on its behalf a paid-up, nonexclusive,
20  * irrevocable, worldwide license in the Software to reproduce,
21  * prepare derivative works, distribute copies to the public, perform
22  * publicly and display publicly, and to permit others to do so.
23  *
24  * This code is distributed under a BSD style license, see the LICENSE
25  * file for complete information.
26  */
27 #include "iperf_config.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <netinet/in.h>
37 #include <netdb.h>
38 #include <sys/time.h>
39 #include <sys/select.h>
40 #include <limits.h>
41 
42 #ifdef HAVE_NETINET_SCTP_H
43 #include <netinet/sctp.h>
44 #endif /* HAVE_NETINET_SCTP_H */
45 
46 #include "iperf.h"
47 #include "iperf_api.h"
48 #include "iperf_sctp.h"
49 #include "net.h"
50 
51 
52 
53 /* iperf_sctp_recv
54  *
55  * receives the data for SCTP
56  */
57 int
iperf_sctp_recv(struct iperf_stream * sp)58 iperf_sctp_recv(struct iperf_stream *sp)
59 {
60 #if defined(HAVE_SCTP)
61     int r;
62 
63     r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
64     if (r < 0)
65         return r;
66 
67     /* Only count bytes received while we're in the correct state. */
68     if (sp->test->state == TEST_RUNNING) {
69 	sp->result->bytes_received += r;
70 	sp->result->bytes_received_this_interval += r;
71     }
72     else {
73 	if (sp->test->debug)
74 	    printf("Late receive, state = %d\n", sp->test->state);
75     }
76 
77     return r;
78 #else
79     i_errno = IENOSCTP;
80     return -1;
81 #endif /* HAVE_SCTP */
82 }
83 
84 
85 /* iperf_sctp_send
86  *
87  * sends the data for SCTP
88  */
89 int
iperf_sctp_send(struct iperf_stream * sp)90 iperf_sctp_send(struct iperf_stream *sp)
91 {
92 #if defined(HAVE_SCTP)
93     int r;
94 
95     r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
96     if (r < 0)
97         return r;
98 
99     sp->result->bytes_sent += r;
100     sp->result->bytes_sent_this_interval += r;
101 
102     return r;
103 #else
104     i_errno = IENOSCTP;
105     return -1;
106 #endif /* HAVE_SCTP */
107 }
108 
109 
110 
111 /* iperf_sctp_accept
112  *
113  * accept a new SCTP stream connection
114  */
115 int
iperf_sctp_accept(struct iperf_test * test)116 iperf_sctp_accept(struct iperf_test * test)
117 {
118 #if defined(HAVE_SCTP)
119     int     s;
120     signed char rbuf = ACCESS_DENIED;
121     char    cookie[COOKIE_SIZE];
122     socklen_t len;
123     struct sockaddr_storage addr;
124 
125     len = sizeof(addr);
126     s = accept(test->listener, (struct sockaddr *) &addr, &len);
127     if (s < 0) {
128         i_errno = IESTREAMCONNECT;
129         return -1;
130     }
131 
132     if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
133         i_errno = IERECVCOOKIE;
134         return -1;
135     }
136 
137     if (strcmp(test->cookie, cookie) != 0) {
138         if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
139             i_errno = IESENDMESSAGE;
140             return -1;
141         }
142         close(s);
143     }
144 
145     return s;
146 #else
147     i_errno = IENOSCTP;
148     return -1;
149 #endif /* HAVE_SCTP */
150 }
151 
152 
153 /* iperf_sctp_listen
154  *
155  * start up a listener for SCTP stream connections
156  */
157 int
iperf_sctp_listen(struct iperf_test * test)158 iperf_sctp_listen(struct iperf_test *test)
159 {
160 #if defined(HAVE_SCTP)
161     struct addrinfo hints, *res;
162     char portstr[6];
163     int s, opt, saved_errno;
164 
165     close(test->listener);
166 
167     snprintf(portstr, 6, "%d", test->server_port);
168     memset(&hints, 0, sizeof(hints));
169     /*
170      * If binding to the wildcard address with no explicit address
171      * family specified, then force us to get an AF_INET6 socket.
172      * More details in the comments in netanounce().
173      */
174     if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
175         hints.ai_family = AF_INET6;
176     } else {
177         hints.ai_family = test->settings->domain;
178     }
179     hints.ai_socktype = SOCK_STREAM;
180     hints.ai_flags = AI_PASSIVE;
181     if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
182         i_errno = IESTREAMLISTEN;
183         return -1;
184     }
185 
186     if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
187         freeaddrinfo(res);
188         i_errno = IESTREAMLISTEN;
189         return -1;
190     }
191 
192 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
193     if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
194         test->settings->domain == AF_INET6)) {
195         if (test->settings->domain == AF_UNSPEC)
196             opt = 0;
197         else
198             opt = 1;
199         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
200 		       (char *) &opt, sizeof(opt)) < 0) {
201 	    saved_errno = errno;
202 	    close(s);
203 	    freeaddrinfo(res);
204 	    errno = saved_errno;
205 	    i_errno = IEPROTOCOL;
206 	    return -1;
207 	}
208     }
209 #endif /* IPV6_V6ONLY */
210 
211     opt = 1;
212     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
213         saved_errno = errno;
214         close(s);
215         freeaddrinfo(res);
216         errno = saved_errno;
217         i_errno = IEREUSEADDR;
218         return -1;
219     }
220 
221     /* servers must call sctp_bindx() _instead_ of bind() */
222     if (!TAILQ_EMPTY(&test->xbind_addrs)) {
223         freeaddrinfo(res);
224         if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER))
225             return -1;
226     } else
227     if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
228         saved_errno = errno;
229         close(s);
230         freeaddrinfo(res);
231         errno = saved_errno;
232         i_errno = IESTREAMLISTEN;
233         return -1;
234     }
235 
236     freeaddrinfo(res);
237 
238     if (listen(s, INT_MAX) < 0) {
239         i_errno = IESTREAMLISTEN;
240         return -1;
241     }
242 
243     test->listener = s;
244 
245     return s;
246 #else
247     i_errno = IENOSCTP;
248     return -1;
249 #endif /* HAVE_SCTP */
250 }
251 
252 
253 /* iperf_sctp_connect
254  *
255  * connect to a SCTP stream listener
256  */
257 int
iperf_sctp_connect(struct iperf_test * test)258 iperf_sctp_connect(struct iperf_test *test)
259 {
260 #if defined(HAVE_SCTP)
261     int s, opt, saved_errno;
262     char portstr[6];
263     struct addrinfo hints, *local_res, *server_res;
264 
265     if (test->bind_address) {
266         memset(&hints, 0, sizeof(hints));
267         hints.ai_family = test->settings->domain;
268         hints.ai_socktype = SOCK_STREAM;
269         if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) {
270             i_errno = IESTREAMCONNECT;
271             return -1;
272         }
273     }
274 
275     memset(&hints, 0, sizeof(hints));
276     hints.ai_family = test->settings->domain;
277     hints.ai_socktype = SOCK_STREAM;
278     snprintf(portstr, sizeof(portstr), "%d", test->server_port);
279     if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) {
280 	if (test->bind_address)
281 	    freeaddrinfo(local_res);
282         i_errno = IESTREAMCONNECT;
283         return -1;
284     }
285 
286     s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
287     if (s < 0) {
288 	if (test->bind_address)
289 	    freeaddrinfo(local_res);
290 	freeaddrinfo(server_res);
291         i_errno = IESTREAMCONNECT;
292         return -1;
293     }
294 
295     /*
296      * Various ways to bind the local end of the connection.
297      * 1.  --bind (with or without --cport).
298      */
299     if (test->bind_address) {
300         struct sockaddr_in *lcladdr;
301         lcladdr = (struct sockaddr_in *)local_res->ai_addr;
302         lcladdr->sin_port = htons(test->bind_port);
303 
304         if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
305 	    saved_errno = errno;
306 	    close(s);
307 	    freeaddrinfo(local_res);
308 	    freeaddrinfo(server_res);
309 	    errno = saved_errno;
310             i_errno = IESTREAMCONNECT;
311             return -1;
312         }
313         freeaddrinfo(local_res);
314     }
315     /* --cport, no --bind */
316     else if (test->bind_port) {
317 	size_t addrlen;
318 	struct sockaddr_storage lcl;
319 
320 	/* IPv4 */
321 	if (server_res->ai_family == AF_INET) {
322 	    struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
323 	    lcladdr->sin_family = AF_INET;
324 	    lcladdr->sin_port = htons(test->bind_port);
325 	    lcladdr->sin_addr.s_addr = INADDR_ANY;
326 	    addrlen = sizeof(struct sockaddr_in);
327 	}
328 	/* IPv6 */
329 	else if (server_res->ai_family == AF_INET6) {
330 	    struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
331 	    lcladdr->sin6_family = AF_INET6;
332 	    lcladdr->sin6_port = htons(test->bind_port);
333 	    lcladdr->sin6_addr = in6addr_any;
334 	    addrlen = sizeof(struct sockaddr_in6);
335 	}
336 	/* Unknown protocol */
337 	else {
338 	    saved_errno = errno;
339 	    close(s);
340 	    freeaddrinfo(server_res);
341 	    errno = saved_errno;
342             i_errno = IEPROTOCOL;
343             return -1;
344 	}
345 
346         if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
347 	    saved_errno = errno;
348 	    close(s);
349 	    freeaddrinfo(server_res);
350 	    errno = saved_errno;
351             i_errno = IESTREAMCONNECT;
352             return -1;
353         }
354     }
355 
356     if (test->no_delay != 0) {
357          opt = 1;
358          if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
359              saved_errno = errno;
360              close(s);
361              freeaddrinfo(server_res);
362              errno = saved_errno;
363              i_errno = IESETNODELAY;
364              return -1;
365          }
366     }
367 
368     if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
369 
370 	/*
371 	 * Some platforms use a struct sctp_assoc_value as the
372 	 * argument to SCTP_MAXSEG.  Other (older API implementations)
373 	 * take an int.  FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
374 	 * but OpenSolaris 11 doesn't.
375 	 */
376 #ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
377         struct sctp_assoc_value av;
378 
379 	/*
380 	 * Some platforms support SCTP_FUTURE_ASSOC, others need to
381 	 * (equivalently) do 0 here.  FreeBSD 10 is an example of the
382 	 * former, CentOS 6 Linux is an example of the latter.
383 	 */
384 #ifdef SCTP_FUTURE_ASSOC
385         av.assoc_id = SCTP_FUTURE_ASSOC;
386 #else
387 	av.assoc_id = 0;
388 #endif
389         av.assoc_value = test->settings->mss;
390 
391         if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
392             saved_errno = errno;
393             close(s);
394             freeaddrinfo(server_res);
395             errno = saved_errno;
396             i_errno = IESETMSS;
397             return -1;
398         }
399 #else
400 	opt = test->settings->mss;
401 
402 	/*
403 	 * Solaris might not support this option.  If it doesn't work,
404 	 * ignore the error (at least for now).
405 	 */
406         if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
407 	    errno != ENOPROTOOPT) {
408             saved_errno = errno;
409             close(s);
410             freeaddrinfo(server_res);
411             errno = saved_errno;
412             i_errno = IESETMSS;
413             return -1;
414         }
415 #endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */
416     }
417 
418     if (test->settings->num_ostreams > 0) {
419         struct sctp_initmsg initmsg;
420 
421         memset(&initmsg, 0, sizeof(struct sctp_initmsg));
422         initmsg.sinit_num_ostreams = test->settings->num_ostreams;
423 
424         if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
425                 saved_errno = errno;
426                 close(s);
427                 freeaddrinfo(server_res);
428                 errno = saved_errno;
429                 i_errno = IESETSCTPNSTREAM;
430                 return -1;
431         }
432     }
433 
434     /* clients must call bind() followed by sctp_bindx() before connect() */
435     if (!TAILQ_EMPTY(&test->xbind_addrs)) {
436         if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT))
437             return -1;
438     }
439 
440     /* TODO support sctp_connectx() to avoid heartbeating. */
441     if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
442 	saved_errno = errno;
443 	close(s);
444 	freeaddrinfo(server_res);
445 	errno = saved_errno;
446         i_errno = IESTREAMCONNECT;
447         return -1;
448     }
449     freeaddrinfo(server_res);
450 
451     /* Send cookie for verification */
452     if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
453 	saved_errno = errno;
454 	close(s);
455 	errno = saved_errno;
456         i_errno = IESENDCOOKIE;
457         return -1;
458     }
459 
460     /*
461      * We want to allow fragmentation.  But there's at least one
462      * implementation (Solaris) that doesn't support this option,
463      * even though it defines SCTP_DISABLE_FRAGMENTS.  So we have to
464      * try setting the option and ignore the error, if it doesn't
465      * work.
466      */
467     opt = 0;
468     if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
469 	errno != ENOPROTOOPT) {
470         saved_errno = errno;
471         close(s);
472         freeaddrinfo(server_res);
473         errno = saved_errno;
474         i_errno = IESETSCTPDISABLEFRAG;
475         return -1;
476     }
477 
478     return s;
479 #else
480     i_errno = IENOSCTP;
481     return -1;
482 #endif /* HAVE_SCTP */
483 }
484 
485 
486 
487 int
iperf_sctp_init(struct iperf_test * test)488 iperf_sctp_init(struct iperf_test *test)
489 {
490 #if defined(HAVE_SCTP)
491     return 0;
492 #else
493     i_errno = IENOSCTP;
494     return -1;
495 #endif /* HAVE_SCTP */
496 }
497 
498 
499 
500 /* iperf_sctp_bindx
501  *
502  * handle binding to multiple endpoints (-X parameters)
503  */
504 int
iperf_sctp_bindx(struct iperf_test * test,int s,int is_server)505 iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
506 {
507 #if defined(HAVE_SCTP)
508     struct addrinfo hints;
509     char portstr[6];
510     char *servname;
511     struct addrinfo *ai, *ai0;
512     struct sockaddr *xaddrs;
513     struct xbind_entry *xbe, *xbe0;
514     char *bp;
515     size_t xaddrlen;
516     int nxaddrs;
517     int retval;
518     int domain;
519     int saved_errno;
520 
521     domain = test->settings->domain;
522     xbe0 = NULL;
523     retval = 0;
524 
525     if (TAILQ_EMPTY(&test->xbind_addrs))
526         return retval; /* nothing to do */
527 
528     memset(&hints, 0, sizeof(hints));
529     hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
530     hints.ai_socktype = SOCK_STREAM;
531     servname = NULL;
532     if (is_server) {
533         hints.ai_flags |= AI_PASSIVE;
534         snprintf(portstr, 6, "%d", test->server_port);
535         servname = portstr;
536     }
537 
538     /* client: must pop first -X address and call bind().
539      * sctp_bindx() must see the ephemeral port chosen by bind().
540      * we deliberately ignore the -B argument in this case.
541      */
542     if (!is_server) {
543         struct sockaddr *sa;
544         struct sockaddr_in *sin;
545         struct sockaddr_in6 *sin6;
546         int eport;
547 
548         xbe0 = TAILQ_FIRST(&test->xbind_addrs);
549         TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
550 
551         if (getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai) != 0) {
552             i_errno = IESETSCTPBINDX;
553             retval = -1;
554             goto out;
555         }
556 
557         ai = xbe0->ai;
558         if (domain != AF_UNSPEC && domain != ai->ai_family) {
559             i_errno = IESETSCTPBINDX;
560             retval = -1;
561             goto out;
562         }
563         if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
564             i_errno = IESETSCTPBINDX;
565             retval = -1;
566             goto out;
567         }
568 
569         /* if only one -X argument, nothing more to do */
570         if (TAILQ_EMPTY(&test->xbind_addrs))
571             goto out;
572 
573         sa = (struct sockaddr *)ai->ai_addr;
574         if (sa->sa_family == AF_INET) {
575             sin = (struct sockaddr_in *)ai->ai_addr;
576             eport = sin->sin_port;
577         } else if (sa->sa_family == AF_INET6) {
578             sin6 = (struct sockaddr_in6 *)ai->ai_addr;
579             eport = sin6->sin6_port;
580         } else {
581             i_errno = IESETSCTPBINDX;
582             retval = -1;
583             goto out;
584         }
585         snprintf(portstr, 6, "%d", ntohs(eport));
586         servname = portstr;
587     }
588 
589     /* pass 1: resolve and compute lengths. */
590     nxaddrs = 0;
591     xaddrlen = 0;
592     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
593         if (xbe->ai != NULL)
594             freeaddrinfo(xbe->ai);
595         if (getaddrinfo(xbe->name, servname, &hints, &xbe->ai) != 0) {
596             i_errno = IESETSCTPBINDX;
597             retval = -1;
598             goto out;
599         }
600         ai0 = xbe->ai;
601         for (ai = ai0; ai; ai = ai->ai_next) {
602             if (domain != AF_UNSPEC && domain != ai->ai_family)
603                 continue;
604             xaddrlen += ai->ai_addrlen;
605             ++nxaddrs;
606         }
607     }
608 
609     /* pass 2: copy into flat buffer. */
610     xaddrs = (struct sockaddr *)malloc(xaddrlen);
611     if (!xaddrs) {
612             i_errno = IESETSCTPBINDX;
613             retval = -1;
614             goto out;
615     }
616     bp = (char *)xaddrs;
617     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
618         ai0 = xbe->ai;
619         for (ai = ai0; ai; ai = ai->ai_next) {
620             if (domain != AF_UNSPEC && domain != ai->ai_family)
621                 continue;
622 	    memcpy(bp, ai->ai_addr, ai->ai_addrlen);
623             bp += ai->ai_addrlen;
624         }
625     }
626 
627     if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
628         saved_errno = errno;
629         close(s);
630         free(xaddrs);
631         errno = saved_errno;
632         i_errno = IESETSCTPBINDX;
633         retval = -1;
634         goto out;
635     }
636 
637     free(xaddrs);
638     retval = 0;
639 
640 out:
641     /* client: put head node back. */
642     if (!is_server && xbe0)
643         TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
644 
645     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
646         if (xbe->ai) {
647             freeaddrinfo(xbe->ai);
648             xbe->ai = NULL;
649         }
650     }
651 
652     return retval;
653 #else
654     i_errno = IENOSCTP;
655     return -1;
656 #endif /* HAVE_SCTP */
657 }
658