blob: 264e8823d79f18ed47ba5a393d0394ff8d7cd0ee [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
5#include "arc-networkd/multicast_forwarder.h"
6
7#include <arpa/inet.h>
8#include <netinet/ip.h>
9#include <string.h>
10#include <sys/socket.h>
11#include <sys/types.h>
12
Kevin Cernekeeb2c0c832016-12-06 11:47:57 -080013#include <utility>
14
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070015#include <base/bind.h>
16#include <base/logging.h>
17#include <base/message_loop/message_loop.h>
18#include <base/time/time.h>
19
Kevin Cernekee73e09202017-06-17 20:55:09 -070020#include "arc-networkd/dns/dns_protocol.h"
21#include "arc-networkd/dns/dns_response.h"
22
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070023namespace {
24
25const int kNumTempSockets = 4;
26const int kBufSize = 1536;
27const int kCleanupIntervalMs = 5000;
28const int kCleanupTimeSeconds = 30;
29
30} // namespace
31
32namespace arc_networkd {
33
34bool MulticastForwarder::Start(const std::string& int_ifname,
35 const std::string& lan_ifname,
Kevin Cernekee73e09202017-06-17 20:55:09 -070036 const std::string& mdns_ipaddr,
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070037 const std::string& mcast_addr,
38 unsigned short port,
39 bool allow_stateless) {
40 int_ifname_ = int_ifname;
41 lan_ifname_ = lan_ifname;
42 port_ = port;
43 allow_stateless_ = allow_stateless;
44
45 if (!inet_aton(mcast_addr.c_str(), &mcast_addr_)) {
46 LOG(ERROR) << "invalid multicast address " << mcast_addr;
47 return false;
48 }
49
Kevin Cernekee73e09202017-06-17 20:55:09 -070050 mdns_ip_.s_addr = INADDR_ANY;
51 if (!mdns_ipaddr.empty() && !inet_aton(mdns_ipaddr.c_str(), &mdns_ip_)) {
52 LOG(WARNING) << "invalid internal IP address " << mdns_ipaddr;
53 }
54
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070055 int_socket_.reset(new MulticastSocket());
56 int_socket_->Bind(int_ifname, mcast_addr_, port, this);
57
58 if (allow_stateless_) {
59 lan_socket_.reset(new MulticastSocket());
60 lan_socket_->Bind(lan_ifname, mcast_addr_, port, this);
Kevin Cernekee73e09202017-06-17 20:55:09 -070061 lan_ip_ = lan_socket_->interface_ip();
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070062 }
63
64 CleanupTask();
65 return true;
66}
67
68// This callback is registered as part of MulticastSocket::Bind().
69// All of our sockets use this function as a common callback.
70void MulticastForwarder::OnFileCanReadWithoutBlocking(int fd) {
71 char data[kBufSize];
72 struct sockaddr_in fromaddr;
73
74 ssize_t bytes = MulticastSocket::RecvFromFd(fd, data, kBufSize, &fromaddr);
75 if (bytes < 0)
76 return;
77
78 unsigned short port = ntohs(fromaddr.sin_port);
79
80 struct sockaddr_in dst = {0};
81 dst.sin_family = AF_INET;
82 dst.sin_port = htons(port_);
83 dst.sin_addr = mcast_addr_;
84
85 // Forward traffic that is part of an existing connection.
86 for (auto& temp : temp_sockets_) {
87 if (fd == temp->fd()) {
88 int_socket_->SendTo(data, bytes, temp->int_addr);
89 return;
90 } else if (fd == int_socket_->fd() &&
91 fromaddr.sin_port == temp->int_addr.sin_port) {
Kevin Cernekee73e09202017-06-17 20:55:09 -070092 TranslateMdnsIp(data, bytes);
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070093 temp->SendTo(data, bytes, dst);
94 return;
95 }
96 }
97
98 // Forward stateless traffic.
99 if (allow_stateless_ && port == port_) {
100 if (fd == int_socket_->fd()) {
Kevin Cernekee73e09202017-06-17 20:55:09 -0700101 TranslateMdnsIp(data, bytes);
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700102 lan_socket_->SendTo(data, bytes, dst);
103 return;
104 } else if (fd == lan_socket_->fd()) {
105 int_socket_->SendTo(data, bytes, dst);
106 return;
107 }
108 }
109
110 // New connection.
111 if (fd != int_socket_->fd())
112 return;
113
114 std::unique_ptr<MulticastSocket> new_sock(new MulticastSocket());
115 if (!new_sock->Bind(lan_ifname_, mcast_addr_, port, this) &&
116 !new_sock->Bind(lan_ifname_, mcast_addr_, 0, this))
117 return;
118 memcpy(&new_sock->int_addr, &fromaddr, sizeof(new_sock->int_addr));
119
120 new_sock->SendTo(data, bytes, dst);
121
122 // This should ideally delete the LRU entry, but since idle entries are
123 // purged by CleanupTask, the limit will only really be reached if
124 // the daemon is flooded with requests.
125 while (temp_sockets_.size() > kNumTempSockets)
126 temp_sockets_.pop_back();
127 temp_sockets_.push_front(std::move(new_sock));
128}
129
Kevin Cernekee73e09202017-06-17 20:55:09 -0700130void MulticastForwarder::TranslateMdnsIp(char* data, ssize_t bytes) {
131 if (mdns_ip_.s_addr == INADDR_ANY) {
132 return;
133 }
134
135 // Make sure this is a valid, successful DNS response from the Android host.
136 if (bytes > net::dns_protocol::kMaxUDPSize || bytes <= 0) {
137 return;
138 }
139 net::DnsResponse resp;
140 memcpy(resp.io_buffer()->data(), data, bytes);
141 if (!resp.InitParseWithoutQuery(bytes) ||
142 !(resp.flags() & net::dns_protocol::kFlagResponse) ||
143 resp.rcode() != net::dns_protocol::kRcodeNOERROR) {
144 return;
145 }
146
147 // Check all A records for the internal IP, and replace it with |lan_ip_|
148 // if it is found.
149 net::DnsRecordParser parser = resp.Parser();
150 while (!parser.AtEnd()) {
151 const size_t ipv4_addr_len = sizeof(lan_ip_.s_addr);
152
153 net::DnsResourceRecord record;
154 DCHECK(parser.ReadRecord(&record));
155 if (record.type == net::dns_protocol::kTypeA &&
156 record.rdata.size() == ipv4_addr_len) {
157 const char* rr_ip = record.rdata.data();
158 if (mdns_ip_.s_addr ==
159 reinterpret_cast<const struct in_addr*>(rr_ip)->s_addr) {
160 // HACK: This is able to calculate the (variable) offset of the IPv4
161 // address inside the resource record by assuming that the StringPiece
162 // returns a pointer inside the io_buffer. It works today, but
163 // future libchrome changes might break it.
164 size_t ip_offset = rr_ip - resp.io_buffer()->data();
165 CHECK(ip_offset <= bytes - ipv4_addr_len);
166 memcpy(&data[ip_offset], &lan_ip_.s_addr, ipv4_addr_len);
167 }
168 }
169 }
170}
171
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700172void MulticastForwarder::CleanupTask() {
173 time_t exp = time(NULL) - kCleanupTimeSeconds;
174 for (auto it = temp_sockets_.begin(); it != temp_sockets_.end(); ) {
175 if ((*it)->last_used() < exp)
176 it = temp_sockets_.erase(it);
177 else
178 it++;
179 }
180
181 base::MessageLoopForIO::current()->PostDelayedTask(
182 FROM_HERE,
183 base::Bind(&MulticastForwarder::CleanupTask,
184 weak_factory_.GetWeakPtr()),
185 base::TimeDelta::FromMilliseconds(kCleanupIntervalMs));
186}
187
188} // namespace arc_networkd