Skip to content

Commit 37c5399

Browse files
committed
Send multicast packets to each interface
1 parent 6208017 commit 37c5399

File tree

5 files changed

+95
-26
lines changed

5 files changed

+95
-26
lines changed

src/api_config.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#include "api_config.h"
2+
#include "cast.h"
23
#include "common.h"
34
#include "inireader.h"
45
#include <algorithm>
56
#include <fstream>
67
#include <mutex>
78

8-
using namespace lsl;
9+
namespace lsl {
910

1011
/// Substitute the "~" character by the full home directory (according to environment variables).
1112
std::string expand_tilde(const std::string &filename) {
@@ -181,6 +182,37 @@ void api_config::load_from_file(const std::string &filename) {
181182
if (ttl_override >= 0) multicast_ttl_ = ttl_override;
182183
if (!address_override.empty()) multicast_addresses_ = address_override;
183184

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

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

@@ -243,3 +275,5 @@ api_config *api_config::get_instance_internal() {
243275
static api_config cfg;
244276
return &cfg;
245277
}
278+
279+
}

src/api_config.h

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

4+
#include "netinterfaces.h"
45
#include <loguru.hpp>
56
#include <cstdint>
67
#include <string>
@@ -107,8 +108,16 @@ class api_config {
107108
const std::string &listen_address() const { return listen_address_; }
108109

109110
/**
110-
* @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.
111113
*
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.
112121
* This is determined according to the ResolveScope setting if not overridden by the TTLOverride
113122
* setting. The higher this number (0-255), the broader their distribution. Routers (if
114123
* 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>
@@ -8,13 +9,16 @@
89
using namespace lsl;
910
using namespace lslboost::asio;
1011
using err_t = const lslboost::system::error_code &;
12+
using ip::multicast::outbound_interface;
1113

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

7377
// also initiate the cancel event, if desired
7478
if (cancel_after_ != FOREVER) {
@@ -142,27 +146,41 @@ void resolve_attempt_udp::handle_receive_outcome(error_code err, std::size_t len
142146

143147
// === send loop ===
144148

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

168186
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 i);
85+
void send_next_query(
86+
endpoint_list::const_iterator i, 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)