• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
GetAddressFromMsghdr(struct msghdr * hdr)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
GetOverflowFromMsghdr(struct msghdr * hdr,uint32 * dropped_packets)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
SetGetAddressInfo(int fd,int address_family)71 int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
72   int get_local_ip = 1;
73   if (address_family == AF_INET) {
74     return setsockopt(fd, IPPROTO_IP, IP_PKTINFO,
75                       &get_local_ip, sizeof(get_local_ip));
76   } else {
77     return setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
78                       &get_local_ip, sizeof(get_local_ip));
79   }
80 }
81 
82 // static
SetSendBufferSize(int fd,size_t size)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
SetReceiveBufferSize(int fd,size_t size)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
ReadPacket(int fd,char * buffer,size_t buf_len,uint32 * dropped_packets,IPAddressNumber * self_address,IPEndPoint * peer_address)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 
SetIpInfoInCmsg(const IPAddressNumber & self_address,cmsghdr * cmsg)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
WritePacket(int fd,const char * buffer,size_t buf_len,const IPAddressNumber & self_address,const IPEndPoint & peer_address)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