Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multicast node discovery for fastd #13

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions doc/examples/openwrt/fastd.init
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,8 @@ reload_instance() {

update_peer_groups "$s" true

rc_procd start_service "$s"
procd_send_signal fastd "$s"
rc_procd start_service "$s"
procd_send_signal fastd "$s"
}

start_service() {
Expand Down Expand Up @@ -402,3 +402,23 @@ generate_key() {

generate_key_instance "$instance"
}

service_triggers_instance() {
local s="$1"

section_enabled "$s" || return 0

config_get triggers "$s" triggers
if [ -n "$triggers" ]
then
for trigger in $triggers
do
procd_add_interface_trigger "interface.*" $trigger /etc/init.d/fastd restart "$s"
done
fi
}

service_triggers() {
config_foreach service_triggers_instance 'fastd'
procd_add_reload_trigger 'fastd'
}
13 changes: 13 additions & 0 deletions src/build.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@
/** Defined if the platform supports SO_FREEBIND */
#mesondefine USE_FREEBIND

/** Defined if the platform supports SO_FREEBIND6 */
#mesondefine USE_FREEBIND6

/** Defined if the platform supports IP_MTU_DISCOVER */
#mesondefine USE_PMTU

/** Defined if the platform supports IP_PKTINFO */
#mesondefine USE_PKTINFO

/** Defined if the platform support IFA_F_* for address types */
#mesondefine USE_IFAFLAGS

/** Defined if the platform supports SO_MARK */
#mesondefine USE_PACKET_MARK

Expand Down Expand Up @@ -118,6 +124,13 @@
#define REORDER_TIME 10000


/** Minimal discovery interval */
#define MIN_DISCOVERY_INTERVAL 1000 /* 1 second */

/** Default discovery interval */
#define DEFAULT_DISCOVERY_INTERVAL 5000 /* 5 seconds */


/** The minimum time that must pass between two on-verify calls on the same peer */
#define MIN_VERIFY_INTERVAL 10000 /* 10 seconds */

Expand Down
118 changes: 108 additions & 10 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "peer_group.h"

#include <dirent.h>
#include <ifaddrs.h>
#include <grp.h>
#include <libgen.h>
#include <pwd.h>
Expand All @@ -36,6 +37,10 @@

#include <sys/stat.h>

#ifdef USE_IFAFLAGS
#include <linux/if_addr.h>
#endif


/** The global configuration */
fastd_config_t conf = {};
Expand Down Expand Up @@ -119,34 +124,127 @@ void fastd_config_mac(const char *name, const char *impl) {
impl, name, name);
}

/** Handles the configuration of a bind address */
void fastd_config_bind_address(const fastd_peer_address_t *address, const char *bindtodev, unsigned flags) {
#ifndef USE_BINDTODEVICE
if (bindtodev && !fastd_peer_address_is_v6_ll(address))
exit_error("config error: device bind configuration not supported on this system");
/** Normalization of a bind address, initializing the target address structure */
static void normalize_address(fastd_peer_address_t *address, const fastd_peer_address_t *config, const char *bindtodev) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normalize_address() approach is problematic. Interface names must not be resolved to indices during configuration, as index changes should not require a restart of fastd.

if (config->sa.sa_family == AF_INET6 && config->in6.sin6_scope_id) {
if (!bindtodev)
exit_error("config error: need interface to bind to resolve ll6 address");

struct ifaddrs *ifaddrs;
if (getifaddrs(&ifaddrs))
exit_error("config error: failed to resolve interface addresses");

struct ifaddrs *ifa;
for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, bindtodev) || !ifa->ifa_addr)
continue;

const fastd_peer_address_t *addr = (const fastd_peer_address_t *)ifa->ifa_addr;
if (!fastd_peer_address_is_v6_ll(addr))
continue;
#ifdef USE_IFAFLAGS
if ((ifa->ifa_flags & IFA_F_DEPRECATED) || (ifa->ifa_flags & IFA_F_DADFAILED))
continue;
#endif

address->in6 = addr->in6;
address->in6.sin6_port = config->in6.sin6_port;
break;
}

freeifaddrs(ifaddrs);
if (!ifa)
exit_error("config error: failed to resolve any ll6 address for interface");
} else {
*address = *config;
if (bindtodev && fastd_peer_address_is_v6_ll(address)) {
char *end;
address->in6.sin6_scope_id = strtoul(bindtodev, &end, 10);

if (*end || !address->in6.sin6_scope_id)
address->in6.sin6_scope_id = if_nametoindex(bindtodev);

if (!address->in6.sin6_scope_id)
exit_error("config error: failed to resolve IPv6 LL scope interface");
} else
fastd_peer_address_simplify(address);
}
}

