Home | History | Annotate | Download | only in quic
      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 <netinet/in.h>
      9 #include <string.h>
     10 #include <sys/socket.h>
     11 #include <sys/uio.h>
     12 #include <string>
     13 
     14 #include "base/basictypes.h"
     15 #include "base/logging.h"
     16 #include "net/quic/quic_protocol.h"
     17 
     18 #ifndef SO_RXQ_OVFL
     19 #define SO_RXQ_OVFL 40
     20 #endif
     21 
     22 namespace net {
     23 namespace tools {
     24 
     25 // static
     26 IPAddressNumber QuicSocketUtils::GetAddressFromMsghdr(struct msghdr *hdr) {
     27   if (hdr->msg_controllen > 0) {
     28     for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr);
     29          cmsg != NULL;
     30          cmsg = CMSG_NXTHDR(hdr, cmsg)) {
     31       const uint8* addr_data = NULL;
     32       int len = 0;
     33       if (cmsg->cmsg_type == IPV6_PKTINFO) {
     34         in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>CMSG_DATA(cmsg);
     35         in6_addr addr = info->ipi6_addr;
     36         addr_data = reinterpret_cast<const uint8*>(&addr);
     37         len = sizeof(addr);
     38       } else if (cmsg->cmsg_type == IP_PKTINFO) {
     39         in_pktinfo* info = reinterpret_cast<in_pktinfo*>CMSG_DATA(cmsg);
     40         in_addr addr = info->ipi_addr;
     41         addr_data = reinterpret_cast<const uint8*>(&addr);
     42         len = sizeof(addr);
     43       } else {
     44         continue;
     45       }
     46       return IPAddressNumber(addr_data, addr_data + len);
     47     }
     48   }
     49   DCHECK(false) << "Unable to get address from msghdr";
     50   return IPAddressNumber();
     51 }
     52 
     53 // static
     54 bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr *hdr,
     55                                             uint32 *dropped_packets) {
     56   if (hdr->msg_controllen > 0) {
     57     struct cmsghdr *cmsg;
     58     for (cmsg = CMSG_FIRSTHDR(hdr);
     59          cmsg != NULL;
     60          cmsg = CMSG_NXTHDR(hdr, cmsg)) {
     61       if (cmsg->cmsg_type == SO_RXQ_OVFL) {
     62         *dropped_packets = *(reinterpret_cast<int*>CMSG_DATA(cmsg));
     63         return true;
     64       }
     65     }
     66   }
     67   return false;
     68 }
     69 
     70 // static
     71 int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
     72   int get_local_ip = 1;
     73   int rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO,
     74                       &get_local_ip, sizeof(get_local_ip));
     75   if (rc == 0 && address_family == AF_INET6) {
     76     rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
     77                     &get_local_ip, sizeof(get_local_ip));
     78   }
     79   return rc;
     80 }
     81 
     82 // static
     83 bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) {
     84   if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) {
     85     LOG(ERROR) << "Failed to set socket send size";
     86     return false;
     87   }
     88   return true;
     89 }
     90 
     91 // static
     92 bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) {
     93   if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) {
     94     LOG(ERROR) << "Failed to set socket recv size";
     95     return false;
     96   }
     97   return true;
     98 }
     99 
    100 // static
    101 int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len,
    102                                 uint32* dropped_packets,
    103                                 IPAddressNumber* self_address,
    104                                 IPEndPoint* peer_address) {
    105   CHECK(peer_address != NULL);
    106   const int kSpaceForOverflowAndIp =
    107       CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(in6_pktinfo));
    108   char cbuf[kSpaceForOverflowAndIp];
    109   memset(cbuf, 0, arraysize(cbuf));
    110 
    111   iovec iov = {buffer, buf_len};
    112   struct sockaddr_storage raw_address;
    113   msghdr hdr;
    114 
    115   hdr.msg_name = &raw_address;
    116   hdr.msg_namelen = sizeof(sockaddr_storage);
    117   hdr.msg_iov = &iov;
    118   hdr.msg_iovlen = 1;
    119   hdr.msg_flags = 0;
    120 
    121   struct cmsghdr *cmsg = (struct cmsghdr *) cbuf;
    122   cmsg->cmsg_len = arraysize(cbuf);
    123   hdr.msg_control = cmsg;
    124   hdr.msg_controllen = arraysize(cbuf);
    125 
    126   int bytes_read = recvmsg(fd, &hdr, 0);
    127 
    128   // Return before setting dropped packets: if we get EAGAIN, it will
    129   // be 0.
    130   if (bytes_read < 0 && errno != 0) {
    131     if (errno != EAGAIN) {
    132       LOG(ERROR) << "Error reading " << strerror(errno);
    133     }
    134     return -1;
    135   }
    136 
    137   if (dropped_packets != NULL) {
    138     GetOverflowFromMsghdr(&hdr, dropped_packets);
    139   }
    140   if (self_address != NULL) {
    141     *self_address = QuicSocketUtils::GetAddressFromMsghdr(&hdr);
    142   }
    143 
    144   if (raw_address.ss_family == AF_INET) {
    145     CHECK(peer_address->FromSockAddr(
    146         reinterpret_cast<const sockaddr*>(&raw_address),
    147         sizeof(struct sockaddr_in)));
    148   } else if (raw_address.ss_family == AF_INET6) {
    149     CHECK(peer_address->FromSockAddr(
    150         reinterpret_cast<const sockaddr*>(&raw_address),
    151         sizeof(struct sockaddr_in6)));
    152   }
    153 
    154   return bytes_read;
    155 }
    156 
    157 size_t QuicSocketUtils::SetIpInfoInCmsg(const IPAddressNumber& self_address,
    158                                         cmsghdr* cmsg) {
    159   if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) {
    160     cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
    161     cmsg->cmsg_level = IPPROTO_IP;
    162     cmsg->cmsg_type = IP_PKTINFO;
    163     in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
    164     memset(pktinfo, 0, sizeof(in_pktinfo));
    165     pktinfo->ipi_ifindex = 0;
    166     memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size());
    167     return sizeof(in_pktinfo);
    168   } else {
    169     cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
    170     cmsg->cmsg_level = IPPROTO_IPV6;
    171     cmsg->cmsg_type = IPV6_PKTINFO;
    172     in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
    173     memset(pktinfo, 0, sizeof(in6_pktinfo));
    174     memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size());
    175     return sizeof(in6_pktinfo);
    176   }
    177 }
    178 
    179 // static
    180 WriteResult QuicSocketUtils::WritePacket(int fd,
    181                                          const char* buffer,
    182                                          size_t buf_len,
    183                                          const IPAddressNumber& self_address,
    184                                          const IPEndPoint& peer_address) {
    185   sockaddr_storage raw_address;
    186   socklen_t address_len = sizeof(raw_address);
    187   CHECK(peer_address.ToSockAddr(
    188       reinterpret_cast<struct sockaddr*>(&raw_address),
    189       &address_len));
    190   iovec iov = {const_cast<char*>(buffer), buf_len};
    191 
    192   msghdr hdr;
    193   hdr.msg_name = &raw_address;
    194   hdr.msg_namelen = address_len;
    195   hdr.msg_iov = &iov;
    196   hdr.msg_iovlen = 1;
    197   hdr.msg_flags = 0;
    198 
    199   const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo));
    200   const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo));
    201   // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info.
    202   const int kSpaceForIp =
    203       (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4;
    204   char cbuf[kSpaceForIp];
    205   if (self_address.empty()) {
    206     hdr.msg_control = 0;
    207     hdr.msg_controllen = 0;
    208   } else {
    209     hdr.msg_control = cbuf;
    210     hdr.msg_controllen = kSpaceForIp;
    211     cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
    212     SetIpInfoInCmsg(self_address, cmsg);
    213     hdr.msg_controllen = cmsg->cmsg_len;
    214   }
    215 
    216   int rc = sendmsg(fd, &hdr, 0);
    217   if (rc >= 0) {
    218     return WriteResult(WRITE_STATUS_OK, rc);
    219   }
    220   return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) ?
    221       WRITE_STATUS_BLOCKED : WRITE_STATUS_ERROR, errno);
    222 }
    223 
    224 }  // namespace tools
    225 }  // namespace net
    226