blob: 59587a486e10a6311818a290c938c3f15e202bc1 [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>
19#include <base/message_loop/message_loop.h>
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070020
Garrick Evans3388a032020-03-24 11:25:55 +090021#include "patchpanel/dns/dns_protocol.h"
22#include "patchpanel/dns/dns_response.h"
23#include "patchpanel/net_util.h"
24#include "patchpanel/socket.h"
Kevin Cernekee73e09202017-06-17 20:55:09 -070025
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070026namespace {
27
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070028const int kBufSize = 1536;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070029
Hugo Benichidcce1142019-06-17 10:52:15 +090030// Returns the IPv4 address assigned to the interface on which the given socket
31// is bound. Or returns INADDR_ANY if the interface has no IPv4 address.
32struct in_addr GetInterfaceIp(int fd, const std::string& ifname) {
33 if (ifname.empty()) {
34 LOG(WARNING) << "Empty interface name";
35 return {0};
36 }
37
38 struct ifreq ifr;
39 memset(&ifr, 0, sizeof(ifr));
40 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
41 if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
42 // Ignore EADDRNOTAVAIL: IPv4 was not provisioned.
43 if (errno != EADDRNOTAVAIL) {
44 PLOG(ERROR) << "SIOCGIFADDR failed for " << ifname;
45 }
46 return {0};
47 }
48
49 struct sockaddr_in* if_addr =
50 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
51 return if_addr->sin_addr;
52}
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090053
54// Fills sockaddr_storage values.
55void SetSockaddr(struct sockaddr_storage* saddr_storage,
56 sa_family_t sa_family,
57 uint16_t port,
58 char* addr) {
59 struct sockaddr* saddr = reinterpret_cast<sockaddr*>(saddr_storage);
60 if (sa_family == AF_INET) {
61 struct sockaddr_in* saddr4 = reinterpret_cast<struct sockaddr_in*>(saddr);
62 saddr4->sin_family = AF_INET;
63 saddr4->sin_port = htons(port);
64 if (addr)
65 memcpy(&saddr4->sin_addr, addr, sizeof(struct in_addr));
66 return;
67 }
68 if (sa_family == AF_INET6) {
69 struct sockaddr_in6* saddr6 = reinterpret_cast<sockaddr_in6*>(saddr);
70 saddr6->sin6_family = AF_INET6;
71 saddr6->sin6_port = htons(port);
72 if (addr)
73 memcpy(&saddr6->sin6_addr, addr, sizeof(struct in6_addr));
74 return;
75 }
76 LOG(ERROR) << "Invalid socket family " << sa_family;
77}
78
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070079} // namespace
80
Garrick Evans3388a032020-03-24 11:25:55 +090081namespace patchpanel {
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070082
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090083MulticastForwarder::Socket::Socket(
84 base::ScopedFD fd,
85 sa_family_t sa_family,
86 const base::Callback<void(int, sa_family_t)>& callback)
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +090087 : fd(std::move(fd)) {
88 watcher = base::FileDescriptorWatcher::WatchReadable(
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090089 Socket::fd.get(),
90 base::BindRepeating(callback, Socket::fd.get(), sa_family));
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +090091}
92
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +090093MulticastForwarder::MulticastForwarder(const std::string& lan_ifname,
94 uint32_t mcast_addr,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090095 const std::string& mcast_addr6,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +090096 uint16_t port)
97 : lan_ifname_(lan_ifname), port_(port) {
98 mcast_addr_.s_addr = mcast_addr;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +090099 CHECK(inet_pton(AF_INET6, mcast_addr6.c_str(), mcast_addr6_.s6_addr));
100
101 base::ScopedFD lan_fd(Bind(AF_INET, lan_ifname_));
102 if (!lan_fd.is_valid()) {
103 LOG(WARNING) << "Could not bind socket on " << lan_ifname_ << " for "
104 << mcast_addr_ << ":" << port_;
105 }
106
107 base::ScopedFD lan_fd6(Bind(AF_INET6, lan_ifname_));
108 if (!lan_fd6.is_valid()) {
109 LOG(WARNING) << "Could not bind socket on " << lan_ifname_ << " for "
110 << mcast_addr6_ << ":" << port_;
111 }
112
113 lan_socket_.emplace(
114 AF_INET, new Socket(std::move(lan_fd), AF_INET,
115 base::BindRepeating(
116 &MulticastForwarder::OnFileCanReadWithoutBlocking,
117 base::Unretained(this))));
118
119 lan_socket_.emplace(
120 AF_INET6,
121 new Socket(
122 std::move(lan_fd6), AF_INET6,
123 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
124 base::Unretained(this))));
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900125}
126
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900127base::ScopedFD MulticastForwarder::Bind(sa_family_t sa_family,
128 const std::string& ifname) {
129 char mcast_addr[INET6_ADDRSTRLEN];
130 inet_ntop(sa_family,
131 sa_family == AF_INET ? reinterpret_cast<const void*>(&mcast_addr_)
132 : reinterpret_cast<const void*>(&mcast_addr6_),
133 mcast_addr, INET6_ADDRSTRLEN);
134
135 base::ScopedFD fd(socket(sa_family, SOCK_DGRAM, 0));
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900136 if (!fd.is_valid()) {
137 PLOG(ERROR) << "socket() failed for multicast forwarder on " << ifname
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900138 << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900139 return base::ScopedFD();
140 }
141
142 // The socket needs to be bound to INADDR_ANY rather than a specific
143 // interface, or it will not receive multicast traffic. Therefore
144 // we use SO_BINDTODEVICE to force TX from this interface, and
145 // specify the interface address in IP_ADD_MEMBERSHIP to control RX.
146 struct ifreq ifr;
147 memset(&ifr, 0, sizeof(ifr));
148 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
149 if (setsockopt(fd.get(), SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
150 PLOG(ERROR) << "setsockopt(SOL_SOCKET) failed for multicast forwarder on "
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900151 << ifname << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900152 return base::ScopedFD();
153 }
154
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900155 int ifindex = if_nametoindex(ifname.c_str());
156 if (ifindex == 0) {
157 PLOG(ERROR)
158 << "Could not obtain interface index for multicast forwarder on "
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900159 << ifname << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900160 return base::ScopedFD();
161 }
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900162
163 int level, optname;
164 if (sa_family == AF_INET) {
165 struct ip_mreqn mreqn;
166 memset(&mreqn, 0, sizeof(mreqn));
167 mreqn.imr_multiaddr = mcast_addr_;
168 mreqn.imr_address.s_addr = htonl(INADDR_ANY);
169 mreqn.imr_ifindex = ifindex;
170 if (setsockopt(fd.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn,
171 sizeof(mreqn)) < 0) {
172 PLOG(ERROR)
173 << "Can't add multicast membership for multicast forwarder on "
174 << ifname << " for " << mcast_addr_ << ":" << port_;
175 return base::ScopedFD();
176 }
177
178 level = IPPROTO_IP;
179 optname = IP_MULTICAST_LOOP;
180 } else if (sa_family == AF_INET6) {
181 struct ipv6_mreq mreqn;
182 memset(&mreqn, 0, sizeof(mreqn));
183 mreqn.ipv6mr_multiaddr = mcast_addr6_;
184 mreqn.ipv6mr_interface = ifindex;
185 if (setsockopt(fd.get(), IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreqn,
186 sizeof(mreqn)) < 0) {
187 PLOG(ERROR)
188 << "Can't add multicast membership for multicast forwarder on "
189 << ifname << " for " << mcast_addr6_ << ":" << port_;
190 return base::ScopedFD();
191 }
192
193 level = IPPROTO_IPV6;
194 optname = IPV6_MULTICAST_LOOP;
195 } else {
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900196 return base::ScopedFD();
197 }
198
199 int off = 0;
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900200 if (setsockopt(fd.get(), level, optname, &off, sizeof(off))) {
201 PLOG(ERROR)
202 << "setsockopt(IP_MULTICAST_LOOP) failed for multicast forwarder on "
203 << ifname << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900204 return base::ScopedFD();
205 }
206
207 int on = 1;
208 if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
209 PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed for multicast forwarder on "
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900210 << ifname << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900211 return base::ScopedFD();
212 }
213
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900214 struct sockaddr_storage bind_addr = {0};
215 SetSockaddr(&bind_addr, sa_family, port_, nullptr);
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900216
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900217 if (bind(fd.get(), (const struct sockaddr*)&bind_addr,
218 sizeof(struct sockaddr_storage)) < 0) {
219 PLOG(ERROR) << "bind(" << port_ << ") failed for multicast forwarder on "
220 << ifname << " for " << mcast_addr << ":" << port_;
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900221 return base::ScopedFD();
222 }
223
224 return fd;
225}
226
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900227bool MulticastForwarder::AddGuest(const std::string& int_ifname) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900228 if (int_sockets_.find(std::make_pair(AF_INET, int_ifname)) !=
229 int_sockets_.end() ||
230 int_sockets_.find(std::make_pair(AF_INET6, int_ifname)) !=
231 int_sockets_.end()) {
Jason Jeremy Iman3f062ea2019-11-12 08:37:53 +0900232 LOG(WARNING) << "Forwarding is already started between " << lan_ifname_
233 << " and " << int_ifname;
234 return false;
235 }
236
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900237 bool success = false;
238
239 // Set up IPv4 multicast forwarder.
240 base::ScopedFD int_fd4(Bind(AF_INET, int_ifname));
241 if (int_fd4.is_valid()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900242 int_fds_.emplace(std::make_pair(AF_INET, int_fd4.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900243
244 std::unique_ptr<Socket> int_socket4 = std::make_unique<Socket>(
245 std::move(int_fd4), AF_INET,
246 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
247 base::Unretained(this)));
248
249 int_sockets_.emplace(std::make_pair(AF_INET, int_ifname),
250 std::move(int_socket4));
251
252 success = true;
253 LOG(INFO) << "Started IPv4 forwarding between " << lan_ifname_ << " and "
254 << int_ifname << " for " << mcast_addr_ << ":" << port_;
255 } else {
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900256 LOG(WARNING) << "Could not bind socket on " << int_ifname << " for "
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900257 << mcast_addr_ << ":" << port_;
Hugo Benichi935eca92018-07-03 13:47:24 +0900258 }
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900259
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900260 // Set up IPv6 multicast forwarder.
261 base::ScopedFD int_fd6(Bind(AF_INET6, int_ifname));
262 if (int_fd6.is_valid()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900263 int_fds_.emplace(std::make_pair(AF_INET6, int_fd6.get()));
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900264
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900265 std::unique_ptr<Socket> int_socket6 = std::make_unique<Socket>(
266 std::move(int_fd6), AF_INET6,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900267 base::BindRepeating(&MulticastForwarder::OnFileCanReadWithoutBlocking,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900268 base::Unretained(this)));
269
270 int_sockets_.emplace(std::make_pair(AF_INET6, int_ifname),
271 std::move(int_socket6));
272
273 success = true;
274 LOG(INFO) << "Started IPv6 forwarding between " << lan_ifname_ << " and "
275 << int_ifname << " for " << mcast_addr6_ << ":" << port_;
276 } else {
277 LOG(WARNING) << "Could not bind socket on " << int_ifname << " for "
278 << mcast_addr6_ << ":" << port_;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700279 }
280
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900281 return success;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700282}
283
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900284void MulticastForwarder::RemoveGuest(const std::string& int_ifname) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900285 const auto& socket4 = int_sockets_.find(std::make_pair(AF_INET, int_ifname));
286 if (socket4 != int_sockets_.end()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900287 int_fds_.erase(std::make_pair(AF_INET, socket4->second->fd.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900288 int_sockets_.erase(socket4);
289 } else {
290 LOG(WARNING) << "IPv4 forwarding is not started between " << lan_ifname_
Jason Jeremy Iman3f062ea2019-11-12 08:37:53 +0900291 << " and " << int_ifname;
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900292 }
293
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900294 const auto& socket6 = int_sockets_.find(std::make_pair(AF_INET6, int_ifname));
295 if (socket6 != int_sockets_.end()) {
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900296 int_fds_.erase(std::make_pair(AF_INET6, socket6->second->fd.get()));
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900297 int_sockets_.erase(socket6);
298 } else {
299 LOG(WARNING) << "IPv6 forwarding is not started between " << lan_ifname_
300 << " and " << int_ifname;
301 }
Jason Jeremy Imand89b5f52019-10-24 10:39:17 +0900302}
303
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900304void MulticastForwarder::OnFileCanReadWithoutBlocking(int fd,
305 sa_family_t sa_family) {
306 CHECK(sa_family == AF_INET || sa_family == AF_INET6);
Jason Jeremy Iman97ac56d2019-10-09 14:15:35 +0900307
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900308 char data[kBufSize];
309
310 struct sockaddr_storage fromaddr_storage = {0};
311 struct sockaddr* fromaddr =
312 reinterpret_cast<struct sockaddr*>(&fromaddr_storage);
313
314 socklen_t addrlen = sizeof(struct sockaddr_storage);
315
316 ssize_t len = recvfrom(fd, data, kBufSize, 0, fromaddr, &addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900317 if (len < 0) {
Hugo Benichic4499672019-04-26 15:24:23 +0900318 PLOG(WARNING) << "recvfrom failed";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700319 return;
Hugo Benichic4499672019-04-26 15:24:23 +0900320 }
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900321
322 socklen_t expectlen = sa_family == AF_INET ? sizeof(struct sockaddr_in)
323 : sizeof(struct sockaddr_in6);
324 if (addrlen != expectlen) {
Hugo Benichic4499672019-04-26 15:24:23 +0900325 LOG(WARNING) << "recvfrom failed: unexpected src addr length " << addrlen;
326 return;
327 }
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700328
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900329 struct sockaddr_storage dst_storage = {0};
330 struct sockaddr* dst = reinterpret_cast<struct sockaddr*>(&dst_storage);
331 uint16_t src_port;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700332
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900333 if (sa_family == AF_INET) {
334 const struct sockaddr_in* addr4 =
335 reinterpret_cast<const struct sockaddr_in*>(fromaddr);
336 src_port = ntohs(addr4->sin_port);
337 } else if (sa_family == AF_INET6) {
338 const struct sockaddr_in6* addr6 =
339 reinterpret_cast<const struct sockaddr_in6*>(fromaddr);
340 src_port = ntohs(addr6->sin6_port);
341 }
342 SetSockaddr(&dst_storage, sa_family, port_,
343 sa_family == AF_INET ? reinterpret_cast<char*>(&mcast_addr_)
344 : reinterpret_cast<char*>(&mcast_addr6_));
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700345
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900346 // Forward ingress traffic to all guests.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900347 const auto& lan_socket = lan_socket_.find(sa_family);
348 if ((lan_socket != lan_socket_.end() && fd == lan_socket->second->fd.get())) {
349 SendToGuests(data, len, dst, addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900350 return;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700351 }
352
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900353 const auto& int_fd = int_fds_.find(std::make_pair(sa_family, fd));
354 if (int_fd == int_fds_.end() || lan_socket == lan_socket_.end())
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700355 return;
356
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900357 // Forward egress traffic from one guest to all other guests.
358 // No IP translation is required as other guests can route to each other
359 // behind the SNAT setup.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900360 SendToGuests(data, len, dst, addrlen, fd);
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900361
362 // On mDNS, sending to physical network requires translating any IPv4
363 // address specific to the guest and not visible to the physical network.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900364 if (sa_family == AF_INET && port_ == kMdnsPort) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900365 // TODO(b/132574450) The replacement address should instead be specified
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900366 // as an input argument, based on the properties of the network
367 // currently connected on |lan_ifname_|.
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900368 const struct in_addr lan_ip =
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900369 GetInterfaceIp(lan_socket->second->fd.get(), lan_ifname_);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900370 if (lan_ip.s_addr == htonl(INADDR_ANY)) {
371 // When the physical interface has no IPv4 address, IPv4 is not
372 // provisioned and there is no point in trying to forward traffic in
373 // either direction.
374 return;
375 }
Jason Jeremy Iman51a94cc2020-03-06 14:36:23 +0900376 TranslateMdnsIp(
377 lan_ip, reinterpret_cast<const struct sockaddr_in*>(fromaddr)->sin_addr,
378 data, len);
Hidehiko Abede129222019-08-16 00:55:04 +0900379 }
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700380
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900381 // Forward egress traffic from one guest to outside network.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900382 SendTo(src_port, data, len, dst, addrlen);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900383}
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700384
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900385bool MulticastForwarder::SendTo(uint16_t src_port,
386 const void* data,
387 ssize_t len,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900388 const struct sockaddr* dst,
389 socklen_t dst_len) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900390 if (src_port == port_) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900391 int lan_fd = lan_socket_.find(dst->sa_family)->second->fd.get();
392 if (sendto(lan_fd, data, len, 0, dst, dst_len) < 0) {
Jason Jeremy Iman6e8855f2019-10-09 12:12:38 +0900393 PLOG(WARNING) << "sendto failed";
394 return false;
395 }
396 return true;
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900397 }
398
Garrick Evans3388a032020-03-24 11:25:55 +0900399 patchpanel::Socket temp_socket(dst->sa_family, SOCK_DGRAM);
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900400
401 struct ifreq ifr;
402 memset(&ifr, 0, sizeof(ifr));
403 strncpy(ifr.ifr_name, lan_ifname_.c_str(), IFNAMSIZ);
404 if (setsockopt(temp_socket.fd(), SOL_SOCKET, SO_BINDTODEVICE, &ifr,
405 sizeof(ifr))) {
406 PLOG(ERROR) << "setsockopt(SOL_SOCKET) failed";
407 return false;
408 }
409
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900410 int level, optname;
411 struct sockaddr_storage bind_addr_storage = {0};
412 struct sockaddr* bind_addr = reinterpret_cast<sockaddr*>(&bind_addr_storage);
413 if (dst->sa_family == AF_INET) {
414 level = IPPROTO_IP;
415 optname = IP_MULTICAST_LOOP;
416 } else if (dst->sa_family == AF_INET6) {
417 level = IPPROTO_IPV6;
418 optname = IPV6_MULTICAST_LOOP;
419 } else {
420 return false;
421 }
422 SetSockaddr(&bind_addr_storage, dst->sa_family, src_port, nullptr);
423
Jason Jeremy Iman8fa749c2020-02-05 19:59:58 +0900424 int flag = 0;
425 if (setsockopt(temp_socket.fd(), level, optname, &flag, sizeof(flag))) {
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900426 PLOG(ERROR) << "setsockopt(IP_MULTICAST_LOOP) failed";
427 return false;
428 }
429
Jason Jeremy Iman8fa749c2020-02-05 19:59:58 +0900430 flag = 1;
431 if (setsockopt(temp_socket.fd(), SOL_SOCKET, SO_REUSEADDR, &flag,
432 sizeof(flag))) {
433 PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed";
434 return false;
435 }
436
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900437 if (!temp_socket.Bind(bind_addr, sizeof(struct sockaddr_storage)))
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900438 return false;
439
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900440 return temp_socket.SendTo(data, len, dst, dst_len);
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700441}
442
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900443bool MulticastForwarder::SendToGuests(const void* data,
444 ssize_t len,
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900445 const struct sockaddr* dst,
446 socklen_t dst_len,
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900447 int ignore_fd) {
448 bool success = true;
449 for (const auto& socket : int_sockets_) {
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900450 if (socket.first.first != dst->sa_family)
451 continue;
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900452 int fd = socket.second->fd.get();
453 if (fd == ignore_fd)
454 continue;
455
456 // Use already created multicast fd.
Jason Jeremy Imand04ad282019-10-09 14:15:35 +0900457 if (sendto(fd, data, len, 0, dst, dst_len) < 0) {
458 PLOG(WARNING) << "sendto failed to " << socket.first.second;
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900459 success = false;
460 }
461 }
462 return success;
463}
464
Hugo Benichi1661ca02019-10-16 15:36:13 +0900465// static
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900466void MulticastForwarder::TranslateMdnsIp(const struct in_addr& lan_ip,
Hugo Benichi1661ca02019-10-16 15:36:13 +0900467 const struct in_addr& guest_ip,
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900468 char* data,
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900469 ssize_t len) {
Hugo Benichi1661ca02019-10-16 15:36:13 +0900470 if (guest_ip.s_addr == htonl(INADDR_ANY)) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700471 return;
472 }
473
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900474 // Make sure this is a valid, successful DNS response from the Android
475 // host.
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900476 if (len > net::dns_protocol::kMaxUDPSize || len <= 0) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700477 return;
478 }
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900479
Kevin Cernekee73e09202017-06-17 20:55:09 -0700480 net::DnsResponse resp;
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900481 memcpy(resp.io_buffer()->data(), data, len);
482 if (!resp.InitParseWithoutQuery(len) ||
Kevin Cernekee73e09202017-06-17 20:55:09 -0700483 !(resp.flags() & net::dns_protocol::kFlagResponse) ||
484 resp.rcode() != net::dns_protocol::kRcodeNOERROR) {
485 return;
486 }
487
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900488 // Check all A records for the internal IP, and replace it with |lan_ip|
Kevin Cernekee73e09202017-06-17 20:55:09 -0700489 // if it is found.
490 net::DnsRecordParser parser = resp.Parser();
491 while (!parser.AtEnd()) {
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900492 const size_t ipv4_addr_len = sizeof(lan_ip.s_addr);
Kevin Cernekee73e09202017-06-17 20:55:09 -0700493
494 net::DnsResourceRecord record;
Kevin Cernekee41fbbb72017-07-26 14:09:40 -0700495 if (!parser.ReadRecord(&record)) {
496 break;
497 }
Kevin Cernekee73e09202017-06-17 20:55:09 -0700498 if (record.type == net::dns_protocol::kTypeA &&
499 record.rdata.size() == ipv4_addr_len) {
Jason Jeremy Iman267a3372019-11-19 13:15:22 +0900500 struct in_addr rr_ip;
501 memcpy(&rr_ip, record.rdata.data(), ipv4_addr_len);
502 if (guest_ip.s_addr == rr_ip.s_addr) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700503 // HACK: This is able to calculate the (variable) offset of the IPv4
Jason Jeremy Imanf63bc652019-10-09 12:41:30 +0900504 // address inside the resource record by assuming that the
505 // StringPiece returns a pointer inside the io_buffer. It works
506 // today, but future libchrome changes might break it.
Jason Jeremy Iman267a3372019-11-19 13:15:22 +0900507 size_t ip_offset = record.rdata.data() - resp.io_buffer()->data();
Jason Jeremy Iman52933042019-10-09 11:53:34 +0900508 CHECK(ip_offset <= len - ipv4_addr_len);
Hugo Benichi5b37b1d2019-06-07 13:22:26 +0900509 memcpy(&data[ip_offset], &lan_ip.s_addr, ipv4_addr_len);
Kevin Cernekee73e09202017-06-17 20:55:09 -0700510 }
511 }
512 }
513}
514
Garrick Evans3388a032020-03-24 11:25:55 +0900515} // namespace patchpanel