/** Handles the configuration of a bind address */
void fastd_config_bind_address(
const fastd_peer_address_t *address, const char *bindtodev, const fastd_peer_address_t *sourceaddr,
fastd_timeout_t interval, unsigned flags) {
#ifndef USE_MULTIAF_BIND
if (address->sa.sa_family == AF_UNSPEC) {
fastd_peer_address_t addr4 = { .in = { .sin_family = AF_INET, .sin_port = address->in.sin_port } };
fastd_peer_address_t addr6 = { .in6 = { .sin6_family = AF_INET6, .sin6_port = address->in.sin_port } };

fastd_config_bind_address(&addr4, bindtodev, flags);
fastd_config_bind_address(&addr6, bindtodev, flags);
if (sourceaddr->sa.sa_family != AF_INET6)
fastd_config_bind_address(&addr4, bindtodev, sourceaddr, interval, flags);
if (sourceaddr->sa.sa_family != AF_INET)
fastd_config_bind_address(&addr6, bindtodev, sourceaddr, interval, flags);
return;
}
#endif

fastd_bind_address_t *addr = fastd_new(fastd_bind_address_t);

normalize_address(&addr->addr, address, bindtodev);
normalize_address(&addr->sourceaddr, sourceaddr, bindtodev);

if (fastd_peer_address_is_multicast(&addr->addr)) {
if (addr->addr.sa.sa_family == AF_INET) {
if (!addr->addr.in.sin_port)
exit_error("config error: multicast bind requires port specification");
#ifndef USE_PKTINFO
exit_error("config error: multicast IPv4 requires PKTINFO support");
#endif
} else if (!addr->addr.in6.sin6_port)
exit_error("config error: multicast bind with no interface requires source address");

if (!bindtodev) {
if (addr->addr.sa.sa_family == AF_INET6)
exit_error("config error: multicast IPv6 bind requires bind interface");
if (addr->sourceaddr.sa.sa_family == AF_UNSPEC)
exit_error("config error: multicast IPv4 bind requires bind interface or source address");
}

if (addr->sourceaddr.sa.sa_family != AF_UNSPEC && addr->addr.sa.sa_family != addr->sourceaddr.sa.sa_family)
exit_error("config error: family of source address does not match family of multicast address");

if (interval != FASTD_TIMEOUT_INV) {
if (interval && interval < MIN_DISCOVERY_INTERVAL)
exit_error("config error: discovery interval is smaller than minimum 1 second");
} else
interval = DEFAULT_DISCOVERY_INTERVAL;
} else {
#ifndef USE_BINDTODEVICE
if (bindtodev && !fastd_peer_address_is_v6_ll(&addr->addr))
exit_error("config error: device bind configuration not supported on this system");
#endif

if (addr->addr.sa.sa_family != AF_UNSPEC && addr->sourceaddr.sa.sa_family != AF_UNSPEC) {
if (addr->addr.sa.sa_family != addr->sourceaddr.sa.sa_family)
exit_error("config error: family of source address does not match family of bind address");
if (!fastd_peer_address_is_any(&addr->addr) && !fastd_peer_address_is_host_equal(&addr->addr, &addr->sourceaddr))
exit_error("config error: source address is different from explicitly bound address");
}

if (interval != FASTD_TIMEOUT_INV)
exit_error("config error: discovery interval set on non-discovery enabled socket");
}

if (addr->sourceaddr.sa.sa_family != AF_UNSPEC && fastd_peer_address_is_multicast(&addr->sourceaddr))
exit_error("config error: source address is a multicast address");

addr->next = conf.bind_addrs;
conf.bind_addrs = addr;
conf.n_bind_addrs++;

addr->addr = *address;
addr->flags = flags;
addr->bindtodev = fastd_strdup(bindtodev);

fastd_peer_address_simplify(&addr->addr);
addr->interval = interval;

if (addr->addr.sa.sa_family != AF_INET6 && ((flags & FASTD_BIND_DEFAULT_IPV4) || !conf.bind_addr_default_v4))
conf.bind_addr_default_v4 = addr;
Expand Down
4 changes: 3 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ void fastd_config_method(fastd_peer_group_t *group, const char *name);
bool fastd_config_ifname(fastd_peer_t *peer, const char *ifname);
void fastd_config_cipher(const char *name, const char *impl);
void fastd_config_mac(const char *name, const char *impl);
void fastd_config_bind_address(const fastd_peer_address_t *address, const char *bindtodev, unsigned flags);
void fastd_config_bind_address(
const fastd_peer_address_t *address, const char *bindtodev, const fastd_peer_address_t *sourceaddr,
fastd_timeout_t interval, unsigned flags);
void fastd_config_release(void);
void fastd_config_handle_options(int argc, char *const argv[]);
void fastd_config_verify(void);
Expand Down
53 changes: 45 additions & 8 deletions src/config.y
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@
%token TOK_INCLUDE
%token TOK_INFO
%token TOK_INTERFACE
%token TOK_INTERVAL
%token TOK_IP
%token TOK_IPV4
%token TOK_IPV6
%token TOK_KEY
%token TOK_LEVEL
%token TOK_LIMIT
%token TOK_LL6
%token TOK_LOG
%token TOK_MAC
%token TOK_MARK
Expand All @@ -102,6 +104,7 @@
%token TOK_SECRET
%token TOK_SECURE
%token TOK_SOCKET
%token TOK_SOURCE
%token TOK_STATUS
%token TOK_STDERR
%token TOK_SYNC
Expand All @@ -126,7 +129,8 @@
#include <limits.h>

static void fastd_config_handle_bind_address(
fastd_peer_address_t address, int64_t maybe_port, const char *bindtodevice, unsigned bind_default);
fastd_peer_address_t address, int64_t maybe_port, const char *bindtodevice, fastd_peer_address_t source,
fastd_timeout_t interval, unsigned bind_default);

