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
22namespace net {
23namespace tools {
24
25// static
26IPAddressNumber 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
54bool 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
71int 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
83bool 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
92bool 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
101int 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
157size_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
180WriteResult 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