1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /**************************************************************************
25 Connections
26
27 **************************************************************************/
28 #include "P_Net.h"
29
30 #define SET_NO_LINGER
31 // set in the OS
32 // #define RECV_BUF_SIZE (1024*64)
33 // #define SEND_BUF_SIZE (1024*64)
34 #define FIRST_RANDOM_PORT 16000
35 #define LAST_RANDOM_PORT 32000
36
37 #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y))
38
39 #if TS_USE_TPROXY
40 #if !defined(IP_TRANSPARENT)
41 unsigned int const IP_TRANSPARENT = 19;
42 #endif
43 #endif
44
45 //
46 // Functions
47 //
48 int
setup_mc_send(sockaddr const * mc_addr,sockaddr const * my_addr,bool non_blocking,unsigned char mc_ttl,bool mc_loopback,Continuation * c)49 Connection::setup_mc_send(sockaddr const *mc_addr, sockaddr const *my_addr, bool non_blocking, unsigned char mc_ttl,
50 bool mc_loopback, Continuation *c)
51 {
52 (void)c;
53 ink_assert(fd == NO_FD);
54 int res = 0;
55 int enable_reuseaddr = 1;
56 in_addr_t mc_if = ats_ip4_addr_cast(my_addr);
57
58 if ((res = socketManager.socket(my_addr->sa_family, SOCK_DGRAM, 0)) < 0) {
59 goto Lerror;
60 }
61
62 fd = res;
63
64 if ((res = safe_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&enable_reuseaddr), sizeof(enable_reuseaddr)) <
65 0)) {
66 goto Lerror;
67 }
68
69 if ((res = socketManager.ink_bind(fd, my_addr, ats_ip_size(my_addr), IPPROTO_UDP)) < 0) {
70 goto Lerror;
71 }
72
73 ats_ip_copy(&addr, mc_addr);
74
75 if ((res = safe_fcntl(fd, F_SETFD, FD_CLOEXEC)) < 0) {
76 goto Lerror;
77 }
78
79 if (non_blocking) {
80 if ((res = safe_nonblocking(fd)) < 0) {
81 goto Lerror;
82 }
83 }
84
85 // Set MultiCast TTL to specified value
86 if ((res = safe_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast<char *>(&mc_ttl), sizeof(mc_ttl)) < 0)) {
87 goto Lerror;
88 }
89
90 // Set MultiCast Interface to specified value
91 if ((res = safe_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast<char *>(&mc_if), sizeof(mc_if)) < 0)) {
92 goto Lerror;
93 }
94
95 // Disable MultiCast loopback if requested
96 if (!mc_loopback) {
97 char loop = 0;
98
99 if ((res = safe_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)) {
100 goto Lerror;
101 }
102 }
103 return 0;
104
105 Lerror:
106 if (fd != NO_FD) {
107 close();
108 }
109 return res;
110 }
111
112 int
setup_mc_receive(sockaddr const * mc_addr,sockaddr const * my_addr,bool non_blocking,Connection * sendChan,Continuation * c)113 Connection::setup_mc_receive(sockaddr const *mc_addr, sockaddr const *my_addr, bool non_blocking, Connection *sendChan,
114 Continuation *c)
115 {
116 ink_assert(fd == NO_FD);
117 (void)sendChan;
118 (void)c;
119 int res = 0;
120 int enable_reuseaddr = 1;
121 IpAddr inaddr_any(INADDR_ANY);
122
123 if ((res = socketManager.socket(mc_addr->sa_family, SOCK_DGRAM, 0)) < 0) {
124 goto Lerror;
125 }
126
127 fd = res;
128
129 if ((res = safe_fcntl(fd, F_SETFD, FD_CLOEXEC)) < 0) {
130 goto Lerror;
131 }
132
133 if ((res = safe_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&enable_reuseaddr), sizeof(enable_reuseaddr)) <
134 0)) {
135 goto Lerror;
136 }
137
138 addr.assign(inaddr_any, ats_ip_port_cast(mc_addr));
139
140 if ((res = socketManager.ink_bind(fd, &addr.sa, ats_ip_size(&addr.sa), IPPROTO_TCP)) < 0) {
141 goto Lerror;
142 }
143
144 if (non_blocking) {
145 if ((res = safe_nonblocking(fd)) < 0) {
146 goto Lerror;
147 }
148 }
149
150 if (ats_is_ip4(&addr)) {
151 struct ip_mreq mc_request;
152 // Add ourselves to the MultiCast group
153 mc_request.imr_multiaddr.s_addr = ats_ip4_addr_cast(mc_addr);
154 mc_request.imr_interface.s_addr = ats_ip4_addr_cast(my_addr);
155
156 if ((res = safe_setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char *>(&mc_request), sizeof(mc_request)) < 0)) {
157 goto Lerror;
158 }
159 }
160 return 0;
161
162 Lerror:
163 if (fd != NO_FD) {
164 close();
165 }
166 return res;
167 }
168
169 namespace
170 {
171 /** Struct to make cleaning up resources easier.
172
173 By default, the @a method is invoked on the @a object when
174 this object is destructed. This can be prevented by calling
175 the @c reset method.
176
177 This is not overly useful in the allocate, check, return case
178 but very handy if there are
179 - multiple resources (each can have its own cleaner)
180 - multiple checks against the resource
181 In such cases, rather than trying to track all the resources
182 that might need cleaned up, you can set up a cleaner at allocation
183 and only have to deal with them on success, which is generally
184 singular.
185
186 @code
187 self::some_method (...) {
188 /// allocate resource
189 cleaner<self> clean_up(this, &self::cleanup);
190 // modify or check the resource
191 if (fail) return FAILURE; // cleanup() is called
192 /// success!
193 clean_up.reset(); // cleanup() not called after this
194 return SUCCESS;
195 @endcode
196 */
197 template <typename T> struct cleaner {
198 T *obj; ///< Object instance.
199 using method = void (T::*)(); ///< Method signature.
200 method m;
201
cleaner__anon33889ada0111::cleaner202 cleaner(T *_obj, method _method) : obj(_obj), m(_method) {}
~cleaner__anon33889ada0111::cleaner203 ~cleaner()
204 {
205 if (obj) {
206 (obj->*m)();
207 }
208 }
209 void
reset__anon33889ada0111::cleaner210 reset()
211 {
212 obj = nullptr;
213 }
214 };
215 } // namespace
216
217 /** Default options.
218
219 @internal This structure is used to reduce the number of places in
220 which the defaults are set. Originally the argument defaulted to
221 @c nullptr which meant that the defaults had to be encoded in any
222 methods that used it as well as the @c NetVCOptions
223 constructor. Now they are controlled only in the latter and not in
224 any of the methods. This makes handling global default values
225 (such as @c RECV_BUF_SIZE) more robust. It doesn't have to be
226 checked in the method, only in the @c NetVCOptions constructor.
227
228 The methods are simpler because they never have to check for the
229 presence of the options, yet the clients aren't inconvenienced
230 because a default value for the argument is provided. Further,
231 clients can pass temporaries and not have to declare a variable in
232 order to tweak options.
233 */
234 NetVCOptions const Connection::DEFAULT_OPTIONS;
235
236 int
open(NetVCOptions const & opt)237 Connection::open(NetVCOptions const &opt)
238 {
239 ink_assert(fd == NO_FD);
240 int enable_reuseaddr = 1; // used for sockopt setting
241 int res = 0; // temp result
242 IpEndpoint local_addr;
243 sock_type = NetVCOptions::USE_UDP == opt.ip_proto ? SOCK_DGRAM : SOCK_STREAM;
244 int family;
245
246 // Need to do address calculations first, so we can determine the
247 // address family for socket creation.
248 ink_zero(local_addr);
249
250 bool is_any_address = false;
251 if (NetVCOptions::FOREIGN_ADDR == opt.addr_binding || NetVCOptions::INTF_ADDR == opt.addr_binding) {
252 // Same for now, transparency for foreign addresses must be handled
253 // *after* the socket is created, and we need to do this calculation
254 // before the socket to get the IP family correct.
255 ink_release_assert(opt.local_ip.isValid());
256 local_addr.assign(opt.local_ip, htons(opt.local_port));
257 family = opt.local_ip.family();
258 } else {
259 // No local address specified, so use family option if possible.
260 family = ats_is_ip(opt.ip_family) ? opt.ip_family : AF_INET;
261 local_addr.setToAnyAddr(family);
262 is_any_address = true;
263 local_addr.port() = htons(opt.local_port);
264 }
265
266 res = socketManager.socket(family, sock_type, 0);
267 if (-1 == res) {
268 return -errno;
269 }
270
271 fd = res;
272 // mark fd for close until we succeed.
273 cleaner<Connection> cleanup(this, &Connection::_cleanup);
274
275 // Try setting the various socket options, if requested.
276
277 if (-1 == safe_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&enable_reuseaddr), sizeof(enable_reuseaddr))) {
278 return -errno;
279 }
280
281 if (NetVCOptions::FOREIGN_ADDR == opt.addr_binding) {
282 static char const *const DEBUG_TEXT = "::open setsockopt() IP_TRANSPARENT";
283 #if TS_USE_TPROXY
284 int value = 1;
285 if (-1 == safe_setsockopt(fd, SOL_IP, TS_IP_TRANSPARENT, reinterpret_cast<char *>(&value), sizeof(value))) {
286 Debug("socket", "%s - fail %d:%s", DEBUG_TEXT, errno, strerror(errno));
287 return -errno;
288 } else {
289 Debug("socket", "%s set", DEBUG_TEXT);
290 }
291 #else
292 Debug("socket", "%s - requested but TPROXY not configured", DEBUG_TEXT);
293 #endif
294 }
295
296 if (!opt.f_blocking_connect && -1 == safe_nonblocking(fd)) {
297 return -errno;
298 }
299
300 if (opt.socket_recv_bufsize > 0) {
301 if (socketManager.set_rcvbuf_size(fd, opt.socket_recv_bufsize)) {
302 // Round down until success
303 int rbufsz = ROUNDUP(opt.socket_recv_bufsize, 1024);
304 while (rbufsz && !socketManager.set_rcvbuf_size(fd, rbufsz)) {
305 rbufsz -= 1024;
306 }
307 Debug("socket", "::open: recv_bufsize = %d of %d", rbufsz, opt.socket_recv_bufsize);
308 }
309 }
310 if (opt.socket_send_bufsize > 0) {
311 if (socketManager.set_sndbuf_size(fd, opt.socket_send_bufsize)) {
312 // Round down until success
313 int sbufsz = ROUNDUP(opt.socket_send_bufsize, 1024);
314 while (sbufsz && !socketManager.set_sndbuf_size(fd, sbufsz)) {
315 sbufsz -= 1024;
316 }
317 Debug("socket", "::open: send_bufsize = %d of %d", sbufsz, opt.socket_send_bufsize);
318 }
319 }
320
321 // apply dynamic options
322 apply_options(opt);
323
324 if (local_addr.port() || !is_any_address) {
325 if (-1 == socketManager.ink_bind(fd, &local_addr.sa, ats_ip_size(&local_addr.sa))) {
326 return -errno;
327 }
328 }
329
330 cleanup.reset();
331 is_bound = true;
332 return 0;
333 }
334
335 int
connect(sockaddr const * target,NetVCOptions const & opt)336 Connection::connect(sockaddr const *target, NetVCOptions const &opt)
337 {
338 ink_assert(fd != NO_FD);
339 ink_assert(is_bound);
340 ink_assert(!is_connected);
341
342 int res;
343
344 if (target != nullptr) {
345 this->setRemote(target);
346 }
347
348 // apply dynamic options with this.addr initialized
349 apply_options(opt);
350
351 cleaner<Connection> cleanup(this, &Connection::_cleanup); // mark for close until we succeed.
352
353 if (opt.f_tcp_fastopen && !opt.f_blocking_connect) {
354 // TCP Fast Open is (effectively) a non-blocking connect, so set the
355 // return value we would see in that case.
356 errno = EINPROGRESS;
357 res = -1;
358 } else {
359 res = ::connect(fd, &this->addr.sa, ats_ip_size(&this->addr.sa));
360 }
361
362 // It's only really an error if either the connect was blocking
363 // or it wasn't blocking and the error was other than EINPROGRESS.
364 // (Is EWOULDBLOCK ok? Does that start the connect?)
365 // We also want to handle the cases where the connect blocking
366 // and IO blocking differ, by turning it on or off as needed.
367 if (-1 == res && (opt.f_blocking_connect || !(EINPROGRESS == errno || EWOULDBLOCK == errno))) {
368 return -errno;
369 } else if (opt.f_blocking_connect && !opt.f_blocking) {
370 if (-1 == safe_nonblocking(fd)) {
371 return -errno;
372 }
373 } else if (!opt.f_blocking_connect && opt.f_blocking) {
374 if (-1 == safe_blocking(fd)) {
375 return -errno;
376 }
377 }
378
379 cleanup.reset();
380
381 // Only mark this connection as connected if we successfully called connect(2). When we
382 // do the TCP Fast Open later, we need to track this accurately.
383 is_connected = !(opt.f_tcp_fastopen && !opt.f_blocking_connect);
384 return 0;
385 }
386
387 void
_cleanup()388 Connection::_cleanup()
389 {
390 this->close();
391 }
392
393 void
apply_options(NetVCOptions const & opt)394 Connection::apply_options(NetVCOptions const &opt)
395 {
396 // Set options which can be changed after a connection is established
397 // ignore other changes
398 if (SOCK_STREAM == sock_type) {
399 if (opt.sockopt_flags & NetVCOptions::SOCK_OPT_NO_DELAY) {
400 safe_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, SOCKOPT_ON, sizeof(int));
401 Debug("socket", "::open: setsockopt() TCP_NODELAY on socket");
402 }
403 if (opt.sockopt_flags & NetVCOptions::SOCK_OPT_KEEP_ALIVE) {
404 safe_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, SOCKOPT_ON, sizeof(int));
405 Debug("socket", "::open: setsockopt() SO_KEEPALIVE on socket");
406 }
407 if (opt.sockopt_flags & NetVCOptions::SOCK_OPT_LINGER_ON) {
408 struct linger l;
409 l.l_onoff = 1;
410 l.l_linger = 0;
411 safe_setsockopt(fd, SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>(&l), sizeof(l));
412 Debug("socket", "::open:: setsockopt() turn on SO_LINGER on socket");
413 }
414 }
415
416 #if TS_HAS_SO_MARK
417 if (opt.sockopt_flags & NetVCOptions::SOCK_OPT_PACKET_MARK) {
418 uint32_t mark = opt.packet_mark;
419 safe_setsockopt(fd, SOL_SOCKET, SO_MARK, reinterpret_cast<char *>(&mark), sizeof(uint32_t));
420 }
421 #endif
422
423 #if TS_HAS_IP_TOS
424 if (opt.sockopt_flags & NetVCOptions::SOCK_OPT_PACKET_TOS) {
425 uint32_t tos = opt.packet_tos;
426 if (addr.isIp4()) {
427 safe_setsockopt(fd, IPPROTO_IP, IP_TOS, reinterpret_cast<char *>(&tos), sizeof(uint32_t));
428 } else if (addr.isIp6()) {
429 safe_setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, reinterpret_cast<char *>(&tos), sizeof(uint32_t));
430 }
431 }
432 #endif
433 }
434