• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/iomgr/port.h"
22 
23 #ifdef GRPC_POSIX_SOCKET_UTILS_COMMON
24 
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <netinet/in.h>
30 
31 #include "src/core/lib/iomgr/socket_utils.h"
32 #include "src/core/lib/iomgr/socket_utils_posix.h"
33 #ifdef GRPC_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #else
36 #include <netinet/tcp.h>
37 #endif
38 #include <grpc/event_engine/endpoint_config.h>
39 #include <grpc/support/alloc.h>
40 #include <grpc/support/sync.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 
47 #include <string>
48 
49 #include "absl/log/check.h"
50 #include "absl/log/log.h"
51 #include "absl/strings/str_cat.h"
52 #include "src/core/lib/address_utils/sockaddr_utils.h"
53 #include "src/core/lib/iomgr/sockaddr.h"
54 #include "src/core/util/crash.h"
55 #include "src/core/util/strerror.h"
56 #include "src/core/util/string.h"
57 
58 // set a socket to use zerocopy
grpc_set_socket_zerocopy(int fd)59 grpc_error_handle grpc_set_socket_zerocopy(int fd) {
60 #ifdef GRPC_LINUX_ERRQUEUE
61   const int enable = 1;
62   auto err = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &enable, sizeof(enable));
63   if (err != 0) {
64     return GRPC_OS_ERROR(errno, "setsockopt(SO_ZEROCOPY)");
65   }
66   return absl::OkStatus();
67 #else
68   (void)fd;
69   return GRPC_OS_ERROR(ENOSYS, "setsockopt(SO_ZEROCOPY)");
70 #endif
71 }
72 
73 // set a socket to non blocking mode
grpc_set_socket_nonblocking(int fd,int non_blocking)74 grpc_error_handle grpc_set_socket_nonblocking(int fd, int non_blocking) {
75   int oldflags = fcntl(fd, F_GETFL, 0);
76   if (oldflags < 0) {
77     return GRPC_OS_ERROR(errno, "fcntl");
78   }
79 
80   if (non_blocking) {
81     oldflags |= O_NONBLOCK;
82   } else {
83     oldflags &= ~O_NONBLOCK;
84   }
85 
86   if (fcntl(fd, F_SETFL, oldflags) != 0) {
87     return GRPC_OS_ERROR(errno, "fcntl");
88   }
89 
90   return absl::OkStatus();
91 }
92 
grpc_set_socket_no_sigpipe_if_possible(int fd)93 grpc_error_handle grpc_set_socket_no_sigpipe_if_possible(int fd) {
94 #ifdef GRPC_HAVE_SO_NOSIGPIPE
95   int val = 1;
96   int newval;
97   socklen_t intlen = sizeof(newval);
98   if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) {
99     return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)");
100   }
101   if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) {
102     return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)");
103   }
104   if ((newval != 0) != (val != 0)) {
105     return GRPC_ERROR_CREATE("Failed to set SO_NOSIGPIPE");
106   }
107 #else
108   // Avoid unused parameter warning for conditional parameter
109   (void)fd;
110 #endif
111   return absl::OkStatus();
112 }
113 
grpc_set_socket_ip_pktinfo_if_possible(int fd)114 grpc_error_handle grpc_set_socket_ip_pktinfo_if_possible(int fd) {
115   // Use conditionally-important parameter to avoid warning
116   (void)fd;
117 #ifdef GRPC_HAVE_IP_PKTINFO
118   int get_local_ip = 1;
119   if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
120                       sizeof(get_local_ip))) {
121     return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)");
122   }
123 #endif
124   return absl::OkStatus();
125 }
126 
grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd)127 grpc_error_handle grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
128   // Use conditionally-important parameter to avoid warning
129   (void)fd;
130 #ifdef GRPC_HAVE_IPV6_RECVPKTINFO
131   int get_local_ip = 1;
132   if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
133                       sizeof(get_local_ip))) {
134     return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)");
135   }
136 #endif
137   return absl::OkStatus();
138 }
139 
grpc_set_socket_sndbuf(int fd,int buffer_size_bytes)140 grpc_error_handle grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) {
141   return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes,
142                          sizeof(buffer_size_bytes))
143              ? absl::OkStatus()
144              : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)");
145 }
146 
grpc_set_socket_rcvbuf(int fd,int buffer_size_bytes)147 grpc_error_handle grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) {
148   return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes,
149                          sizeof(buffer_size_bytes))
150              ? absl::OkStatus()
151              : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)");
152 }
153 
154 // set a socket to close on exec
grpc_set_socket_cloexec(int fd,int close_on_exec)155 grpc_error_handle grpc_set_socket_cloexec(int fd, int close_on_exec) {
156   int oldflags = fcntl(fd, F_GETFD, 0);
157   if (oldflags < 0) {
158     return GRPC_OS_ERROR(errno, "fcntl");
159   }
160 
161   if (close_on_exec) {
162     oldflags |= FD_CLOEXEC;
163   } else {
164     oldflags &= ~FD_CLOEXEC;
165   }
166 
167   if (fcntl(fd, F_SETFD, oldflags) != 0) {
168     return GRPC_OS_ERROR(errno, "fcntl");
169   }
170 
171   return absl::OkStatus();
172 }
173 
174 // set a socket to reuse old addresses
grpc_set_socket_reuse_addr(int fd,int reuse)175 grpc_error_handle grpc_set_socket_reuse_addr(int fd, int reuse) {
176   int val = (reuse != 0);
177   int newval;
178   socklen_t intlen = sizeof(newval);
179   if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
180     return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)");
181   }
182   if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) {
183     return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)");
184   }
185   if ((newval != 0) != val) {
186     return GRPC_ERROR_CREATE("Failed to set SO_REUSEADDR");
187   }
188 
189   return absl::OkStatus();
190 }
191 
192 // set a socket to reuse old addresses
grpc_set_socket_reuse_port(int fd,int reuse)193 grpc_error_handle grpc_set_socket_reuse_port(int fd, int reuse) {
194 #ifndef SO_REUSEPORT
195   return GRPC_ERROR_CREATE("SO_REUSEPORT unavailable on compiling system");
196 #else
197   int val = (reuse != 0);
198   int newval;
199   socklen_t intlen = sizeof(newval);
200   if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) {
201     return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)");
202   }
203   if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) {
204     return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)");
205   }
206   if ((newval != 0) != val) {
207     return GRPC_ERROR_CREATE("Failed to set SO_REUSEPORT");
208   }
209 
210   return absl::OkStatus();
211 #endif
212 }
213 
214 static gpr_once g_probe_so_reuesport_once = GPR_ONCE_INIT;
215 static int g_support_so_reuseport = false;
216 
probe_so_reuseport_once(void)217 void probe_so_reuseport_once(void) {
218   int s = socket(AF_INET, SOCK_STREAM, 0);
219   if (s < 0) {
220     // This might be an ipv6-only environment in which case 'socket(AF_INET,..)'
221     // call would fail. Try creating IPv6 socket in that case
222     s = socket(AF_INET6, SOCK_STREAM, 0);
223   }
224   if (s >= 0) {
225     g_support_so_reuseport = GRPC_LOG_IF_ERROR(
226         "check for SO_REUSEPORT", grpc_set_socket_reuse_port(s, 1));
227     close(s);
228   }
229 }
230 
grpc_is_socket_reuse_port_supported()231 bool grpc_is_socket_reuse_port_supported() {
232   gpr_once_init(&g_probe_so_reuesport_once, probe_so_reuseport_once);
233   return g_support_so_reuseport;
234 }
235 
236 // disable nagle
grpc_set_socket_low_latency(int fd,int low_latency)237 grpc_error_handle grpc_set_socket_low_latency(int fd, int low_latency) {
238   int val = (low_latency != 0);
239   int newval;
240   socklen_t intlen = sizeof(newval);
241   if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
242     return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)");
243   }
244   if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) {
245     return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)");
246   }
247   if ((newval != 0) != val) {
248     return GRPC_ERROR_CREATE("Failed to set TCP_NODELAY");
249   }
250   return absl::OkStatus();
251 }
252 
253 /* Set Differentiated Services Code Point (DSCP) */
grpc_set_socket_dscp(int fd,int dscp)254 grpc_error_handle grpc_set_socket_dscp(int fd, int dscp) {
255   if (dscp == grpc_core::PosixTcpOptions::kDscpNotSet) {
256     return absl::OkStatus();
257   }
258   // The TOS/TrafficClass byte consists of following bits:
259   // | 7 6 5 4 3 2 | 1 0 |
260   // |    DSCP     | ECN |
261   int value = dscp << 2;
262 
263   int optval;
264   socklen_t optlen = sizeof(optval);
265   // Get ECN bits from current IP_TOS value unless IPv6 only
266   if (0 == getsockopt(fd, IPPROTO_IP, IP_TOS, &optval, &optlen)) {
267     value |= (optval & 0x3);
268     if (0 != setsockopt(fd, IPPROTO_IP, IP_TOS, &value, sizeof(value))) {
269       return GRPC_OS_ERROR(errno, "setsockopt(IP_TOS)");
270     }
271   }
272   // Get ECN from current Traffic Class value if IPv6 is available
273   if (0 == getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &optval, &optlen)) {
274     value |= (optval & 0x3);
275     if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, sizeof(value))) {
276       return GRPC_OS_ERROR(errno, "setsockopt(IPV6_TCLASS)");
277     }
278   }
279   return absl::OkStatus();
280 }
281 
282 // The default values for TCP_USER_TIMEOUT are currently configured to be in
283 // line with the default values of KEEPALIVE_TIMEOUT as proposed in
284 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md
285 #define DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS 20000  // 20 seconds
286 #define DEFAULT_SERVER_TCP_USER_TIMEOUT_MS 20000  // 20 seconds
287 
288 static int g_default_client_tcp_user_timeout_ms =
289     DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS;
290 static int g_default_server_tcp_user_timeout_ms =
291     DEFAULT_SERVER_TCP_USER_TIMEOUT_MS;
292 static bool g_default_client_tcp_user_timeout_enabled = false;
293 static bool g_default_server_tcp_user_timeout_enabled = true;
294 
295 #if GPR_LINUX == 1
296 // For Linux, it will be detected to support TCP_USER_TIMEOUT
297 #ifndef TCP_USER_TIMEOUT
298 #define TCP_USER_TIMEOUT 18
299 #endif
300 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT 0
301 #else
302 // For non-Linux, TCP_USER_TIMEOUT will be used if TCP_USER_TIMEOUT is defined.
303 #ifdef TCP_USER_TIMEOUT
304 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT 0
305 #else
306 #define TCP_USER_TIMEOUT 0
307 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT -1
308 #endif  // TCP_USER_TIMEOUT
309 #endif  // GPR_LINUX == 1
310 
311 // Whether the socket supports TCP_USER_TIMEOUT option.
312 // (0: don't know, 1: support, -1: not support)
313 static std::atomic<int> g_socket_supports_tcp_user_timeout(
314     SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT);
315 
config_default_tcp_user_timeout(bool enable,int timeout,bool is_client)316 void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client) {
317   if (is_client) {
318     g_default_client_tcp_user_timeout_enabled = enable;
319     if (timeout > 0) {
320       g_default_client_tcp_user_timeout_ms = timeout;
321     }
322   } else {
323     g_default_server_tcp_user_timeout_enabled = enable;
324     if (timeout > 0) {
325       g_default_server_tcp_user_timeout_ms = timeout;
326     }
327   }
328 }
329 
330 // Set TCP_USER_TIMEOUT
331 // As documented in
332 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md, the
333 // default values for TCP_USER_TIMEOUT are currently configured to be in line
334 // with the default values of KEEPALIVE_TIMEOUT as proposed in
335 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md. In
336 // other words, by default, TCP_USER_TIMEOUT is disabled on clients (since
337 // keepalive is disabled on clients by default), and enabled on servers. To
338 // override the default settings of enabling/disabling TCP_USER_TIMEOUT, the
339 // value of KEEPALIVE_TIME on channel args is used. If present, a value of
340 // INT_MAX means that TCP_USER_TIMEOUT would be disabled, while any other value
341 // would enable TCP_USER_TIMEOUT. If TCP_USER_TIMEOUT is enabled, the default
342 // setting is 20 seconds (aligning with the default of KEEPALIVE_TIMEOUT). The
343 // KEEPALIVE_TIMEOUT channel arg overrides the value used for TCP_USER_TIMEOUT.
grpc_set_socket_tcp_user_timeout(int fd,const grpc_core::PosixTcpOptions & options,bool is_client)344 grpc_error_handle grpc_set_socket_tcp_user_timeout(
345     int fd, const grpc_core::PosixTcpOptions& options, bool is_client) {
346   // Use conditionally-important parameter to avoid warning
347   (void)fd;
348   (void)is_client;
349   if (g_socket_supports_tcp_user_timeout.load() >= 0) {
350     bool enable;
351     int timeout;
352     if (is_client) {
353       enable = g_default_client_tcp_user_timeout_enabled;
354       timeout = g_default_client_tcp_user_timeout_ms;
355     } else {
356       enable = g_default_server_tcp_user_timeout_enabled;
357       timeout = g_default_server_tcp_user_timeout_ms;
358     }
359     int value = options.keep_alive_time_ms;
360     if (value > 0) {
361       enable = value != INT_MAX;
362     }
363     value = options.keep_alive_timeout_ms;
364     if (value > 0) {
365       timeout = value;
366     }
367     if (enable) {
368       int newval;
369       socklen_t len = sizeof(newval);
370       // If this is the first time to use TCP_USER_TIMEOUT, try to check
371       // if it is available.
372       if (g_socket_supports_tcp_user_timeout.load() == 0) {
373         if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
374           GRPC_TRACE_LOG(tcp, INFO)
375               << "TCP_USER_TIMEOUT is not available. TCP_USER_TIMEOUT won't be "
376                  "used thereafter";
377           g_socket_supports_tcp_user_timeout.store(-1);
378         } else {
379           GRPC_TRACE_LOG(tcp, INFO)
380               << "TCP_USER_TIMEOUT is available. TCP_USER_TIMEOUT will be used "
381                  "thereafter";
382           g_socket_supports_tcp_user_timeout.store(1);
383         }
384       }
385       if (g_socket_supports_tcp_user_timeout.load() > 0) {
386         GRPC_TRACE_LOG(tcp, INFO)
387             << "Enabling TCP_USER_TIMEOUT with a timeout of " << timeout
388             << " ms";
389         if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout,
390                             sizeof(timeout))) {
391           LOG(ERROR) << "setsockopt(TCP_USER_TIMEOUT) "
392                      << grpc_core::StrError(errno);
393           return absl::OkStatus();
394         }
395         if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
396           LOG(ERROR) << "getsockopt(TCP_USER_TIMEOUT) "
397                      << grpc_core::StrError(errno);
398           return absl::OkStatus();
399         }
400         if (newval != timeout) {
401           GRPC_TRACE_LOG(tcp, INFO)
402               << "Setting TCP_USER_TIMEOUT to value " << timeout
403               << " ms. Actual TCP_USER_TIMEOUT value is " << newval << " ms";
404           return absl::OkStatus();
405         }
406       }
407     }
408   } else {
409     GRPC_TRACE_LOG(tcp, INFO)
410         << "TCP_USER_TIMEOUT not supported for this platform";
411   }
412   return absl::OkStatus();
413 }
414 
415 // set a socket using a grpc_socket_mutator
grpc_set_socket_with_mutator(int fd,grpc_fd_usage usage,grpc_socket_mutator * mutator)416 grpc_error_handle grpc_set_socket_with_mutator(int fd, grpc_fd_usage usage,
417                                                grpc_socket_mutator* mutator) {
418   CHECK(mutator);
419   if (!grpc_socket_mutator_mutate_fd(mutator, fd, usage)) {
420     return GRPC_ERROR_CREATE("grpc_socket_mutator failed.");
421   }
422   return absl::OkStatus();
423 }
424 
grpc_apply_socket_mutator_in_args(int fd,grpc_fd_usage usage,const grpc_core::PosixTcpOptions & options)425 grpc_error_handle grpc_apply_socket_mutator_in_args(
426     int fd, grpc_fd_usage usage, const grpc_core::PosixTcpOptions& options) {
427   if (options.socket_mutator == nullptr) {
428     return absl::OkStatus();
429   }
430   return grpc_set_socket_with_mutator(fd, usage, options.socket_mutator);
431 }
432 
433 static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
434 static int g_ipv6_loopback_available;
435 
probe_ipv6_once(void)436 static void probe_ipv6_once(void) {
437   int fd = socket(AF_INET6, SOCK_STREAM, 0);
438   g_ipv6_loopback_available = 0;
439   if (fd < 0) {
440     GRPC_TRACE_LOG(tcp, INFO)
441         << "Disabling AF_INET6 sockets because socket() failed.";
442   } else {
443     grpc_sockaddr_in6 addr;
444     memset(&addr, 0, sizeof(addr));
445     addr.sin6_family = AF_INET6;
446     addr.sin6_addr.s6_addr[15] = 1;  // [::1]:0
447     if (bind(fd, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) {
448       g_ipv6_loopback_available = 1;
449     } else {
450       GRPC_TRACE_LOG(tcp, INFO)
451           << "Disabling AF_INET6 sockets because ::1 is not available.";
452     }
453     close(fd);
454   }
455 }
456 
grpc_ipv6_loopback_available(void)457 int grpc_ipv6_loopback_available(void) {
458   gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
459   return g_ipv6_loopback_available;
460 }
461 
error_for_fd(int fd)462 static grpc_error_handle error_for_fd(int fd) {
463   if (fd >= 0) return absl::OkStatus();
464   return GRPC_OS_ERROR(errno, "socket");
465 }
466 
grpc_create_dualstack_socket(const grpc_resolved_address * resolved_addr,int type,int protocol,grpc_dualstack_mode * dsmode,int * newfd)467 grpc_error_handle grpc_create_dualstack_socket(
468     const grpc_resolved_address* resolved_addr, int type, int protocol,
469     grpc_dualstack_mode* dsmode, int* newfd) {
470   return grpc_create_dualstack_socket_using_factory(
471       nullptr, resolved_addr, type, protocol, dsmode, newfd);
472 }
473 
create_socket(grpc_socket_factory * factory,int domain,int type,int protocol)474 static int create_socket(grpc_socket_factory* factory, int domain, int type,
475                          int protocol) {
476   int res = (factory != nullptr)
477                 ? grpc_socket_factory_socket(factory, domain, type, protocol)
478                 : socket(domain, type, protocol);
479   if (res < 0 && errno == EMFILE) {
480     int saved_errno = errno;
481     LOG_EVERY_N_SEC(ERROR, 10)
482         << "socket(" << domain << ", " << type << ", " << protocol
483         << ") returned " << res << " with error: |"
484         << grpc_core::StrError(errno)
485         << "|. This process might not have a sufficient file descriptor limit "
486            "for the number of connections grpc wants to open (which is "
487            "generally a function of the number of grpc channels, the lb policy "
488            "of each channel, and the number of backends each channel is load "
489            "balancing across).";
490     errno = saved_errno;
491   }
492   return res;
493 }
494 
grpc_create_dualstack_socket_using_factory(grpc_socket_factory * factory,const grpc_resolved_address * resolved_addr,int type,int protocol,grpc_dualstack_mode * dsmode,int * newfd)495 grpc_error_handle grpc_create_dualstack_socket_using_factory(
496     grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr,
497     int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) {
498   const grpc_sockaddr* addr =
499       reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
500   int family = addr->sa_family;
501   if (family == AF_INET6) {
502     if (grpc_ipv6_loopback_available()) {
503       *newfd = create_socket(factory, family, type, protocol);
504     } else {
505       *newfd = -1;
506       errno = EAFNOSUPPORT;
507     }
508     // Check if we've got a valid dualstack socket.
509     if (*newfd >= 0 && grpc_set_socket_dualstack(*newfd)) {
510       *dsmode = GRPC_DSMODE_DUALSTACK;
511       return absl::OkStatus();
512     }
513     // If this isn't an IPv4 address, then return whatever we've got.
514     if (!grpc_sockaddr_is_v4mapped(resolved_addr, nullptr)) {
515       *dsmode = GRPC_DSMODE_IPV6;
516       return error_for_fd(*newfd);
517     }
518     // Fall back to AF_INET.
519     if (*newfd >= 0) {
520       close(*newfd);
521     }
522     family = AF_INET;
523   }
524   *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
525   *newfd = create_socket(factory, family, type, protocol);
526   return error_for_fd(*newfd);
527 }
528 
529 #endif
530