xref: /iperf/src/net.c (revision 023fb45e)
1 /*
2  * Copyright (c) 2009-2014, The Regents of the University of California,
3  * through Lawrence Berkeley National Laboratory (subject to receipt of any
4  * required approvals from the U.S. Dept. of Energy).  All rights reserved.
5  *
6  * This code is distributed under a BSD style license, see the LICENSE file
7  * for complete information.
8  */
9 #include "iperf_config.h"
10 
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <sys/errno.h>
17 #include <netinet/in.h>
18 #include <netinet/tcp.h>
19 #include <assert.h>
20 #include <netdb.h>
21 #include <string.h>
22 #include <sys/fcntl.h>
23 
24 #ifdef HAVE_SENDFILE
25 #ifdef linux
26 #include <sys/sendfile.h>
27 #else
28 #ifdef __FreeBSD__
29 #include <sys/uio.h>
30 #else
31 #if defined(__APPLE__) && defined(__MACH__)	/* OS X */
32 #include <AvailabilityMacros.h>
33 #if defined(MAC_OS_X_VERSION_10_6)
34 #include <sys/uio.h>
35 #endif
36 #endif
37 #endif
38 #endif
39 #endif /* HAVE_SENDFILE */
40 
41 #include "iperf_util.h"
42 #include "net.h"
43 #include "timer.h"
44 
45 /* netdial and netannouce code comes from libtask: http://swtch.com/libtask/
46  * Copyright: http://swtch.com/libtask/COPYRIGHT
47 */
48 
49 /* make connection to server */
50 int
51 netdial(int domain, int proto, char *local, int local_port, char *server, int port)
52 {
53     struct addrinfo hints, *local_res, *server_res;
54     int s;
55 
56     if (local) {
57         memset(&hints, 0, sizeof(hints));
58         hints.ai_family = domain;
59         hints.ai_socktype = proto;
60         if (getaddrinfo(local, NULL, &hints, &local_res) != 0)
61             return -1;
62     }
63 
64     memset(&hints, 0, sizeof(hints));
65     hints.ai_family = domain;
66     hints.ai_socktype = proto;
67     if (getaddrinfo(server, NULL, &hints, &server_res) != 0)
68         return -1;
69 
70     s = socket(server_res->ai_family, proto, 0);
71     if (s < 0) {
72 	if (local)
73 	    freeaddrinfo(local_res);
74 	freeaddrinfo(server_res);
75         return -1;
76     }
77 
78     if (local) {
79         if (local_port) {
80             struct sockaddr_in *lcladdr;
81             lcladdr = (struct sockaddr_in *)local_res->ai_addr;
82             lcladdr->sin_port = htons(local_port);
83             local_res->ai_addr = (struct sockaddr *)lcladdr;
84         }
85 
86         if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
87 	    close(s);
88 	    freeaddrinfo(local_res);
89 	    freeaddrinfo(server_res);
90             return -1;
91 	}
92         freeaddrinfo(local_res);
93     }
94 
95     ((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port);
96     if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
97 	close(s);
98 	freeaddrinfo(server_res);
99         return -1;
100     }
101 
102     freeaddrinfo(server_res);
103     return s;
104 }
105 
106 /***************************************************************/
107 
108 int
109 netannounce(int domain, int proto, char *local, int port)
110 {
111     struct addrinfo hints, *res;
112     char portstr[6];
113     int s, opt;
114 
115     snprintf(portstr, 6, "%d", port);
116     memset(&hints, 0, sizeof(hints));
117     /*
118      * If binding to the wildcard address with no explicit address
119      * family specified, then force us to get an AF_INET6 socket.  On
120      * CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family,
121      * and ai_flags containing AI_PASSIVE returns a result structure
122      * with ai_family set to AF_INET, with the result that we create
123      * and bind an IPv4 address wildcard address and by default, we
124      * can't accept IPv6 connections.
125      *
126      * On FreeBSD, under the above circumstances, ai_family in the
127      * result structure is set to AF_INET6.
128      */
129     if (domain == AF_UNSPEC && !local) {
130 	hints.ai_family = AF_INET6;
131     }
132     else {
133 	hints.ai_family = domain;
134     }
135     hints.ai_socktype = proto;
136     hints.ai_flags = AI_PASSIVE;
137     if (getaddrinfo(local, portstr, &hints, &res) != 0)
138         return -1;
139 
140     s = socket(res->ai_family, proto, 0);
141     if (s < 0) {
142 	freeaddrinfo(res);
143         return -1;
144     }
145 
146     opt = 1;
147     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
148 		   (char *) &opt, sizeof(opt)) < 0) {
149 	close(s);
150 	freeaddrinfo(res);
151 	return -1;
152     }
153     /*
154      * If we got an IPv6 socket, figure out if it should accept IPv4
155      * connections as well.  We do that if and only if no address
156      * family was specified explicitly.  Note that we can only
157      * do this if the IPV6_V6ONLY socket option is supported.  Also,
158      * OpenBSD explicitly omits support for IPv4-mapped addresses,
159      * even though it implements IPV6_V6ONLY.
160      */
161 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
162     if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) {
163 	if (domain == AF_UNSPEC)
164 	    opt = 0;
165 	else
166 	    opt = 1;
167 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
168 		       (char *) &opt, sizeof(opt)) < 0) {
169 	    close(s);
170 	    freeaddrinfo(res);
171 	    return -1;
172 	}
173     }
174 #endif /* IPV6_V6ONLY */
175 
176     if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
177         close(s);
178 	freeaddrinfo(res);
179         return -1;
180     }
181 
182     freeaddrinfo(res);
183 
184     if (proto == SOCK_STREAM) {
185         if (listen(s, 5) < 0) {
186 	    close(s);
187             return -1;
188         }
189     }
190 
191     return s;
192 }
193 
194 
195 /*******************************************************************/
196 /* reads 'count' bytes from a socket  */
197 /********************************************************************/
198 
199 int
200 Nread(int fd, char *buf, size_t count, int prot)
201 {
202     register ssize_t r;
203     register size_t nleft = count;
204 
205     while (nleft > 0) {
206         r = read(fd, buf, nleft);
207         if (r < 0) {
208             if (errno == EINTR || errno == EAGAIN)
209                 break;
210             else
211                 return NET_HARDERROR;
212         } else if (r == 0)
213             break;
214 
215         nleft -= r;
216         buf += r;
217     }
218     return count - nleft;
219 }
220 
221 
222 /*
223  *                      N W R I T E
224  */
225 
226 int
227 Nwrite(int fd, const char *buf, size_t count, int prot)
228 {
229     register ssize_t r;
230     register size_t nleft = count;
231 
232     while (nleft > 0) {
233 	r = write(fd, buf, nleft);
234 	if (r < 0) {
235 	    switch (errno) {
236 		case EINTR:
237 		case EAGAIN:
238 		return count - nleft;
239 
240 		case ENOBUFS:
241 		return NET_SOFTERROR;
242 
243 		default:
244 		return NET_HARDERROR;
245 	    }
246 	} else if (r == 0)
247 	    return NET_SOFTERROR;
248 	nleft -= r;
249 	buf += r;
250     }
251     return count;
252 }
253 
254 
255 int
256 has_sendfile(void)
257 {
258 #if defined(HAVE_SENDFILE)
259     return 1;
260 #else /* HAVE_SENDFILE */
261     return 0;
262 #endif /* HAVE_SENDFILE */
263 
264 }
265 
266 
267 /*
268  *                      N S E N D F I L E
269  */
270 
271 int
272 Nsendfile(int fromfd, int tofd, const char *buf, size_t count)
273 {
274     off_t offset;
275 #if defined(HAVE_SENDFILE)
276 #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6))
277     off_t sent;
278 #endif
279     register size_t nleft;
280     register ssize_t r;
281 
282     nleft = count;
283     while (nleft > 0) {
284 	offset = count - nleft;
285 #ifdef linux
286 	r = sendfile(tofd, fromfd, &offset, nleft);
287 #else
288 #ifdef __FreeBSD__
289 	r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0);
290 	if (r == 0)
291 	    r = sent;
292 #else
293 #if defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)	/* OS X */
294 	sent = nleft;
295 	r = sendfile(fromfd, tofd, offset, &sent, NULL, 0);
296 	if (r == 0)
297 	    r = sent;
298 #else
299 	/* Shouldn't happen. */
300 	r = -1;
301 	errno = ENOSYS;
302 #endif
303 #endif
304 #endif
305 	if (r < 0) {
306 	    switch (errno) {
307 		case EINTR:
308 		case EAGAIN:
309 		return count - nleft;
310 
311 		case ENOBUFS:
312 		case ENOMEM:
313 		return NET_SOFTERROR;
314 
315 		default:
316 		return NET_HARDERROR;
317 	    }
318 	} else if (r == 0)
319 	    return NET_SOFTERROR;
320 	nleft -= r;
321     }
322     return count;
323 #else /* HAVE_SENDFILE */
324     errno = ENOSYS;	/* error if somehow get called without HAVE_SENDFILE */
325     return -1;
326 #endif /* HAVE_SENDFILE */
327 }
328 
329 /*************************************************************************/
330 
331 /**
332  * getsock_tcp_mss - Returns the MSS size for TCP
333  *
334  */
335 
336 int
337 getsock_tcp_mss(int inSock)
338 {
339     int             mss = 0;
340 
341     int             rc;
342     socklen_t       len;
343 
344     assert(inSock >= 0); /* print error and exit if this is not true */
345 
346     /* query for mss */
347     len = sizeof(mss);
348     rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char *)&mss, &len);
349     if (rc == -1) {
350 	perror("getsockopt TCP_MAXSEG");
351 	return -1;
352     }
353 
354     return mss;
355 }
356 
357 
358 
359 /*************************************************************/
360 
361 /* sets TCP_NODELAY and TCP_MAXSEG if requested */
362 // XXX: This function is not being used.
363 
364 int
365 set_tcp_options(int sock, int no_delay, int mss)
366 {
367     socklen_t len;
368     int rc;
369     int new_mss;
370 
371     if (no_delay == 1) {
372         len = sizeof(no_delay);
373         rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&no_delay, len);
374         if (rc == -1) {
375             perror("setsockopt TCP_NODELAY");
376             return -1;
377         }
378     }
379 #ifdef TCP_MAXSEG
380     if (mss > 0) {
381         len = sizeof(new_mss);
382         assert(sock != -1);
383 
384         /* set */
385         new_mss = mss;
386         len = sizeof(new_mss);
387         rc = setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char *)&new_mss, len);
388         if (rc == -1) {
389             perror("setsockopt TCP_MAXSEG");
390             return -1;
391         }
392         /* verify results */
393         rc = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char *)&new_mss, &len);
394         if (rc == -1) {
395             perror("getsockopt TCP_MAXSEG");
396             return -1;
397         }
398         if (new_mss != mss) {
399             perror("setsockopt value mismatch");
400             return -1;
401         }
402     }
403 #endif
404     return 0;
405 }
406 
407 /****************************************************************************/
408 
409 int
410 setnonblocking(int fd, int nonblocking)
411 {
412     int flags, newflags;
413 
414     flags = fcntl(fd, F_GETFL, 0);
415     if (flags < 0) {
416         perror("fcntl(F_GETFL)");
417         return -1;
418     }
419     if (nonblocking)
420 	newflags = flags | (int) O_NONBLOCK;
421     else
422 	newflags = flags & ~((int) O_NONBLOCK);
423     if (newflags != flags)
424 	if (fcntl(fd, F_SETFL, newflags) < 0) {
425 	    perror("fcntl(F_SETFL)");
426 	    return -1;
427 	}
428     return 0;
429 }
430 
431 /****************************************************************************/
432 
433 int
434 getsockdomain(int sock)
435 {
436     struct sockaddr sa;
437     socklen_t len = sizeof(sa);
438 
439     if (getsockname(sock, &sa, &len) < 0)
440 	return -1;
441     return sa.sa_family;
442 }
443