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