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 <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 != nullptr; | |
30 cmsg = CMSG_NXTHDR(hdr, cmsg)) { | |
31 const uint8* addr_data = nullptr; | |
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 QuicPacketCount* dropped_packets) { | |
56 if (hdr->msg_controllen > 0) { | |
57 struct cmsghdr* cmsg; | |
58 for (cmsg = CMSG_FIRSTHDR(hdr); | |
59 cmsg != nullptr; | |
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 QuicPacketCount* dropped_packets, | |
103 IPAddressNumber* self_address, | |
104 IPEndPoint* peer_address) { | |
105 DCHECK(peer_address != nullptr); | |
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 != nullptr) { | |
138 GetOverflowFromMsghdr(&hdr, dropped_packets); | |
139 } | |
140 if (self_address != nullptr) { | |
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 | |
OLD | NEW |