]> sjero.net Git - iperf/blob - src/PerfSocket.cpp
3a3b4561d69b9947859d69779cf8b94c418cb443
[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 || inSettings->mBindPort > 0) {
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         char            lport[6];
195         int             rc, socktype = sockType(inSettings->mProtocol);
196
197         assert(inSettings->mLocalhost || inSettings->mHost);
198
199         memset(&inSettings->local, 0, sizeof(inSettings->local));
200         memset(&inSettings->peer,  0, sizeof(inSettings->peer));
201         sprintf(port, "%u", inSettings->mPort);
202         if(inSettings->mBindPort > 0){
203                 sprintf(lport, "%u", inSettings->mBindPort);
204         }else{
205                 sprintf(lport, "%u", inSettings->mPort);
206         }
207
208         /*
209          *      Set up address hint structure
210          */
211         memset(&hints, 0, sizeof(hints));
212         hints.ai_family   = inSettings->mSockAF;
213         hints.ai_socktype = socktype;
214         /*
215          * CHEAT: getaddrinfo does not support SOCK_DCCP and using a zero
216          * ai_socktype will return IPv4 addresses first (which is bad on
217          * a dual-stack host). Pretend here to be UDP - this usually works.
218          */
219         if (inSettings->mProtocol == IPPROTO_DCCP)
220                 hints.ai_socktype = SOCK_DGRAM;
221
222         /* only use addresses available on the host */
223         hints.ai_flags = AI_ADDRCONFIG;
224         if (inSettings->mSockAF == AF_INET6)
225                 /* use v4-mapped-v6 if no v6 addresses found */
226                 hints.ai_flags |= AI_V4MAPPED | AI_ALL;
227
228         /*
229          *      Obtain local/remote address information
230          */
231         if (inSettings->mLocalhost || inSettings->mBindPort > 0 || inSettings->mThreadMode == kMode_Listener) {
232                 if (inSettings->mThreadMode == kMode_Listener)
233                         hints.ai_flags |= AI_PASSIVE;
234                 if ((rc = getaddrinfo(inSettings->mLocalhost, lport, &hints, &local)))
235                         die("Can not resolve local address %s#%s: %s",
236                             inSettings->mLocalhost ? : "(local)", lport, gai_strerror(rc));
237         }
238
239         if (inSettings->mHost && inSettings->mThreadMode != kMode_Listener) {
240                 if ((rc = getaddrinfo(inSettings->mHost, port, &hints, &remote)))
241                         die("Can not resolve peer address %s#%s: %s",
242                             inSettings->mHost, port, gai_strerror(rc));
243         }
244
245         /*
246          *      Iterate over all src/dst combination, exhausting dst first
247          */
248         for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
249                 if (src && src->ai_family == AF_INET &&
250                     dst && dst->ai_family == AF_INET6)
251                         goto get_next_dst; /* v4 -> v6 is not possible */
252
253                 inSettings->mSockAF = src ? src->ai_family : dst->ai_family;
254                 inSettings->mSock   = socket(inSettings->mSockAF, socktype,
255                                              inSettings->mProtocol);
256                 if (inSettings->mSock < 0)
257                         goto get_next_dst;
258
259                 SetSocketOptions(inSettings);
260
261                 if (src) {
262                         if (bind(inSettings->mSock, src->ai_addr, src->ai_addrlen) < 0) {
263                                 close(inSettings->mSock);
264                                 goto get_next_src;
265                         }
266                         if (!dst)
267                                 break;  /* bind-only completed successfully */
268                 }
269
270                 if (dst && connect(inSettings->mSock, dst->ai_addr, dst->ai_addrlen) == 0)
271                         break;          /* connection completed successfully */
272                 close(inSettings->mSock);
273 get_next_dst:
274                 if (dst && (dst = dst->ai_next))
275                         continue;
276 get_next_src:
277                 if (src && (src = src->ai_next))
278                         dst = remote;   /* restart inner loop */
279         }
280
281         if (src == NULL && dst == NULL)
282                 die("Can not create %s socket", protoName(inSettings->mProtocol));
283         if (src) {
284                 if (SockAddr_isMulticast(src->ai_addr))
285                         setMulticast(inSettings);
286                 memcpy(&inSettings->local, src->ai_addr, src->ai_addrlen);
287         }
288         if (dst) {
289                 if (SockAddr_isMulticast(dst->ai_addr))
290                         setMulticast(inSettings);
291                 memcpy(&inSettings->peer, dst->ai_addr, dst->ai_addrlen);
292         }
293
294         if (local)
295                 freeaddrinfo(local);
296         if (remote)
297                 freeaddrinfo(remote);
298
299         if (isMulticast(inSettings) && !isConnectionLess(inSettings))
300                 die("Can not use %s with multicast.", protoName(inSettings->mProtocol));
301 }