static void fastd_config_error(YYLTYPE *loc, fastd_parser_state_t *state, const char *s);
}
Expand All @@ -142,6 +146,8 @@
%type <uint64> maybe_af
%type <addr> bind_address
%type <str> maybe_bind_interface
%type <addr> maybe_source_address
%type <int64> maybe_interval
%type <uint64> maybe_bind_default
%type <uint64> bind_default
%type <uint64> drop_capabilities_enabled
Expand Down Expand Up @@ -306,12 +312,12 @@ interface: TOK_STRING {
}
;

bind: bind_address maybe_bind_port maybe_bind_interface maybe_bind_default {
fastd_config_handle_bind_address($1, $2, $3 ? $3->str : NULL, $4);
bind: bind_address maybe_bind_port maybe_bind_interface maybe_source_address maybe_interval maybe_bind_default {
fastd_config_handle_bind_address($1, $2, $3 ? $3->str : NULL, $4, $5, $6);
}
| TOK_ADDR6_SCOPED maybe_bind_port maybe_bind_default {
| TOK_ADDR6_SCOPED maybe_bind_port maybe_source_address maybe_interval maybe_bind_default {
fastd_peer_address_t addr = { .in6 = { .sin6_family = AF_INET6, .sin6_addr = $1.addr } };
fastd_config_handle_bind_address(addr, $2, $1.ifname, $3);
fastd_config_handle_bind_address(addr, $2, $1.ifname, $3, $4, $5);
}
;

Expand All @@ -322,8 +328,11 @@ bind_address:
| TOK_ADDR6 {
$$ = (fastd_peer_address_t){ .in6 = { .sin6_family = AF_INET6, .sin6_addr = $1 } };
}
| TOK_LL6 {
$$ = (fastd_peer_address_t){ .in6 = { .sin6_family = AF_INET6, .sin6_scope_id = -1 } };
}
| TOK_ANY {
$$ = (fastd_peer_address_t){ .in = { .sin_family = AF_UNSPEC } };
$$ = (fastd_peer_address_t){ .sa = { .sa_family = AF_UNSPEC } };
}
;

Expand All @@ -336,6 +345,30 @@ maybe_bind_interface:
}
;

maybe_source_address:
TOK_SOURCE TOK_ADDR4 {
$$ = (fastd_peer_address_t){ .in = { .sin_family = AF_INET, .sin_addr = $2 } };
}
| TOK_SOURCE TOK_ADDR6 {
$$ = (fastd_peer_address_t){ .in6 = { .sin6_family = AF_INET6, .sin6_addr = $2 } };
}
| TOK_SOURCE TOK_LL6 {
$$ = (fastd_peer_address_t){ .in6 = { .sin6_family = AF_INET6, .sin6_scope_id = -1 } };
}
| {
$$ = (fastd_peer_address_t){ .sa = { .sa_family = AF_UNSPEC } };
}
;

maybe_interval:
TOK_INTERVAL TOK_UINT {
$$ = $2;
}
| {
$$ = FASTD_TIMEOUT_INV;
}
;

maybe_bind_default:
TOK_DEFAULT bind_default {
$$ = $2;
Expand Down Expand Up @@ -671,7 +704,8 @@ bind_port: colon_or_port TOK_UINT {

%%
static void fastd_config_handle_bind_address(
fastd_peer_address_t address, int64_t maybe_port, const char *bindtodevice, unsigned bind_default) {
fastd_peer_address_t address, int64_t maybe_port, const char *bindtodevice,
fastd_peer_address_t source, fastd_timeout_t interval, unsigned bind_default) {

unsigned flags = bind_default;
uint16_t port = 0;
Expand All @@ -686,7 +720,10 @@ static void fastd_config_handle_bind_address(
else
address.in6.sin6_port = port;

fastd_config_bind_address(&address, bindtodevice, flags);
if (interval != FASTD_TIMEOUT_INV)
interval *= 1000;

fastd_config_bind_address(&address, bindtodevice, &source, interval, flags);
}

static void fastd_config_error(YYLTYPE *loc, fastd_parser_state_t *state, const char *s) {
Expand Down
2 changes: 1 addition & 1 deletion src/fastd.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ static void init_sockets(void) {
if (ctx.ioctl_sock < 0)
exit_errno("unable to create ioctl socket");

ctx.socks = fastd_new_array(conf.n_bind_addrs, fastd_socket_t);
ctx.socks = fastd_new0_array(conf.n_bind_addrs, fastd_socket_t);

size_t i;
fastd_bind_address_t *addr = conf.bind_addrs;
Expand Down
Loading