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