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