blob: 634ba20be989683108a15b13264803c399729970 [file] [log] [blame]
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07001// Copyright 2016 The Chromium OS 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
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/multicast_forwarder.h"
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07006
7#include <arpa/inet.h>
Hugo Benichidcce1142019-06-17 10:52:15 +09008#include <net/if.h>
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07009#include <netinet/ip.h>
10#include <string.h>
Hugo Benichidcce1142019-06-17 10:52:15 +090011#include <sys/ioctl.h>
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070012#include <sys/socket.h>
13#include <sys/types.h>
14
Kevin Cernekeeb2c0c832016-12-06 11:47:57 -080015#include <utility>
16
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070017#include <base/bind.h>
18#include <base/logging.h>
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070019
Garrick Evans3388a032020-03-24 11:25:55 +090020#include "patchpanel/dns/dns_protocol.h"
21#include "patchpanel/dns/dns_response.h"
22#include "patchpanel/net_util.h"
23#include "patchpanel/socket.h"
Kevin Cernekee73e09202017-06-17 20:55:09 -070024
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070025namespace {
26
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070027const int kBufSize = 1536;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070028
Hugo Benichidcce1142019-06-17 10:52:15 +090029// Returns the IPv4 address assigned to the interface on which the given socket
30// is bound. Or returns INADDR_ANY if the interface has no IPv4 address.
31struct in_addr GetInterfaceIp(int fd, const std::string& ifname) {
32 if (ifname.empty()) {
33 LOG(WARNING) << "Empty interface name";
34 return {0};
35 }
36
37 struct ifreq ifr;
38 memset(&ifr, 0, sizeof(ifr));
39 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
40 if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
41 // Ignore EADDRNOTAVAIL: IPv4 was not provisioned.
42 if (errno != EADDRNOTAVAIL) {
43 PLOG(ERROR) << "SIOCGIFADDR failed for " << ifname;
44 }
45 return {0};
46 }
47
48 struct sockaddr_in* if_addr =
49 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
50 return if_addr->sin_addr;
51}
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090052
53// Fills sockaddr_storage values.
54void SetSockaddr(struct sockaddr_storage* saddr_storage,
55 sa_family_t sa_family,
56 uint16_t port,
57 char* addr) {
58 struct sockaddr* saddr = reinterpret_cast<sockaddr*>(saddr_storage);
59 if (sa_family == AF_INET) {
60 struct sockaddr_in* saddr4 = reinterpret_cast<struct sockaddr_in*>(saddr);
61 saddr4->sin_family = AF_INET;
62 saddr4->sin_port = htons(port);
63 if (addr)
64 memcpy(&saddr4->sin_addr, addr, sizeof(struct in_addr));
65 return;
66 }
67 if (sa_family == AF_INET6) {
68 struct sockaddr_in6* saddr6 = reinterpret_cast<sockaddr_in6*>(saddr);
69 saddr6->sin6_family = AF_INET6;
70 saddr6->sin6_port = htons(port);
71 if (addr)
72 memcpy(&saddr6->sin6_addr, addr, sizeof(struct in6_addr));
73 return;
74 }
75 LOG(ERROR) << "Invalid socket family " << sa_family;
76}
77
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070078} // namespace
79
Garrick Evans3388a032020-03-24 11:25:55 +090080namespace patchpanel {
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070081
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090082MulticastForwarder::Socket::Socket(
83 base::ScopedFD fd,
84 sa_family_t sa_family,
85 const base::Callback<void(int, sa_family_t)>& callback)
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +090086 : fd(std::move(fd)) {
87 watcher = base::FileDescriptorWatcher::WatchReadable(
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090088 Socket::fd.get(),
89 base::BindRepeating(callback, Socket::fd.get(), sa_family));
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +090090}
91
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +090092MulticastForwarder::MulticastForwarder(const std::string& lan_ifname,
93 uint32_t mcast_addr,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090094 const std::string& mcast_addr6,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +090095 uint16_t port)
96 : lan_ifname_(lan_ifname), port_(port) {
97 mcast_addr_.s_addr = mcast_addr;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090098 CHECK(inet_pton(AF_INET6, mcast_addr6.c_str(), mcast_addr6_.s6_addr));
99
100 base::ScopedFD lan_fd(Bind(AF_INET, lan_ifname_));
101 if (!lan_fd.is_valid()) {
102 LOG(WARNING) << "Could not bind socket on " << lan_ifname_ << " for "
103 << mcast_addr_ << ":" << port_;
104 }
105
106 base::ScopedFD lan_fd6(Bind(AF_INET6, lan_ifname_));
107 if (!lan_fd6.is_valid()) {
108 LOG(WARNING) << "Could not bind socket on " << lan_ifname_ << " for "
109 << mcast_addr6_ << ":" << port_;
110 }
111
112 lan_socket_.emplace(
113 AF_INET, new Socket(std::move(lan_fd), AF_INET,
114 base::BindRepeating(
115 &MulticastForwarder::OnFileCanReadWithoutBlocking,
116 base::Unretained(this))));
117
118 lan_socket_.emplace(
119 AF_INET6,
120 new Socket(
121 std::move(lan_fd6), AF_INET6,
122 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
123 base::Unretained(this))));
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900124}
125
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900126base::ScopedFD MulticastForwarder::Bind(sa_family_t sa_family,
127 const std::string& ifname) {
128 char mcast_addr[INET6_ADDRSTRLEN];
129 inet_ntop(sa_family,
130 sa_family == AF_INET ? reinterpret_cast<const void*>(&mcast_addr_)
131 : reinterpret_cast<const void*>(&mcast_addr6_),
132 mcast_addr, INET6_ADDRSTRLEN);
133
134 base::ScopedFD fd(socket(sa_family, SOCK_DGRAM, 0));
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900135 if (!fd.is_valid()) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900136 PLOG(ERROR) << "socket() failed on " << ifname << " for " << mcast_addr
137 << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900138 return base::ScopedFD();
139 }
140
141 // The socket needs to be bound to INADDR_ANY rather than a specific
142 // interface, or it will not receive multicast traffic. Therefore
143 // we use SO_BINDTODEVICE to force TX from this interface, and
144 // specify the interface address in IP_ADD_MEMBERSHIP to control RX.
145 struct ifreq ifr;
146 memset(&ifr, 0, sizeof(ifr));
147 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
148 if (setsockopt(fd.get(), SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900149 PLOG(ERROR) << "setsockopt(SO_BINDTODEVICE) failed on " << ifname << " for "
150 << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900151 return base::ScopedFD();
152 }
153
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900154 int ifindex = if_nametoindex(ifname.c_str());
155 if (ifindex == 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900156 PLOG(ERROR) << "Could not obtain interface index of " << ifname << " for "
157 << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900158 return base::ScopedFD();
159 }
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900160
161 int level, optname;
162 if (sa_family == AF_INET) {
163 struct ip_mreqn mreqn;
164 memset(&mreqn, 0, sizeof(mreqn));
165 mreqn.imr_multiaddr = mcast_addr_;
166 mreqn.imr_address.s_addr = htonl(INADDR_ANY);
167 mreqn.imr_ifindex = ifindex;
168 if (setsockopt(fd.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn,
169 sizeof(mreqn)) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900170 PLOG(ERROR) << "Can't add IPv4 multicast membership for on " << ifname
171 << " for " << mcast_addr_ << ":" << port_;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900172 return base::ScopedFD();
173 }
174
175 level = IPPROTO_IP;
176 optname = IP_MULTICAST_LOOP;
177 } else if (sa_family == AF_INET6) {
178 struct ipv6_mreq mreqn;
179 memset(&mreqn, 0, sizeof(mreqn));
180 mreqn.ipv6mr_multiaddr = mcast_addr6_;
181 mreqn.ipv6mr_interface = ifindex;
182 if (setsockopt(fd.get(), IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreqn,
183 sizeof(mreqn)) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900184 PLOG(ERROR) << "Can't add IPv6 multicast membership on " << ifname
185 << " for " << mcast_addr6_ << ":" << port_;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900186 return base::ScopedFD();
187 }
188
189 level = IPPROTO_IPV6;
190 optname = IPV6_MULTICAST_LOOP;
191 } else {
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900192 return base::ScopedFD();
193 }
194
195 int off = 0;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900196 if (setsockopt(fd.get(), level, optname, &off, sizeof(off))) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900197 PLOG(ERROR) << "setsockopt(IP_MULTICAST_LOOP) failed on " << ifname
198 << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900199 return base::ScopedFD();
200 }
201
202 int on = 1;
203 if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900204 PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed on " << ifname << " for "
205 << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900206 return base::ScopedFD();
207 }
208
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900209 struct sockaddr_storage bind_addr = {0};
210 SetSockaddr(&bind_addr, sa_family, port_, nullptr);
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900211
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900212 if (bind(fd.get(), (const struct sockaddr*)&bind_addr,
213 sizeof(struct sockaddr_storage)) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900214 PLOG(ERROR) << "bind(" << port_ << ") failed for on " << ifname << " for "
215 << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900216 return base::ScopedFD();
217 }
218
219 return fd;
220}
221
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900222bool MulticastForwarder::AddGuest(const std::string& int_ifname) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900223 if (int_sockets_.find(std::make_pair(AF_INET, int_ifname)) !=
224 int_sockets_.end() ||
225 int_sockets_.find(std::make_pair(AF_INET6, int_ifname)) !=
226 int_sockets_.end()) {
Jason Jeremy Iman3f062ea2019-11-12 08:37:53 +0900227 LOG(WARNING) << "Forwarding is already started between " << lan_ifname_
228 << " and " << int_ifname;
229 return false;
230 }
231
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900232 bool success = false;
233
234 // Set up IPv4 multicast forwarder.
235 base::ScopedFD int_fd4(Bind(AF_INET, int_ifname));
236 if (int_fd4.is_valid()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900237 int_fds_.emplace(std::make_pair(AF_INET, int_fd4.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900238
239 std::unique_ptr<Socket> int_socket4 = std::make_unique<Socket>(
240 std::move(int_fd4), AF_INET,
241 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
242 base::Unretained(this)));
243
244 int_sockets_.emplace(std::make_pair(AF_INET, int_ifname),
245 std::move(int_socket4));
246
247 success = true;
248 LOG(INFO) << "Started IPv4 forwarding between " << lan_ifname_ << " and "
249 << int_ifname << " for " << mcast_addr_ << ":" << port_;
250 } else {
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900251 LOG(WARNING) << "Could not bind socket on " << int_ifname << " for "
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900252 << mcast_addr_ << ":" << port_;
Hugo Benichi935eca92018-07-03 13:47:24 +0900253 }
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900254
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900255 // Set up IPv6 multicast forwarder.
256 base::ScopedFD int_fd6(Bind(AF_INET6, int_ifname));
257 if (int_fd6.is_valid()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900258 int_fds_.emplace(std::make_pair(AF_INET6, int_fd6.get()));
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900259
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900260 std::unique_ptr<Socket> int_socket6 = std::make_unique<Socket>(
261 std::move(int_fd6), AF_INET6,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900262 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900263 base::Unretained(this)));
264
265 int_sockets_.emplace(std::make_pair(AF_INET6, int_ifname),
266 std::move(int_socket6));
267
268 success = true;
269 LOG(INFO) << "Started IPv6 forwarding between " << lan_ifname_ << " and "
270 << int_ifname << " for " << mcast_addr6_ << ":" << port_;
271 } else {
272 LOG(WARNING) << "Could not bind socket on " << int_ifname << " for "
273 << mcast_addr6_ << ":" << port_;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700274 }
275
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900276 return success;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700277}
278
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900279void MulticastForwarder::RemoveGuest(const std::string& int_ifname) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900280 const auto& socket4 = int_sockets_.find(std::make_pair(AF_INET, int_ifname));
281 if (socket4 != int_sockets_.end()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900282 int_fds_.erase(std::make_pair(AF_INET, socket4->second->fd.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900283 int_sockets_.erase(socket4);
284 } else {
285 LOG(WARNING) << "IPv4 forwarding is not started between " << lan_ifname_
Jason Jeremy Iman3f062ea2019-11-12 08:37:53 +0900286 << " and " << int_ifname;
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900287 }
288
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900289 const auto& socket6 = int_sockets_.find(std::make_pair(AF_INET6, int_ifname));
290 if (socket6 != int_sockets_.end()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900291 int_fds_.erase(std::make_pair(AF_INET6, socket6->second->fd.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900292 int_sockets_.erase(socket6);
293 } else {
294 LOG(WARNING) << "IPv6 forwarding is not started between " << lan_ifname_
295 << " and " << int_ifname;
296 }
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900297}
298
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900299void MulticastForwarder::OnFileCanReadWithoutBlocking(int fd,
300 sa_family_t sa_family) {
301 CHECK(sa_family == AF_INET || sa_family == AF_INET6);
Jason Jeremy Iman97ac56d2019-10-09 14:15:35 +0900302
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900303 char data[kBufSize];
304
305 struct sockaddr_storage fromaddr_storage = {0};
306 struct sockaddr* fromaddr =
307 reinterpret_cast<struct sockaddr*>(&fromaddr_storage);
308
309 socklen_t addrlen = sizeof(struct sockaddr_storage);
310
311 ssize_t len = recvfrom(fd, data, kBufSize, 0, fromaddr, &addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900312 if (len < 0) {
Hugo Benichic4499672019-04-26 15:24:23 +0900313 PLOG(WARNING) << "recvfrom failed";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700314 return;
Hugo Benichic4499672019-04-26 15:24:23 +0900315 }
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900316
317 socklen_t expectlen = sa_family == AF_INET ? sizeof(struct sockaddr_in)
318 : sizeof(struct sockaddr_in6);
319 if (addrlen != expectlen) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900320 LOG(WARNING) << "recvfrom failed: src addr length was " << addrlen
321 << " but expected " << expectlen;
Hugo Benichic4499672019-04-26 15:24:23 +0900322 return;
323 }
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700324
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900325 struct sockaddr_storage dst_storage = {0};
326 struct sockaddr* dst = reinterpret_cast<struct sockaddr*>(&dst_storage);
327 uint16_t src_port;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700328
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900329 if (sa_family == AF_INET) {
330 const struct sockaddr_in* addr4 =
331 reinterpret_cast<const struct sockaddr_in*>(fromaddr);
332 src_port = ntohs(addr4->sin_port);
333 } else if (sa_family == AF_INET6) {
334 const struct sockaddr_in6* addr6 =
335 reinterpret_cast<const struct sockaddr_in6*>(fromaddr);
336 src_port = ntohs(addr6->sin6_port);
337 }
338 SetSockaddr(&dst_storage, sa_family, port_,
339 sa_family == AF_INET ? reinterpret_cast<char*>(&mcast_addr_)
340 : reinterpret_cast<char*>(&mcast_addr6_));
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700341
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900342 // Forward ingress traffic to all guests.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900343 const auto& lan_socket = lan_socket_.find(sa_family);
344 if ((lan_socket != lan_socket_.end() && fd == lan_socket->second->fd.get())) {
345 SendToGuests(data, len, dst, addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900346 return;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700347 }
348
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900349 const auto& int_fd = int_fds_.find(std::make_pair(sa_family, fd));
350 if (int_fd == int_fds_.end() || lan_socket == lan_socket_.end())
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700351 return;
352
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900353 // Forward egress traffic from one guest to all other guests.
354 // No IP translation is required as other guests can route to each other
355 // behind the SNAT setup.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900356 SendToGuests(data, len, dst, addrlen, fd);
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900357
358 // On mDNS, sending to physical network requires translating any IPv4
359 // address specific to the guest and not visible to the physical network.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900360 if (sa_family == AF_INET && port_ == kMdnsPort) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900361 // TODO(b/132574450) The replacement address should instead be specified
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900362 // as an input argument, based on the properties of the network
363 // currently connected on |lan_ifname_|.
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900364 const struct in_addr lan_ip =
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900365 GetInterfaceIp(lan_socket->second->fd.get(), lan_ifname_);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900366 if (lan_ip.s_addr == htonl(INADDR_ANY)) {
367 // When the physical interface has no IPv4 address, IPv4 is not
368 // provisioned and there is no point in trying to forward traffic in
369 // either direction.
370 return;
371 }
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900372 TranslateMdnsIp(
373 lan_ip, reinterpret_cast<const struct sockaddr_in*>(fromaddr)->sin_addr,
374 data, len);
Hidehiko Abede129222019-08-16 00:55:04 +0900375 }
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700376
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900377 // Forward egress traffic from one guest to outside network.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900378 SendTo(src_port, data, len, dst, addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900379}
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700380
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900381bool MulticastForwarder::SendTo(uint16_t src_port,
382 const void* data,
383 ssize_t len,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900384 const struct sockaddr* dst,
385 socklen_t dst_len) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900386 if (src_port == port_) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900387 int lan_fd = lan_socket_.find(dst->sa_family)->second->fd.get();
388 if (sendto(lan_fd, data, len, 0, dst, dst_len) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900389 PLOG(WARNING) << "sendto " << *dst << " on " << lan_ifname_
390 << " from port " << src_port << " failed";
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900391 return false;
392 }
393 return true;
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900394 }
395
Garrick Evans3388a032020-03-24 11:25:55 +0900396 patchpanel::Socket temp_socket(dst->sa_family, SOCK_DGRAM);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900397
398 struct ifreq ifr;
399 memset(&ifr, 0, sizeof(ifr));
400 strncpy(ifr.ifr_name, lan_ifname_.c_str(), IFNAMSIZ);
401 if (setsockopt(temp_socket.fd(), SOL_SOCKET, SO_BINDTODEVICE, &ifr,
402 sizeof(ifr))) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900403 PLOG(ERROR) << "setsockopt(SO_BINDTODEVICE) failed";
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900404 return false;
405 }
406
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900407 int level, optname;
408 struct sockaddr_storage bind_addr_storage = {0};
409 struct sockaddr* bind_addr = reinterpret_cast<sockaddr*>(&bind_addr_storage);
410 if (dst->sa_family == AF_INET) {
411 level = IPPROTO_IP;
412 optname = IP_MULTICAST_LOOP;
413 } else if (dst->sa_family == AF_INET6) {
414 level = IPPROTO_IPV6;
415 optname = IPV6_MULTICAST_LOOP;
416 } else {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900417 LOG(ERROR) << "Unexpected sa_family " << dst->sa_family;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900418 return false;
419 }
420 SetSockaddr(&bind_addr_storage, dst->sa_family, src_port, nullptr);
421
Jason Jeremy Iman8fa749c2020-02-05 19:59:58 +0900422 int flag = 0;
423 if (setsockopt(temp_socket.fd(), level, optname, &flag, sizeof(flag))) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900424 PLOG(ERROR) << "setsockopt(IP_MULTICAST_LOOP) failed";
425 return false;
426 }
427
Jason Jeremy Iman8fa749c2020-02-05 19:59:58 +0900428 flag = 1;
429 if (setsockopt(temp_socket.fd(), SOL_SOCKET, SO_REUSEADDR, &flag,
430 sizeof(flag))) {
431 PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed";
432 return false;
433 }
434
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900435 if (!temp_socket.Bind(bind_addr, sizeof(struct sockaddr_storage)))
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900436 return false;
437
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900438 if (!temp_socket.SendTo(data, len, dst, dst_len)) {
439 PLOG(WARNING) << "sendto " << *dst << " on " << lan_ifname_ << " from port "
440 << src_port << " failed";
441 return false;
442 }
443 return true;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700444}
445
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900446bool MulticastForwarder::SendToGuests(const void* data,
447 ssize_t len,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900448 const struct sockaddr* dst,
449 socklen_t dst_len,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900450 int ignore_fd) {
451 bool success = true;
452 for (const auto& socket : int_sockets_) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900453 if (socket.first.first != dst->sa_family)
454 continue;
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900455 int fd = socket.second->fd.get();
456 if (fd == ignore_fd)
457 continue;
458
459 // Use already created multicast fd.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900460 if (sendto(fd, data, len, 0, dst, dst_len) < 0) {
Hugo Benichi3cfadbe2020-08-14 11:42:27 +0900461 PLOG(WARNING) << "sendto " << socket.first.second << " failed";
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900462 success = false;
463 }
464 }
465 return success;
466}
467
Hugo Benichi1661ca02019-10-16 15:36:13 +0900468// static
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900469void MulticastForwarder::TranslateMdnsIp(const struct in_addr& lan_ip,
Hugo Benichi1661ca02019-10-16 15:36:13 +0900470 const struct in_addr& guest_ip,
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900471 char* data,
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900472 ssize_t len) {
Hugo Benichi1661ca02019-10-16 15:36:13 +0900473 if (guest_ip.s_addr == htonl(INADDR_ANY)) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700474 return;
475 }
476
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900477 // Make sure this is a valid, successful DNS response from the Android
478 // host.
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900479 if (len > net::dns_protocol::kMaxUDPSize || len <= 0) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700480 return;
481 }
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900482
Kevin Cernekee73e09202017-06-17 20:55:09 -0700483 net::DnsResponse resp;
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900484 memcpy(resp.io_buffer()->data(), data, len);
485 if (!resp.InitParseWithoutQuery(len) ||
Kevin Cernekee73e09202017-06-17 20:55:09 -0700486 !(resp.flags() & net::dns_protocol::kFlagResponse) ||
487 resp.rcode() != net::dns_protocol::kRcodeNOERROR) {
488 return;
489 }
490
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900491 // Check all A records for the internal IP, and replace it with |lan_ip|
Kevin Cernekee73e09202017-06-17 20:55:09 -0700492 // if it is found.
493 net::DnsRecordParser parser = resp.Parser();
494 while (!parser.AtEnd()) {
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900495 const size_t ipv4_addr_len = sizeof(lan_ip.s_addr);
Kevin Cernekee73e09202017-06-17 20:55:09 -0700496
497 net::DnsResourceRecord record;
Kevin Cernekee41fbbb72017-07-26 14:09:40 -0700498 if (!parser.ReadRecord(&record)) {
499 break;
500 }
Kevin Cernekee73e09202017-06-17 20:55:09 -0700501 if (record.type == net::dns_protocol::kTypeA &&
502 record.rdata.size() == ipv4_addr_len) {
Jason Jeremy Iman267a3372019-11-19 13:15:22 +0900503 struct in_addr rr_ip;
504 memcpy(&rr_ip, record.rdata.data(), ipv4_addr_len);
505 if (guest_ip.s_addr == rr_ip.s_addr) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700506 // HACK: This is able to calculate the (variable) offset of the IPv4
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900507 // address inside the resource record by assuming that the
508 // StringPiece returns a pointer inside the io_buffer. It works
509 // today, but future libchrome changes might break it.
Jason Jeremy Iman267a3372019-11-19 13:15:22 +0900510 size_t ip_offset = record.rdata.data() - resp.io_buffer()->data();
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900511 CHECK(ip_offset <= len - ipv4_addr_len);
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900512 memcpy(&data[ip_offset], &lan_ip.s_addr, ipv4_addr_len);
Kevin Cernekee73e09202017-06-17 20:55:09 -0700513 }
514 }
515 }
516}
517
Garrick Evans3388a032020-03-24 11:25:55 +0900518} // namespace patchpanel