| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/tools/quic/quic_socket_utils.h" | |
| 6 | |
| 7 #include <errno.h> | |
| 8 #include <linux/net_tstamp.h> | |
| 9 #include <netinet/in.h> | |
| 10 #include <string.h> | |
| 11 #include <sys/socket.h> | |
| 12 #include <sys/uio.h> | |
| 13 #include <string> | |
| 14 | |
| 15 #include "base/logging.h" | |
| 16 #include "net/quic/core/quic_bug_tracker.h" | |
| 17 #include "net/quic/core/quic_flags.h" | |
| 18 #include "net/quic/core/quic_packets.h" | |
| 19 #include "net/quic/platform/api/quic_socket_address.h" | |
| 20 | |
| 21 #ifndef SO_RXQ_OVFL | |
| 22 #define SO_RXQ_OVFL 40 | |
| 23 #endif | |
| 24 | |
| 25 using std::string; | |
| 26 | |
| 27 namespace net { | |
| 28 | |
| 29 // static | |
| 30 void QuicSocketUtils::GetAddressAndTimestampFromMsghdr( | |
| 31 struct msghdr* hdr, | |
| 32 QuicIpAddress* address, | |
| 33 QuicWallTime* walltimestamp) { | |
| 34 if (hdr->msg_controllen > 0) { | |
| 35 for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; | |
| 36 cmsg = CMSG_NXTHDR(hdr, cmsg)) { | |
| 37 char* addr_data = nullptr; | |
| 38 int len = 0; | |
| 39 if (cmsg->cmsg_type == IPV6_PKTINFO) { | |
| 40 in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); | |
| 41 addr_data = reinterpret_cast<char*>(&info->ipi6_addr); | |
| 42 len = sizeof(in6_addr); | |
| 43 address->FromPackedString(addr_data, len); | |
| 44 } else if (cmsg->cmsg_type == IP_PKTINFO) { | |
| 45 in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); | |
| 46 addr_data = reinterpret_cast<char*>(&info->ipi_addr); | |
| 47 len = sizeof(in_addr); | |
| 48 address->FromPackedString(addr_data, len); | |
| 49 } else if (cmsg->cmsg_level == SOL_SOCKET && | |
| 50 cmsg->cmsg_type == SO_TIMESTAMPING) { | |
| 51 LinuxTimestamping* lts = | |
| 52 reinterpret_cast<LinuxTimestamping*>(CMSG_DATA(cmsg)); | |
| 53 timespec* ts = <s->systime; | |
| 54 int64_t usec = (static_cast<int64_t>(ts->tv_sec) * 1000 * 1000) + | |
| 55 (static_cast<int64_t>(ts->tv_nsec) / 1000); | |
| 56 *walltimestamp = QuicWallTime::FromUNIXMicroseconds(usec); | |
| 57 } | |
| 58 } | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 // static | |
| 63 bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr* hdr, | |
| 64 QuicPacketCount* dropped_packets) { | |
| 65 if (hdr->msg_controllen > 0) { | |
| 66 struct cmsghdr* cmsg; | |
| 67 for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; | |
| 68 cmsg = CMSG_NXTHDR(hdr, cmsg)) { | |
| 69 if (cmsg->cmsg_type == SO_RXQ_OVFL) { | |
| 70 *dropped_packets = *(reinterpret_cast<uint32_t*> CMSG_DATA(cmsg)); | |
| 71 return true; | |
| 72 } | |
| 73 } | |
| 74 } | |
| 75 return false; | |
| 76 } | |
| 77 | |
| 78 // static | |
| 79 bool QuicSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) { | |
| 80 if (hdr->msg_controllen > 0) { | |
| 81 struct cmsghdr* cmsg; | |
| 82 for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; | |
| 83 cmsg = CMSG_NXTHDR(hdr, cmsg)) { | |
| 84 if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) || | |
| 85 (cmsg->cmsg_level == IPPROTO_IPV6 && | |
| 86 cmsg->cmsg_type == IPV6_HOPLIMIT)) { | |
| 87 *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg))); | |
| 88 return true; | |
| 89 } | |
| 90 } | |
| 91 } | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 // static | |
| 96 int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) { | |
| 97 int get_local_ip = 1; | |
| 98 int rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, | |
| 99 sizeof(get_local_ip)); | |
| 100 if (rc == 0 && address_family == AF_INET6) { | |
| 101 rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, | |
| 102 sizeof(get_local_ip)); | |
| 103 } | |
| 104 return rc; | |
| 105 } | |
| 106 | |
| 107 // static | |
| 108 int QuicSocketUtils::SetGetSoftwareReceiveTimestamp(int fd) { | |
| 109 int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE; | |
| 110 return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, ×tamping, | |
| 111 sizeof(timestamping)); | |
| 112 } | |
| 113 | |
| 114 // static | |
| 115 bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) { | |
| 116 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) { | |
| 117 LOG(ERROR) << "Failed to set socket send size"; | |
| 118 return false; | |
| 119 } | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 // static | |
| 124 bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) { | |
| 125 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) { | |
| 126 LOG(ERROR) << "Failed to set socket recv size"; | |
| 127 return false; | |
| 128 } | |
| 129 return true; | |
| 130 } | |
| 131 | |
| 132 // static | |
| 133 int QuicSocketUtils::ReadPacket(int fd, | |
| 134 char* buffer, | |
| 135 size_t buf_len, | |
| 136 QuicPacketCount* dropped_packets, | |
| 137 QuicIpAddress* self_address, | |
| 138 QuicWallTime* walltimestamp, | |
| 139 QuicSocketAddress* peer_address) { | |
| 140 DCHECK(peer_address != nullptr); | |
| 141 char cbuf[kSpaceForCmsg]; | |
| 142 memset(cbuf, 0, arraysize(cbuf)); | |
| 143 | |
| 144 iovec iov = {buffer, buf_len}; | |
| 145 struct sockaddr_storage raw_address; | |
| 146 msghdr hdr; | |
| 147 | |
| 148 hdr.msg_name = &raw_address; | |
| 149 hdr.msg_namelen = sizeof(sockaddr_storage); | |
| 150 hdr.msg_iov = &iov; | |
| 151 hdr.msg_iovlen = 1; | |
| 152 hdr.msg_flags = 0; | |
| 153 | |
| 154 struct cmsghdr* cmsg = reinterpret_cast<struct cmsghdr*>(cbuf); | |
| 155 cmsg->cmsg_len = arraysize(cbuf); | |
| 156 hdr.msg_control = cmsg; | |
| 157 hdr.msg_controllen = arraysize(cbuf); | |
| 158 | |
| 159 int bytes_read = recvmsg(fd, &hdr, 0); | |
| 160 | |
| 161 // Return before setting dropped packets: if we get EAGAIN, it will | |
| 162 // be 0. | |
| 163 if (bytes_read < 0 && errno != 0) { | |
| 164 if (errno != EAGAIN) { | |
| 165 LOG(ERROR) << "Error reading " << strerror(errno); | |
| 166 } | |
| 167 return -1; | |
| 168 } | |
| 169 | |
| 170 if (hdr.msg_controllen >= arraysize(cbuf)) { | |
| 171 QUIC_BUG << "Incorrectly set control length: " << hdr.msg_controllen | |
| 172 << ", expected " << arraysize(cbuf); | |
| 173 return -1; | |
| 174 } | |
| 175 | |
| 176 if (dropped_packets != nullptr) { | |
| 177 GetOverflowFromMsghdr(&hdr, dropped_packets); | |
| 178 } | |
| 179 | |
| 180 QuicIpAddress stack_address; | |
| 181 if (self_address == nullptr) { | |
| 182 self_address = &stack_address; | |
| 183 } | |
| 184 | |
| 185 QuicWallTime stack_walltimestamp = QuicWallTime::FromUNIXMicroseconds(0); | |
| 186 if (walltimestamp == nullptr) { | |
| 187 walltimestamp = &stack_walltimestamp; | |
| 188 } | |
| 189 | |
| 190 GetAddressAndTimestampFromMsghdr(&hdr, self_address, walltimestamp); | |
| 191 | |
| 192 *peer_address = QuicSocketAddress(raw_address); | |
| 193 return bytes_read; | |
| 194 } | |
| 195 | |
| 196 size_t QuicSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address, | |
| 197 cmsghdr* cmsg) { | |
| 198 string address_string; | |
| 199 if (self_address.IsIPv4()) { | |
| 200 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); | |
| 201 cmsg->cmsg_level = IPPROTO_IP; | |
| 202 cmsg->cmsg_type = IP_PKTINFO; | |
| 203 in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); | |
| 204 memset(pktinfo, 0, sizeof(in_pktinfo)); | |
| 205 pktinfo->ipi_ifindex = 0; | |
| 206 address_string = self_address.ToPackedString(); | |
| 207 memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(), | |
| 208 address_string.length()); | |
| 209 return sizeof(in_pktinfo); | |
| 210 } else if (self_address.IsIPv6()) { | |
| 211 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); | |
| 212 cmsg->cmsg_level = IPPROTO_IPV6; | |
| 213 cmsg->cmsg_type = IPV6_PKTINFO; | |
| 214 in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); | |
| 215 memset(pktinfo, 0, sizeof(in6_pktinfo)); | |
| 216 address_string = self_address.ToPackedString(); | |
| 217 memcpy(&pktinfo->ipi6_addr, address_string.c_str(), | |
| 218 address_string.length()); | |
| 219 return sizeof(in6_pktinfo); | |
| 220 } else { | |
| 221 NOTREACHED() << "Unrecognized IPAddress"; | |
| 222 return 0; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // static | |
| 227 WriteResult QuicSocketUtils::WritePacket( | |
| 228 int fd, | |
| 229 const char* buffer, | |
| 230 size_t buf_len, | |
| 231 const QuicIpAddress& self_address, | |
| 232 const QuicSocketAddress& peer_address) { | |
| 233 sockaddr_storage raw_address = peer_address.generic_address(); | |
| 234 iovec iov = {const_cast<char*>(buffer), buf_len}; | |
| 235 | |
| 236 msghdr hdr; | |
| 237 hdr.msg_name = &raw_address; | |
| 238 hdr.msg_namelen = raw_address.ss_family == AF_INET ? sizeof(sockaddr_in) | |
| 239 : sizeof(sockaddr_in6); | |
| 240 hdr.msg_iov = &iov; | |
| 241 hdr.msg_iovlen = 1; | |
| 242 hdr.msg_flags = 0; | |
| 243 | |
| 244 const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo)); | |
| 245 const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo)); | |
| 246 // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info. | |
| 247 const int kSpaceForIp = | |
| 248 (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4; | |
| 249 char cbuf[kSpaceForIp]; | |
| 250 if (!self_address.IsInitialized()) { | |
| 251 hdr.msg_control = 0; | |
| 252 hdr.msg_controllen = 0; | |
| 253 } else { | |
| 254 hdr.msg_control = cbuf; | |
| 255 hdr.msg_controllen = kSpaceForIp; | |
| 256 cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); | |
| 257 SetIpInfoInCmsg(self_address, cmsg); | |
| 258 hdr.msg_controllen = cmsg->cmsg_len; | |
| 259 } | |
| 260 | |
| 261 int rc; | |
| 262 do { | |
| 263 rc = sendmsg(fd, &hdr, 0); | |
| 264 } while (rc < 0 && errno == EINTR); | |
| 265 if (rc >= 0) { | |
| 266 return WriteResult(WRITE_STATUS_OK, rc); | |
| 267 } | |
| 268 return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) | |
| 269 ? WRITE_STATUS_BLOCKED | |
| 270 : WRITE_STATUS_ERROR, | |
| 271 errno); | |
| 272 } | |
| 273 | |
| 274 // static | |
| 275 int QuicSocketUtils::CreateUDPSocket(const QuicSocketAddress& address, | |
| 276 bool* overflow_supported) { | |
| 277 int address_family = address.host().AddressFamilyToInt(); | |
| 278 int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); | |
| 279 if (fd < 0) { | |
| 280 LOG(ERROR) << "socket() failed: " << strerror(errno); | |
| 281 return -1; | |
| 282 } | |
| 283 | |
| 284 int get_overflow = 1; | |
| 285 int rc = setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, | |
| 286 sizeof(get_overflow)); | |
| 287 if (rc < 0) { | |
| 288 DLOG(WARNING) << "Socket overflow detection not supported"; | |
| 289 } else { | |
| 290 *overflow_supported = true; | |
| 291 } | |
| 292 | |
| 293 if (!SetReceiveBufferSize(fd, kDefaultSocketReceiveBuffer)) { | |
| 294 return -1; | |
| 295 } | |
| 296 | |
| 297 if (!SetSendBufferSize(fd, kDefaultSocketReceiveBuffer)) { | |
| 298 return -1; | |
| 299 } | |
| 300 | |
| 301 rc = SetGetAddressInfo(fd, address_family); | |
| 302 if (rc < 0) { | |
| 303 LOG(ERROR) << "IP detection not supported" << strerror(errno); | |
| 304 return -1; | |
| 305 } | |
| 306 | |
| 307 rc = SetGetSoftwareReceiveTimestamp(fd); | |
| 308 if (rc < 0) { | |
| 309 LOG(WARNING) << "SO_TIMESTAMPING not supported; using fallback: " | |
| 310 << strerror(errno); | |
| 311 } | |
| 312 | |
| 313 return fd; | |
| 314 } | |
| 315 | |
| 316 } // namespace net | |
| OLD | NEW |