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