1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/detail/SocketFastOpen.h>
18
19#include <folly/portability/Sockets.h>
20
21#include <cerrno>
22#include <cstdio>
23
24namespace folly {
25namespace detail {
26
27#if FOLLY_ALLOW_TFO && defined(__linux__)
28
29// Sometimes these flags are not present in the headers,
30// so define them if not present.
31#if !defined(MSG_FASTOPEN)
32#define MSG_FASTOPEN 0x20000000
33#endif
34
35#if !defined(TCP_FASTOPEN)
36#define TCP_FASTOPEN 23
37#endif
38
39#if !defined(TCPI_OPT_SYN_DATA)
40#define TCPI_OPT_SYN_DATA 32
41#endif
42
43ssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) {
44 flags |= MSG_FASTOPEN;
45 return netops::sendmsg(sockfd, msg, flags);
46}
47
48int tfo_enable(NetworkSocket sockfd, size_t max_queue_size) {
49 return netops::setsockopt(
50 sockfd, SOL_TCP, TCP_FASTOPEN, &max_queue_size, sizeof(max_queue_size));
51}
52
53bool tfo_succeeded(NetworkSocket sockfd) {
54 // Call getsockopt to check if TFO was used.
55 struct tcp_info info;
56 socklen_t info_len = sizeof(info);
57 errno = 0;
58 if (netops::getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) !=
59 0) {
60 // errno is set from getsockopt
61 return false;
62 }
63 return info.tcpi_options & TCPI_OPT_SYN_DATA;
64}
65
66#elif FOLLY_ALLOW_TFO && defined(__APPLE__)
67
68ssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) {
69 sa_endpoints_t endpoints;
70 endpoints.sae_srcif = 0;
71 endpoints.sae_srcaddr = nullptr;
72 endpoints.sae_srcaddrlen = 0;
73 endpoints.sae_dstaddr = (struct sockaddr*)msg->msg_name;
74 endpoints.sae_dstaddrlen = msg->msg_namelen;
75 int ret = connectx(
76 sockfd.toFd(),
77 &endpoints,
78 SAE_ASSOCID_ANY,
79 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
80 nullptr,
81 0,
82 nullptr,
83 nullptr);
84
85 if (ret != 0) {
86 return ret;
87 }
88 ret = netops::sendmsg(sockfd, msg, flags);
89 return ret;
90}
91
92int tfo_enable(NetworkSocket sockfd, size_t max_queue_size) {
93 return netops::setsockopt(
94 sockfd,
95 IPPROTO_TCP,
96 TCP_FASTOPEN,
97 &max_queue_size,
98 sizeof(max_queue_size));
99}
100
101bool tfo_succeeded(NetworkSocket /* sockfd */) {
102 errno = EOPNOTSUPP;
103 return false;
104}
105
106#else
107
108ssize_t tfo_sendmsg(
109 NetworkSocket /* sockfd */,
110 const struct msghdr* /* msg */,
111 int /* flags */) {
112 errno = EOPNOTSUPP;
113 return -1;
114}
115
116int tfo_enable(NetworkSocket /* sockfd */, size_t /* max_queue_size */) {
117 errno = ENOPROTOOPT;
118 return -1;
119}
120
121bool tfo_succeeded(NetworkSocket /* sockfd */) {
122 errno = EOPNOTSUPP;
123 return false;
124}
125
126#endif
127} // namespace detail
128} // namespace folly
129