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