Skip to content

Commit 062dc6e

Browse files
committed
Send multicast packets to each interface
1 parent 1a2e886 commit 062dc6e

File tree

5 files changed

+93
-26
lines changed

5 files changed

+93
-26
lines changed

src/api_config.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "api_config.h"
22
#include "common.h"
3+
#include "util/cast.hpp"
34
#include "util/inireader.hpp"
45
#include "util/strfuns.hpp"
56
#include <algorithm>
@@ -184,6 +185,37 @@ void api_config::load_from_file(const std::string &filename) {
184185
if (ttl_override >= 0) multicast_ttl_ = ttl_override;
185186
if (!address_override.empty()) multicast_addresses_ = address_override;
186187

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

217249
// read the [log] settings
218-
int log_level = pt.get("log.level", (int) loguru::Verbosity_INFO);
250+
int log_level = pt.get("log.level", (int)loguru::Verbosity_INFO);
219251
if (log_level < -3 || log_level > 9)
220252
throw std::runtime_error("Invalid log.level (valid range: -3 to 9");
221253

src/api_config.h

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

4+
#include "netinterfaces.h"
45
#include <cstdint>
6+
#include <loguru.hpp>
57
#include <string>
68
#include <vector>
79

@@ -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/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 "util/strfuns.hpp"
@@ -11,13 +12,16 @@
1112
#include <sstream>
1213

1314
using namespace lsl;
15+
using err_t = const asio::error_code &;
16+
using asio::ip::multicast::outbound_interface;
1417

1518
resolve_attempt_udp::resolve_attempt_udp(asio::io_context &io, const udp &protocol,
1619
const std::vector<udp::endpoint> &targets, const std::string &query, resolver_impl &resolver,
1720
double cancel_after)
1821
: io_(io), resolver_(resolver), cancel_after_(cancel_after), cancelled_(false),
1922
targets_(targets), query_(query), unicast_socket_(io), broadcast_socket_(io),
20-
multicast_socket_(io), recv_socket_(io), cancel_timer_(io) {
23+
multicast_socket_(io), multicast_interfaces(api_config::get_instance()->multicast_interfaces),
24+
recv_socket_(io), cancel_timer_(io) {
2125
// open the sockets that we might need
2226
recv_socket_.open(protocol);
2327
try {
@@ -71,7 +75,7 @@ void resolve_attempt_udp::begin() {
7175
// initiate the result gathering chain
7276
receive_next_result();
7377
// initiate the send chain
74-
send_next_query(targets_.begin());
78+
send_next_query(targets_.begin(), multicast_interfaces.begin());
7579

7680
// also initiate the cancel event, if desired
7781
if (cancel_after_ != FOREVER) {
@@ -153,27 +157,41 @@ void resolve_attempt_udp::handle_receive_outcome(err_t err, std::size_t len) {
153157

154158
// === send loop ===
155159

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

179197
void resolve_attempt_udp::do_cancel() {

src/resolve_attempt_udp.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
#include "cancellation.h"
55
#include "forward.h"
6+
#include "netinterfaces.h"
67
#include "stream_info_impl.h"
78
#include "socket_utils.h"
8-
#include <asio/ip/udp.hpp>
9+
#include <asio/ip/multicast.hpp>
910
#include <asio/steady_timer.hpp>
1011
#include <map>
1112
#include <memory>
@@ -21,6 +22,8 @@ using steady_timer = asio::basic_waitable_timer<asio::chrono::steady_clock, asio
2122

2223
/// A container for resolve results (map from stream instance UID onto (stream_info,receive-time)).
2324
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;
2427

2528
/**
2629
* An asynchronous resolve attempt for a single query targeted at a set of endpoints, via UDP.
@@ -78,7 +81,8 @@ class resolve_attempt_udp final : public cancellable_obj,
7881
void receive_next_result();
7982

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

8387
/// Handler that gets called when a receive has completed.
8488
void handle_receive_outcome(err_t err, std::size_t len);
@@ -122,6 +126,8 @@ class resolve_attempt_udp final : public cancellable_obj,
122126
udp_socket broadcast_socket_;
123127
/// socket to send data over (for multicasts)
124128
udp_socket multicast_socket_;
129+
/// Interface addresses to send multicast packets from
130+
const mcast_interface_list &multicast_interfaces;
125131
/// socket to receive replies (always unicast)
126132
udp_socket recv_socket_;
127133
/// timer to schedule the cancel action

src/util/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

0 commit comments

Comments
 (0)