Skip to content

Commit 2d54dfe

Browse files
committed
Send multicast packets to each interface
1 parent f62b3bd commit 2d54dfe

File tree

5 files changed

+96
-26
lines changed

5 files changed

+96
-26
lines changed

src/api_config.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "api_config.h"
2+
#include "cast.h"
23
#include "common.h"
34
#include "inireader.h"
45
#include <algorithm>
@@ -8,7 +9,7 @@
89
#include <mutex>
910
#include <stdexcept>
1011

11-
using namespace lsl;
12+
namespace lsl {
1213

1314
/// Substitute the "~" character by the full home directory (according to environment variables).
1415
std::string expand_tilde(const std::string &filename) {
@@ -183,6 +184,37 @@ void api_config::load_from_file(const std::string &filename) {
183184
if (ttl_override >= 0) multicast_ttl_ = ttl_override;
184185
if (!address_override.empty()) multicast_addresses_ = address_override;
185186

187+
// The network stack requires the source interfaces for multicast packets to be
188+
// specified as IPv4 address or an IPv6 interface index
189+
// Try getting the interfaces from the configuration files
190+
using namespace lslboost::asio::ip;
191+
std::vector<std::string> netifs = parse_set(pt.get("multicast.Interfaces", "{}"));
192+
for (const auto &netifstr : netifs) {
193+
netif if_;
194+
if_.name = std::string("Configured in lslapi.cfg");
195+
if_.addr = make_address(netifstr);
196+
if (if_.addr.is_v6()) if_.ifindex = if_.addr.to_v6().scope_id();
197+
multicast_interfaces.push_back(if_);
198+
}
199+
// Try getting the interfaces from the OS
200+
if (multicast_interfaces.empty()) multicast_interfaces = get_local_interfaces();
201+
202+
// Otherwise, let the OS select an appropriate network interface
203+
if (multicast_interfaces.empty()) {
204+
LOG_F(ERROR,
205+
"No local network interface addresses found, resolving streams will likely "
206+
"only work for devices connected to the main network adapter\n");
207+
// Add dummy interface with default settings
208+
netif dummy;
209+
dummy.name = "Dummy interface";
210+
dummy.addr = address_v4::any();
211+
multicast_interfaces.push_back(dummy);
212+
dummy.name = "IPv6 dummy interface";
213+
dummy.addr = address_v6::any();
214+
multicast_interfaces.push_back(dummy);
215+
}
216+
217+
186218
// read the [lab] settings
187219
known_peers_ = parse_set(pt.get("lab.KnownPeers", "{}"));
188220
session_id_ = pt.get("lab.SessionID", "default");
@@ -212,7 +244,7 @@ void api_config::load_from_file(const std::string &filename) {
212244
force_default_timestamps_ = pt.get("tuning.ForceDefaultTimestamps", false);
213245

214246
// read the [log] settings
215-
int log_level = pt.get("log.level", (int) loguru::Verbosity_INFO);
247+
int log_level = pt.get("log.level", (int)loguru::Verbosity_INFO);
216248
if (log_level < -3 || log_level > 9)
217249
throw std::runtime_error("Invalid log.level (valid range: -3 to 9");
218250

@@ -251,3 +283,5 @@ api_config *api_config::get_instance_internal() {
251283
static api_config cfg;
252284
return &cfg;
253285
}
286+
287+
}

src/api_config.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef API_CONFIG_H
22
#define API_CONFIG_H
33

4+
#include "netinterfaces.h"
5+
#include <loguru.hpp>
46
#include <cstdint>
57
#include <string>
68
#include <vector>
@@ -106,8 +108,16 @@ class api_config {
106108
const std::string &listen_address() const { return listen_address_; }
107109

108110
/**
109-
* @brief The TTL setting (time-to-live) for the multicast packets.
111+
* A list of local interface addresses the multicast packets should be
112+
* sent from.
110113
*
114+
* The ini file may contain IPv4 addresses and/or IPv6 addresses with the
115+
* interface index as scope id, e.g. `1234:5678::2%3`
116+
**/
117+
std::vector<lsl::netif> multicast_interfaces;
118+
119+
/**
120+
* The TTL setting (time-to-live) for the multicast packets.
111121
* This is determined according to the ResolveScope setting if not overridden by the TTLOverride
112122
* setting. The higher this number (0-255), the broader their distribution. Routers (if
113123
* correctly configured) employ various thresholds below which packets are not further

src/cast.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ template signed char from_string(const std::string &);
3636
template char from_string(const std::string &);
3737
template int16_t from_string(const std::string &);
3838
template int32_t from_string(const std::string &);
39+
template uint32_t from_string(const std::string &);
3940
template int64_t from_string(const std::string &);
4041
} // namespace lsl

src/resolve_attempt_udp.cpp

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "resolve_attempt_udp.h"
22
#include "api_config.h"
3+
#include "netinterfaces.h"
34
#include "resolver_impl.h"
45
#include "socket_utils.h"
56
#include <boost/asio/ip/multicast.hpp>
@@ -9,13 +10,16 @@
910
using namespace lsl;
1011
namespace asio = lslboost::asio;
1112
using err_t = const lslboost::system::error_code &;
13+
using asio::ip::multicast::outbound_interface;
1214

1315
resolve_attempt_udp::resolve_attempt_udp(asio::io_context &io, const udp &protocol,
1416
const std::vector<udp::endpoint> &targets, const std::string &query, result_container &results,
1517
std::mutex &results_mut, double cancel_after, cancellable_registry *registry)
1618
: io_(io), results_(results), results_mut_(results_mut), cancel_after_(cancel_after),
1719
cancelled_(false), targets_(targets), query_(query), unicast_socket_(io),
18-
broadcast_socket_(io), multicast_socket_(io), recv_socket_(io), cancel_timer_(io) {
20+
broadcast_socket_(io), multicast_socket_(io),
21+
multicast_interfaces(api_config::get_instance()->multicast_interfaces), recv_socket_(io),
22+
cancel_timer_(io) {
1923
// open the sockets that we might need
2024
recv_socket_.open(protocol);
2125
try {
@@ -69,7 +73,7 @@ void resolve_attempt_udp::begin() {
6973
// initiate the result gathering chain
7074
receive_next_result();
7175
// initiate the send chain
72-
send_next_query(targets_.begin());
76+
send_next_query(targets_.begin(), multicast_interfaces.begin());
7377

7478
// also initiate the cancel event, if desired
7579
if (cancel_after_ != FOREVER) {
@@ -143,27 +147,41 @@ void resolve_attempt_udp::handle_receive_outcome(error_code err, std::size_t len
143147

144148
// === send loop ===
145149

146-
void resolve_attempt_udp::send_next_query(endpoint_list::const_iterator next) {
147-
if (next == targets_.end() || cancelled_) return;
148-
149-
udp::endpoint ep(*next++);
150-
// endpoint matches our active protocol?
151-
if (ep.protocol() == recv_socket_.local_endpoint().protocol()) {
152-
// select socket to use
153-
udp::socket &sock =
154-
(ep.address() == asio::ip::address_v4::broadcast())
155-
? broadcast_socket_
156-
: (ep.address().is_multicast() ? multicast_socket_ : unicast_socket_);
157-
// and send the query over it
158-
sock.async_send_to(lslboost::asio::buffer(query_msg_), ep,
159-
[shared_this = shared_from_this(), next](err_t err, size_t /*unused*/) {
160-
if (!shared_this->cancelled_ && err != asio::error::operation_aborted &&
161-
err != asio::error::not_connected && err != asio::error::not_socket)
162-
shared_this->send_next_query(next);
163-
});
150+
void resolve_attempt_udp::send_next_query(
151+
endpoint_list::const_iterator next, mcast_interface_list::const_iterator mcit) {
152+
if (cancelled_ || mcit == multicast_interfaces.end()) return;
153+
auto proto = recv_socket_.local_endpoint().protocol();
154+
if (next == targets_.begin()) {
155+
// Mismatching protocols? Skip this round
156+
if(mcit->addr.is_v4() != (proto==asio::ip::udp::v4()))
157+
next = targets_.end();
158+
else
159+
multicast_socket_.set_option(mcit->addr.is_v4() ? outbound_interface(mcit->addr.to_v4())
160+
: outbound_interface(mcit->ifindex));
161+
}
162+
if (next != targets_.end()) {
163+
udp::endpoint ep(*next++);
164+
// endpoint matches our active protocol?
165+
if (ep.protocol() == recv_socket_.local_endpoint().protocol()) {
166+
// select socket to use
167+
udp::socket &sock =
168+
(ep.address() == asio::ip::address_v4::broadcast())
169+
? broadcast_socket_
170+
: (ep.address().is_multicast() ? multicast_socket_ : unicast_socket_);
171+
// and send the query over it
172+
auto keepalive(shared_from_this());
173+
sock.async_send_to(
174+
lslboost::asio::buffer(query_msg_), ep, [shared_this = shared_from_this(), next, mcit](err_t err, size_t /*unused*/) {
175+
if (!shared_this->cancelled_ && err != asio::error::operation_aborted &&
176+
err != asio::error::not_connected && err != asio::error::not_socket)
177+
shared_this->send_next_query(next, mcit);
178+
});
179+
} else
180+
// otherwise just go directly to the next query
181+
send_next_query(next, mcit);
164182
} else
165-
// otherwise just go directly to the next query
166-
send_next_query(next);
183+
// Restart from the next interface
184+
send_next_query(targets_.begin(), ++mcit);
167185
}
168186

169187
void resolve_attempt_udp::do_cancel() {

src/resolve_attempt_udp.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#define RESOLVE_ATTEMPT_UDP_H
33

44
#include "cancellation.h"
5+
#include "netinterfaces.h"
56
#include "stream_info_impl.h"
7+
#include <boost/asio/ip/multicast.hpp>
68
#include <boost/asio/ip/udp.hpp>
79
#include <boost/asio/steady_timer.hpp>
810
#include <map>
@@ -20,6 +22,8 @@ namespace lsl {
2022

2123
/// A container for resolve results (map from stream instance UID onto (stream_info,receive-time)).
2224
typedef std::map<std::string, std::pair<stream_info_impl, double>> result_container;
25+
/// A container for outgoing multicast interfaces
26+
typedef std::vector<class netif> mcast_interface_list;
2327

2428
/**
2529
* An asynchronous resolve attempt for a single query targeted at a set of endpoints, via UDP.
@@ -78,7 +82,8 @@ class resolve_attempt_udp : public cancellable_obj,
7882
void receive_next_result();
7983

8084
/// Thos function starts an async send operation for the given current endpoint.
81-
void send_next_query(endpoint_list::const_iterator next);
85+
void send_next_query(
86+
endpoint_list::const_iterator next, mcast_interface_list::const_iterator mcit);
8287

8388
/// Handler that gets called when a receive has completed.
8489
void handle_receive_outcome(error_code err, std::size_t len);
@@ -124,6 +129,8 @@ class resolve_attempt_udp : public cancellable_obj,
124129
udp::socket broadcast_socket_;
125130
/// socket to send data over (for multicasts)
126131
udp::socket multicast_socket_;
132+
/// Interface addresses to send multicast packets from
133+
const mcast_interface_list &multicast_interfaces;
127134
/// socket to receive replies (always unicast)
128135
udp::socket recv_socket_;
129136
/// timer to schedule the cancel action

0 commit comments

Comments
 (0)