diff options
Diffstat (limited to 'chromium/net/tools/quic/quic_socket_utils.cc')
-rw-r--r-- | chromium/net/tools/quic/quic_socket_utils.cc | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/chromium/net/tools/quic/quic_socket_utils.cc b/chromium/net/tools/quic/quic_socket_utils.cc new file mode 100644 index 00000000000..4d7d3600760 --- /dev/null +++ b/chromium/net/tools/quic/quic_socket_utils.cc @@ -0,0 +1,191 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/quic/quic_socket_utils.h" + +#include <errno.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <string> + +#include "base/logging.h" + +#ifndef SO_RXQ_OVFL +#define SO_RXQ_OVFL 40 +#endif + +namespace net { +namespace tools { + +// static +IPAddressNumber QuicSocketUtils::GetAddressFromMsghdr(struct msghdr *hdr) { + IPAddressNumber ret; + if (hdr->msg_controllen > 0) { + for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); + cmsg != NULL; + cmsg = CMSG_NXTHDR(hdr, cmsg)) { + const uint8* addr_data = reinterpret_cast<const uint8*>CMSG_DATA(cmsg); + int len = 0; + if (cmsg->cmsg_type == IPV6_PKTINFO) { + len = sizeof(in6_pktinfo); + } else if (cmsg->cmsg_type == IP_PKTINFO) { + len = sizeof(in_pktinfo); + } + ret.assign(addr_data, addr_data + len); + break; + } + } + return ret; +} + +// static +bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr *hdr, + int *dropped_packets) { + if (hdr->msg_controllen > 0) { + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(hdr); + cmsg != NULL; + cmsg = CMSG_NXTHDR(hdr, cmsg)) { + if (cmsg->cmsg_type == SO_RXQ_OVFL) { + *dropped_packets = *(reinterpret_cast<int*>CMSG_DATA(cmsg)); + return true; + } + } + } + return false; +} + +// static +int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) { + int get_local_ip = 1; + if (address_family == AF_INET) { + return setsockopt(fd, IPPROTO_IP, IP_PKTINFO, + &get_local_ip, sizeof(get_local_ip)); + } else { + return setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &get_local_ip, sizeof(get_local_ip)); + } +} + +// static +int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len, + int* dropped_packets, + IPAddressNumber* self_address, + IPEndPoint* peer_address) { + CHECK(peer_address != NULL); + const int kSpaceForOverflowAndIp = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(in6_pktinfo)); + char cbuf[kSpaceForOverflowAndIp]; + memset(cbuf, 0, arraysize(cbuf)); + + iovec iov = {buffer, buf_len}; + struct sockaddr_storage raw_address; + msghdr hdr; + + hdr.msg_name = &raw_address; + hdr.msg_namelen = sizeof(sockaddr_storage); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_flags = 0; + + struct cmsghdr *cmsg = (struct cmsghdr *) cbuf; + cmsg->cmsg_len = arraysize(cbuf); + hdr.msg_control = cmsg; + hdr.msg_controllen = arraysize(cbuf); + + int bytes_read = recvmsg(fd, &hdr, 0); + + // Return before setting dropped packets: if we get EAGAIN, it will + // be 0. + if (bytes_read < 0 && errno != 0) { + if (errno != EAGAIN) { + LOG(ERROR) << "Error reading " << strerror(errno); + } + return -1; + } + + if (dropped_packets != NULL) { + GetOverflowFromMsghdr(&hdr, dropped_packets); + } + if (self_address != NULL) { + *self_address = QuicSocketUtils::GetAddressFromMsghdr(&hdr); + } + + if (raw_address.ss_family == AF_INET) { + CHECK(peer_address->FromSockAddr( + reinterpret_cast<const sockaddr*>(&raw_address), + sizeof(struct sockaddr_in))); + } else if (raw_address.ss_family == AF_INET6) { + CHECK(peer_address->FromSockAddr( + reinterpret_cast<const sockaddr*>(&raw_address), + sizeof(struct sockaddr_in6))); + } + + return bytes_read; +} + +// static +int QuicSocketUtils::WritePacket(int fd, const char* buffer, size_t buf_len, + const IPAddressNumber& self_address, + const IPEndPoint& peer_address, + int* error) { + sockaddr_storage raw_address; + socklen_t address_len = sizeof(raw_address); + CHECK(peer_address.ToSockAddr( + reinterpret_cast<struct sockaddr*>(&raw_address), + &address_len)); + iovec iov = {const_cast<char*>(buffer), buf_len}; + + msghdr hdr; + hdr.msg_name = &raw_address; + hdr.msg_namelen = address_len; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_flags = 0; + + const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo)); + const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo)); + // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info. + const int kSpaceForIp = + (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4; + char cbuf[kSpaceForIp]; + if (self_address.empty()) { + hdr.msg_control = 0; + hdr.msg_controllen = 0; + } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) { + hdr.msg_control = cbuf; + hdr.msg_controllen = kSpaceForIp; + cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); + + cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in_pktinfo)); + pktinfo->ipi_ifindex = 0; + memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size()); + hdr.msg_controllen = cmsg->cmsg_len; + } else { + hdr.msg_control = cbuf; + hdr.msg_controllen = kSpaceForIp; + cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); + + cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in6_pktinfo)); + memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size()); + hdr.msg_controllen = cmsg->cmsg_len; + } + + int rc = sendmsg(fd, &hdr, 0); + *error = (rc >= 0) ? 0 : errno; + return rc; +} + +} // namespace tools +} // namespace net |