]> sjero.net Git - iperf/blob - src/PerfSocket.cpp
de88a753f8709600ddd91ee1b30da4e9b842511d
[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 the DiffServ codepoint for IPv4 TOS or IPv6 traffic class
103     if ( inSettings->mTOS > 0 ) {
104         val = inSettings->mTOS;
105 #if defined(IP_TOS)
106         if ( inSettings->mSockAF == AF_INET ) {
107           rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS, &val, len );
108           WARN_errno( rc == SOCKET_ERROR, "setsockopt IP_TOS" );
109         }
110 #endif
111 #if defined(IPV6_TCLASS)
112         if ( inSettings->mSockAF == AF_INET6 ) {
113           rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_TCLASS, &val, len );
114           WARN_errno( rc == SOCKET_ERROR, "setsockopt IPV6_TCLASS" );
115         }
116 #endif
117     }
118
119
120     // TCP-specific options
121     if ( inSettings->mProtocol == kProto_TCP ) {
122
123         // set the TCP window size (socket buffer sizes)
124         // must occur before call to accept() for large window sizes
125         setsock_tcp_windowsize(inSettings->mSock, inSettings->mWinSize,
126                                inSettings->mThreadMode == kMode_Client);
127         setsock_tcp_mss( inSettings->mSock, inSettings->mMSS );
128
129 #ifdef TCP_NODELAY
130         if ( isNoDelay( inSettings ) ) {
131             val = 1;
132             rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY,
133                              &val, len );
134             WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_NODELAY" );
135         }
136 #endif
137         if ( inSettings->congAlgo ) {
138             len = strlen( inSettings->congAlgo );
139             rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION,
140                              inSettings->congAlgo , len );
141             WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_CONGESTION" );
142         }
143
144     } else {
145         rc = set_buffer_sock_size(inSettings->mSock, inSettings->mWinSize,
146                                   inSettings->mThreadMode == kMode_Client);
147         WARN_errno( rc < 0 , "setsockopt for buffer size" );
148     }
149     // DCCP-specific options
150     if ( inSettings->mProtocol == kProto_DCCP ) {
151         /*
152          * We use the service code SC:PERF (0x50455246) from
153          * draft-fairhurst-dccp-serv-codes to identify this service.
154          */
155         val = htonl(0x50455246);                        /* ALWAYS use htonl */
156         rc = setsockopt( inSettings->mSock, SOL_DCCP, DCCP_SOCKOPT_SERVICE,
157                          &val, len );
158         WARN_errno( rc == SOCKET_ERROR, "setsockopt DCCP_SOCKOPT_SERVICE" );
159
160         if(inSettings->mCCID!=0){
161                 char cv=inSettings->mCCID;
162                 rc=setsockopt( inSettings->mSock, SOL_DCCP, DCCP_SOCKOPT_CCID,
163                          &cv, sizeof(cv));
164
165                 WARN_errno( rc == SOCKET_ERROR, "Error Setting CCID");
166         }
167     }
168     //  UDP-Lite specific options
169     if ( inSettings->mProtocol == kProto_UDPLITE ) {
170         /* we set the checksum coverage for both directions */
171         rc = setsockopt(inSettings->mSock, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV,
172                         &inSettings->cscov, len);
173         WARN_errno(rc == SOCKET_ERROR, "setsockopt UDPLITE_SEND_CSCOV");
174
175         rc = setsockopt(inSettings->mSock, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV,
176                         &inSettings->cscov, len);
177
178         WARN_errno(rc == SOCKET_ERROR, "setsockopt UDPLITE_RECV_CSCOV");
179     }
180
181     // reuse the address, so we can run if a former server was killed off
182     if (inSettings->mThreadMode == kMode_Listener) {
183         val = 1;
184         rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, &val, len);
185         WARN_errno( rc == SOCKET_ERROR, "setsockopt SO_REUSEADDR" );
186     }
187 }
188
189 void MakeSocket(thread_Settings *inSettings)
190 {
191         struct addrinfo *local = NULL, *src,
192                         *remote = NULL, *dst, hints;
193         char            port[6];
194         int             rc, socktype = sockType(inSettings->mProtocol);
195
196         assert(inSettings->mLocalhost || inSettings->mHost);
197
198         memset(&inSettings->local, 0, sizeof(inSettings->local));
199         memset(&inSettings->peer,  0, sizeof(inSettings->peer));
200         sprintf(port, "%u", inSettings->mPort);
201
202         /*
203          *      Set up address hint structure
204          */
205         memset(&hints, 0, sizeof(hints));
206         hints.ai_family   = inSettings->mSockAF;
207         hints.ai_socktype = socktype;
208         /*
209          * CHEAT: getaddrinfo does not support SOCK_DCCP and using a zero
210          * ai_socktype will return IPv4 addresses first (which is bad on
211          * a dual-stack host). Pretend here to be UDP - this usually works.
212          */
213         if (inSettings->mProtocol == IPPROTO_DCCP)
214                 hints.ai_socktype = SOCK_DGRAM;
215
216         /* only use addresses available on the host */
217         hints.ai_flags = AI_ADDRCONFIG;
218         if (inSettings->mSockAF == AF_INET6)
219                 /* use v4-mapped-v6 if no v6 addresses found */
220                 hints.ai_flags |= AI_V4MAPPED | AI_ALL;
221
222         /*
223          *      Obtain local/remote address information
224          */
225         if (inSettings->mLocalhost || inSettings->mThreadMode == kMode_Listener) {
226                 if (inSettings->mLocalhost == NULL)
227                         hints.ai_flags |= AI_PASSIVE;
228                 if ((rc = getaddrinfo(inSettings->mLocalhost, port, &hints, &local)))
229                         die("Can not resolve local address %s#%s: %s",
230                             inSettings->mLocalhost ? : "(local)", port, gai_strerror(rc));
231         }
232
233         if (inSettings->mHost && inSettings->mThreadMode != kMode_Listener) {
234                 if ((rc = getaddrinfo(inSettings->mHost, port, &hints, &remote)))
235                         die("Can not resolve peer address %s#%s: %s",
236                             inSettings->mHost, port, gai_strerror(rc));
237         }
238
239         /*
240          *      Iterate over all src/dst combination, exhausting dst first
241          */
242         for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
243                 if (src && src->ai_family == AF_INET &&
244                     dst && dst->ai_family == AF_INET6)
245                         goto get_next_dst; /* v4 -> v6 is not possible */
246
247                 inSettings->mSockAF = src ? src->ai_family : dst->ai_family;
248                 inSettings->mSock   = socket(inSettings->mSockAF, socktype,
249                                              inSettings->mProtocol);
250                 if (inSettings->mSock < 0)
251                         goto get_next_dst;
252
253                 SetSocketOptions(inSettings);
254
255                 if (src) {
256                         if (bind(inSettings->mSock, src->ai_addr, src->ai_addrlen) < 0) {
257                                 close(inSettings->mSock);
258                                 goto get_next_src;
259                         }
260                         if (!dst)
261                                 break;  /* bind-only completed successfully */
262                 }
263
264                 if (dst && connect(inSettings->mSock, dst->ai_addr, dst->ai_addrlen) == 0)
265                         break;          /* connection completed successfully */
266                 close(inSettings->mSock);
267 get_next_dst:
268                 if (dst && (dst = dst->ai_next))
269                         continue;
270 get_next_src:
271                 if (src && (src = src->ai_next))
272                         dst = remote;   /* restart inner loop */
273         }
274
275         if (src == NULL && dst == NULL)
276                 die("Can not create %s socket", protoName(inSettings->mProtocol));
277         if (src) {
278                 if (SockAddr_isMulticast(src->ai_addr))
279                         setMulticast(inSettings);
280                 memcpy(&inSettings->local, src->ai_addr, src->ai_addrlen);
281         }
282         if (dst) {
283                 if (SockAddr_isMulticast(dst->ai_addr))
284                         setMulticast(inSettings);
285                 memcpy(&inSettings->peer, dst->ai_addr, dst->ai_addrlen);
286         }
287
288         if (local)
289                 freeaddrinfo(local);
290         if (remote)
291                 freeaddrinfo(remote);
292
293         if (isMulticast(inSettings) && !isConnectionLess(inSettings))
294                 die("Can not use %s with multicast.", protoName(inSettings->mProtocol));
295 }