blob: 2be2f604dad140a42104cc973f6082414b088a50 [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
20namespace {
21
22const int kNumTempSockets = 4;
23const int kBufSize = 1536;
24const int kCleanupIntervalMs = 5000;
25const int kCleanupTimeSeconds = 30;
26
27} // namespace
28
29namespace arc_networkd {
30
31bool MulticastForwarder::Start(const std::string& int_ifname,
32 const std::string& lan_ifname,
33 const std::string& mcast_addr,
34 unsigned short port,
35 bool allow_stateless) {
36 int_ifname_ = int_ifname;
37 lan_ifname_ = lan_ifname;
38 port_ = port;
39 allow_stateless_ = allow_stateless;
40
41 if (!inet_aton(mcast_addr.c_str(), &mcast_addr_)) {
42 LOG(ERROR) << "invalid multicast address " << mcast_addr;
43 return false;
44 }
45
46 int_socket_.reset(new MulticastSocket());
47 int_socket_->Bind(int_ifname, mcast_addr_, port, this);
48
49 if (allow_stateless_) {
50 lan_socket_.reset(new MulticastSocket());
51 lan_socket_->Bind(lan_ifname, mcast_addr_, port, this);
52 }
53
54 CleanupTask();
55 return true;
56}
57
58// This callback is registered as part of MulticastSocket::Bind().
59// All of our sockets use this function as a common callback.
60void MulticastForwarder::OnFileCanReadWithoutBlocking(int fd) {
61 char data[kBufSize];
62 struct sockaddr_in fromaddr;
63
64 ssize_t bytes = MulticastSocket::RecvFromFd(fd, data, kBufSize, &fromaddr);
65 if (bytes < 0)
66 return;
67
68 unsigned short port = ntohs(fromaddr.sin_port);
69
70 struct sockaddr_in dst = {0};
71 dst.sin_family = AF_INET;
72 dst.sin_port = htons(port_);
73 dst.sin_addr = mcast_addr_;
74
75 // Forward traffic that is part of an existing connection.
76 for (auto& temp : temp_sockets_) {
77 if (fd == temp->fd()) {
78 int_socket_->SendTo(data, bytes, temp->int_addr);
79 return;
80 } else if (fd == int_socket_->fd() &&
81 fromaddr.sin_port == temp->int_addr.sin_port) {
82 temp->SendTo(data, bytes, dst);
83 return;
84 }
85 }
86
87 // Forward stateless traffic.
88 if (allow_stateless_ && port == port_) {
89 if (fd == int_socket_->fd()) {
90 lan_socket_->SendTo(data, bytes, dst);
91 return;
92 } else if (fd == lan_socket_->fd()) {
93 int_socket_->SendTo(data, bytes, dst);
94 return;
95 }
96 }
97
98 // New connection.
99 if (fd != int_socket_->fd())
100 return;
101
102 std::unique_ptr<MulticastSocket> new_sock(new MulticastSocket());
103 if (!new_sock->Bind(lan_ifname_, mcast_addr_, port, this) &&
104 !new_sock->Bind(lan_ifname_, mcast_addr_, 0, this))
105 return;
106 memcpy(&new_sock->int_addr, &fromaddr, sizeof(new_sock->int_addr));
107
108 new_sock->SendTo(data, bytes, dst);
109
110 // This should ideally delete the LRU entry, but since idle entries are
111 // purged by CleanupTask, the limit will only really be reached if
112 // the daemon is flooded with requests.
113 while (temp_sockets_.size() > kNumTempSockets)
114 temp_sockets_.pop_back();
115 temp_sockets_.push_front(std::move(new_sock));
116}
117
118void MulticastForwarder::CleanupTask() {
119 time_t exp = time(NULL) - kCleanupTimeSeconds;
120 for (auto it = temp_sockets_.begin(); it != temp_sockets_.end(); ) {
121 if ((*it)->last_used() < exp)
122 it = temp_sockets_.erase(it);
123 else
124 it++;
125 }
126
127 base::MessageLoopForIO::current()->PostDelayedTask(
128 FROM_HERE,
129 base::Bind(&MulticastForwarder::CleanupTask,
130 weak_factory_.GetWeakPtr()),
131 base::TimeDelta::FromMilliseconds(kCleanupIntervalMs));
132}
133
134} // namespace arc_networkd