]> sjero.net Git - iperf/blob - src/PerfSocket.cpp
TCP Congestion Control Module via options
[iperf] / src / PerfSocket.cpp
1 /*--------------------------------------------------------------- 
2  * Copyright (c) 1999,2000,2001,2002,2003                              
3  * The Board of Trustees of the University of Illinois            
4  * All Rights Reserved.                                           
5  *--------------------------------------------------------------- 
6  * Permission is hereby granted, free of charge, to any person    
7  * obtaining a copy of this software (Iperf) and associated       
8  * documentation files (the "Software"), to deal in the Software  
9  * without restriction, including without limitation the          
10  * rights to use, copy, modify, merge, publish, distribute,        
11  * sublicense, and/or sell copies of the Software, and to permit     
12  * persons to whom the Software is furnished to do
13  * so, subject to the following conditions: 
14  *
15  *     
16  * Redistributions of source code must retain the above 
17  * copyright notice, this list of conditions and 
18  * the following disclaimers. 
19  *
20  *     
21  * Redistributions in binary form must reproduce the above 
22  * copyright notice, this list of conditions and the following 
23  * disclaimers in the documentation and/or other materials 
24  * provided with the distribution. 
25  * 
26  *     
27  * Neither the names of the University of Illinois, NCSA, 
28  * nor the names of its contributors may be used to endorse 
29  * or promote products derived from this Software without
30  * specific prior written permission. 
31  * 
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
33  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
34  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
35  * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT 
36  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
37  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
38  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
40  * ________________________________________________________________
41  * National Laboratory for Applied Network Research 
42  * National Center for Supercomputing Applications 
43  * University of Illinois at Urbana-Champaign 
44  * http://www.ncsa.uiuc.edu
45  * ________________________________________________________________ 
46  *
47  * PerfSocket.cpp
48  * by Mark Gates <mgates@nlanr.net>
49  *    Ajay Tirumala <tirumala@ncsa.uiuc.edu>
50  * -------------------------------------------------------------------
51  * Has routines the Client and Server classes use in common for
52  * performance testing the network.
53  * Changes in version 1.2.0
54  *     for extracting data from files
55  * -------------------------------------------------------------------
56  * headers
57  * uses
58  *   <stdlib.h>
59  *   <stdio.h>
60  *   <string.h>
61  *
62  *   <sys/types.h>
63  *   <sys/socket.h>
64  *   <unistd.h>
65  *
66  *   <arpa/inet.h>
67  *   <netdb.h>
68  *   <netinet/in.h>
69  *   <sys/socket.h>
70  * ------------------------------------------------------------------- */
71 #define HEADERS()
72
73 #include "headers.h"
74
75 #include "PerfSocket.hpp"
76 #include "SocketAddr.h"
77 #include "util.h"
78
79 /* -------------------------------------------------------------------
80  * Set socket options before the listen() or connect() calls.
81  * These are optional performance tuning factors.
82  * ------------------------------------------------------------------- */
83 void SetSocketOptions( thread_Settings *inSettings )
84 {
85     int rc, val;
86     Socklen_t len = sizeof(int);
87
88     // check if we're sending multicast, and set TTL
89     if (isMulticast(inSettings) && inSettings->mTTL > 0) {
90         val = inSettings->mTTL;
91         if (SockAddr_isIPv6(&inSettings->local))
92             rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
93                             &val, len);
94         else
95             rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
96                             &val, len);
97
98         WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
99     }
100
101
102     // set IP TOS (type-of-service) field
103 #ifdef IP_TOS
104     if ( inSettings->mTOS > 0 ) {
105         val = inSettings->mTOS;
106         rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS, &val, len );
107         WARN_errno( rc == SOCKET_ERROR, "setsockopt IP_TOS" );
108     }
109 #endif
110
111
112     // TCP-specific options
113     if ( inSettings->mProtocol == kProto_TCP ) {
114
115         // set the TCP window size (socket buffer sizes)
116         // must occur before call to accept() for large window sizes
117         setsock_tcp_windowsize(inSettings->mSock, inSettings->mWinSize,
118                                inSettings->mThreadMode == kMode_Client);
119         setsock_tcp_mss( inSettings->mSock, inSettings->mMSS );
120
121 #ifdef TCP_NODELAY
122         if ( isNoDelay( inSettings ) ) {
123             val = 1;
124             rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY,
125                              &val, len );
126             WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_NODELAY" );
127         }
128 #endif
129         if ( inSettings->congAlgo ) {
130             len = strlen( inSettings->congAlgo );
131             rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION,
132                              inSettings->congAlgo , len );
133             WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_CONGESTION" );
134         }
135
136     } else {
137         rc = set_buffer_sock_size(inSettings->mSock, inSettings->mWinSize,
138                                   inSettings->mThreadMode == kMode_Client);
139         WARN_errno( rc < 0 , "setsockopt for buffer size" );
140     }
141     // DCCP-specific options
142     if ( inSettings->mProtocol == kProto_DCCP ) {
143         /*
144          * We use the service code SC:PERF (0x50455246) from
145          * draft-fairhurst-dccp-serv-codes to identify this service.
146          */
147         val = htonl(0x50455246);                        /* ALWAYS use htonl */
148         rc = setsockopt( inSettings->mSock, SOL_DCCP, DCCP_SOCKOPT_SERVICE,
149                          &val, len );
150         WARN_errno( rc == SOCKET_ERROR, "setsockopt DCCP_SOCKOPT_SERVICE" );
151     }
152
153     // reuse the address, so we can run if a former server was killed off
154     if (inSettings->mThreadMode == kMode_Listener) {
155         val = 1;
156         rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, &val, len);
157         WARN_errno( rc == SOCKET_ERROR, "setsockopt SO_REUSEADDR" );
158     }
159 }
160
161 void MakeSocket(thread_Settings *inSettings)
162 {
163         struct addrinfo *local = NULL, *src,
164                         *remote = NULL, *dst, hints;
165         char            port[6];
166         int             rc, socktype = sockType(inSettings->mProtocol);
167
168         assert(inSettings->mLocalhost || inSettings->mHost);
169
170         memset(&inSettings->local, 0, sizeof(inSettings->local));
171         memset(&inSettings->peer,  0, sizeof(inSettings->peer));
172         sprintf(port, "%u", inSettings->mPort);
173
174         /*
175          *      Set up address hint structure
176          */
177         memset(&hints, 0, sizeof(hints));
178         hints.ai_family   = inSettings->mSockAF;
179         hints.ai_socktype = socktype;
180         /*
181          * CHEAT: getaddrinfo does not support SOCK_DCCP and using a zero
182          * ai_socktype will return IPv4 addresses first (which is bad on
183          * a dual-stack host). Pretend here to be UDP - this usually works.
184          */
185         if (inSettings->mProtocol == IPPROTO_DCCP)
186                 hints.ai_socktype = SOCK_DGRAM;
187
188         /* only use addresses available on the host */
189         hints.ai_flags = AI_ADDRCONFIG;
190         if (inSettings->mSockAF == AF_INET6)
191                 /* use v4-mapped-v6 if no v6 addresses found */
192                 hints.ai_flags |= AI_V4MAPPED | AI_ALL;
193
194         /*
195          *      Obtain local/remote address information
196          */
197         if (inSettings->mLocalhost || inSettings->mThreadMode == kMode_Listener) {
198                 if (inSettings->mLocalhost == NULL)
199                         hints.ai_flags |= AI_PASSIVE;
200                 if ((rc = getaddrinfo(inSettings->mLocalhost, port, &hints, &local)))
201                         die("Can not resolve local address %s#%s: %s",
202                             inSettings->mLocalhost ? : "(local)", port, gai_strerror(rc));
203         }
204
205         if (inSettings->mHost && inSettings->mThreadMode != kMode_Listener) {
206                 if ((rc = getaddrinfo(inSettings->mHost, port, &hints, &remote)))
207                         die("Can not resolve peer address %s#%s: %s",
208                             inSettings->mHost, port, gai_strerror(rc));
209         }
210
211         /*
212          *      Iterate over all src/dst combination, exhausting dst first
213          */
214         for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
215                 if (src && src->ai_family == AF_INET &&
216                     dst && dst->ai_family == AF_INET6)
217                         goto get_next_dst; /* v4 -> v6 is not possible */
218
219                 inSettings->mSockAF = src ? src->ai_family : dst->ai_family;
220                 inSettings->mSock   = socket(inSettings->mSockAF, socktype,
221                                              inSettings->mProtocol);
222                 if (inSettings->mSock < 0)
223                         goto get_next_dst;
224
225                 SetSocketOptions(inSettings);
226
227                 if (src) {
228                         if (bind(inSettings->mSock, src->ai_addr, src->ai_addrlen) < 0) {
229                                 close(inSettings->mSock);
230                                 goto get_next_src;
231                         }
232                         if (!dst)
233                                 break;  /* bind-only completed successfully */
234                 }
235
236                 if (dst && connect(inSettings->mSock, dst->ai_addr, dst->ai_addrlen) == 0)
237                         break;          /* connection completed successfully */
238                 close(inSettings->mSock);
239 get_next_dst:
240                 if (dst && (dst = dst->ai_next))
241                         continue;
242 get_next_src:
243                 if (src && (src = src->ai_next))
244                         dst = remote;   /* restart inner loop */
245         }
246
247         if (src == NULL && dst == NULL)
248                 die("Can not create %s socket", protoName(inSettings->mProtocol));
249         if (src) {
250                 if (SockAddr_isMulticast(src->ai_addr))
251                         setMulticast(inSettings);
252                 memcpy(&inSettings->local, src->ai_addr, src->ai_addrlen);
253         }
254         if (dst) {
255                 if (SockAddr_isMulticast(dst->ai_addr))
256                         setMulticast(inSettings);
257                 memcpy(&inSettings->peer, dst->ai_addr, dst->ai_addrlen);
258         }
259
260         if (local)
261                 freeaddrinfo(local);
262         if (remote)
263                 freeaddrinfo(remote);
264
265         if (isMulticast(inSettings) && !isConnectionLess(inSettings))
266                 die("Can not use %s with multicast.", protoName(inSettings->mProtocol));
267 }