]> sjero.net Git - iperf/blob - src/PerfSocket.cpp
Support for UDP-Lite in iperf
[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     //  UDP-Lite specific options
161     if ( inSettings->mProtocol == kProto_UDPLITE ) {
162         /* we set the checksum coverage for both directions */
163         rc = setsockopt(inSettings->mSock, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV,
164                         &inSettings->cscov, len);
165         WARN_errno(rc == SOCKET_ERROR, "setsockopt UDPLITE_SEND_CSCOV");
166
167         rc = setsockopt(inSettings->mSock, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV,
168                         &inSettings->cscov, len);
169
170         WARN_errno(rc == SOCKET_ERROR, "setsockopt UDPLITE_RECV_CSCOV");
171     }
172
173     // reuse the address, so we can run if a former server was killed off
174     if (inSettings->mThreadMode == kMode_Listener) {
175         val = 1;
176         rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, &val, len);
177         WARN_errno( rc == SOCKET_ERROR, "setsockopt SO_REUSEADDR" );
178     }
179 }
180
181 void MakeSocket(thread_Settings *inSettings)
182 {
183         struct addrinfo *local = NULL, *src,
184                         *remote = NULL, *dst, hints;
185         char            port[6];
186         int             rc, socktype = sockType(inSettings->mProtocol);
187
188         assert(inSettings->mLocalhost || inSettings->mHost);
189
190         memset(&inSettings->local, 0, sizeof(inSettings->local));
191         memset(&inSettings->peer,  0, sizeof(inSettings->peer));
192         sprintf(port, "%u", inSettings->mPort);
193
194         /*
195          *      Set up address hint structure
196          */
197         memset(&hints, 0, sizeof(hints));
198         hints.ai_family   = inSettings->mSockAF;
199         hints.ai_socktype = socktype;
200         /*
201          * CHEAT: getaddrinfo does not support SOCK_DCCP and using a zero
202          * ai_socktype will return IPv4 addresses first (which is bad on
203          * a dual-stack host). Pretend here to be UDP - this usually works.
204          */
205         if (inSettings->mProtocol == IPPROTO_DCCP)
206                 hints.ai_socktype = SOCK_DGRAM;
207
208         /* only use addresses available on the host */
209         hints.ai_flags = AI_ADDRCONFIG;
210         if (inSettings->mSockAF == AF_INET6)
211                 /* use v4-mapped-v6 if no v6 addresses found */
212                 hints.ai_flags |= AI_V4MAPPED | AI_ALL;
213
214         /*
215          *      Obtain local/remote address information
216          */
217         if (inSettings->mLocalhost || inSettings->mThreadMode == kMode_Listener) {
218                 if (inSettings->mLocalhost == NULL)
219                         hints.ai_flags |= AI_PASSIVE;
220                 if ((rc = getaddrinfo(inSettings->mLocalhost, port, &hints, &local)))
221                         die("Can not resolve local address %s#%s: %s",
222                             inSettings->mLocalhost ? : "(local)", port, gai_strerror(rc));
223         }
224
225         if (inSettings->mHost && inSettings->mThreadMode != kMode_Listener) {
226                 if ((rc = getaddrinfo(inSettings->mHost, port, &hints, &remote)))
227                         die("Can not resolve peer address %s#%s: %s",
228                             inSettings->mHost, port, gai_strerror(rc));
229         }
230
231         /*
232          *      Iterate over all src/dst combination, exhausting dst first
233          */
234         for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
235                 if (src && src->ai_family == AF_INET &&
236                     dst && dst->ai_family == AF_INET6)
237                         goto get_next_dst; /* v4 -> v6 is not possible */
238
239                 inSettings->mSockAF = src ? src->ai_family : dst->ai_family;
240                 inSettings->mSock   = socket(inSettings->mSockAF, socktype,
241                                              inSettings->mProtocol);
242                 if (inSettings->mSock < 0)
243                         goto get_next_dst;
244
245                 SetSocketOptions(inSettings);
246
247                 if (src) {
248                         if (bind(inSettings->mSock, src->ai_addr, src->ai_addrlen) < 0) {
249                                 close(inSettings->mSock);
250                                 goto get_next_src;
251                         }
252                         if (!dst)
253                                 break;  /* bind-only completed successfully */
254                 }
255
256                 if (dst && connect(inSettings->mSock, dst->ai_addr, dst->ai_addrlen) == 0)
257                         break;          /* connection completed successfully */
258                 close(inSettings->mSock);
259 get_next_dst:
260                 if (dst && (dst = dst->ai_next))
261                         continue;
262 get_next_src:
263                 if (src && (src = src->ai_next))
264                         dst = remote;   /* restart inner loop */
265         }
266
267         if (src == NULL && dst == NULL)
268                 die("Can not create %s socket", protoName(inSettings->mProtocol));
269         if (src) {
270                 if (SockAddr_isMulticast(src->ai_addr))
271                         setMulticast(inSettings);
272                 memcpy(&inSettings->local, src->ai_addr, src->ai_addrlen);
273         }
274         if (dst) {
275                 if (SockAddr_isMulticast(dst->ai_addr))
276                         setMulticast(inSettings);
277                 memcpy(&inSettings->peer, dst->ai_addr, dst->ai_addrlen);
278         }
279
280         if (local)
281                 freeaddrinfo(local);
282         if (remote)
283                 freeaddrinfo(remote);
284
285         if (isMulticast(inSettings) && !isConnectionLess(inSettings))
286                 die("Can not use %s with multicast.", protoName(inSettings->mProtocol));
287 }