diff options
-rw-r--r-- | src/config.c | 240 | ||||
-rw-r--r-- | src/config.y | 2 | ||||
-rw-r--r-- | src/fastd.c | 49 | ||||
-rw-r--r-- | src/fastd.h | 14 | ||||
-rw-r--r-- | src/peer.c | 60 | ||||
-rw-r--r-- | src/peer.h | 9 | ||||
-rw-r--r-- | src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c | 87 | ||||
-rw-r--r-- | src/protocol_null.c | 14 |
8 files changed, 362 insertions, 113 deletions
diff --git a/src/config.c b/src/config.c index c708609..7725c40 100644 --- a/src/config.c +++ b/src/config.c @@ -71,6 +71,7 @@ static void default_config(fastd_config *conf) { conf->key_valid = 3600; /* 60 minutes */ conf->key_refresh = 3300; /* 55 minutes */ + conf->peer_dirs = NULL; conf->peers = NULL; conf->on_up = NULL; @@ -96,67 +97,69 @@ static bool config_match(const char *opt, ...) { return match; } -void fastd_read_config_dir(fastd_context *ctx, fastd_config *conf, const char *dir, int depth) { - if (depth >= MAX_CONFIG_DEPTH) - exit_error(ctx, "maximum config include depth exceeded"); +static void read_peer_dir(fastd_context *ctx, fastd_config *conf, const char *dir) { + DIR *dirh = opendir("."); - char *oldcwd = get_current_dir_name(); + if (dirh) { + while (true) { + struct dirent entry, *result; + int ret; - if (!chdir(dir)) { - char *newdir = get_current_dir_name(); - conf->peer_dirs = fastd_string_stack_push(conf->peer_dirs, newdir); - free(newdir); + ret = readdir_r(dirh, &entry, &result); + if (ret) { + pr_error(ctx, "readdir_r: %s", strerror(ret)); + break; + } - DIR *dirh = opendir("."); + if (!result) + break; + if (result->d_name[0] == '.') + continue; - if (dirh) { - while (true) { - struct dirent entry, *result; - int ret; + struct stat statbuf; + if (stat(result->d_name, &statbuf)) { + pr_warn(ctx, "ignoring file `%s': stat failed: %s", result->d_name, strerror(errno)); + continue; + } + if ((statbuf.st_mode & S_IFMT) != S_IFREG) { + pr_info(ctx, "ignoring file `%s': no regular file", result->d_name); + continue; + } - ret = readdir_r(dirh, &entry, &result); - if (ret) { - pr_error(ctx, "readdir_r: %s", strerror(ret)); - break; - } + fastd_peer_config_new(ctx, conf); + conf->peers->name = strdup(result->d_name); + conf->peers->config_source_dir = dir; - if (!result) - break; - if (result->d_name[0] == '.') - continue; + if (!fastd_read_config(ctx, conf, result->d_name, true, 0)) { + pr_warn(ctx, "peer config `%s' will be ignored", result->d_name); + fastd_peer_config_delete(ctx, conf); + } + } - struct stat statbuf; - if (stat(result->d_name, &statbuf)) { - pr_warn(ctx, "ignoring file `%s': stat failed: %s", result->d_name, strerror(errno)); - continue; - } - if ((statbuf.st_mode & S_IFMT) != S_IFREG) { - pr_info(ctx, "ignoring file `%s': no regular file", result->d_name); - continue; - } + closedir(dirh); + } + else { + pr_error(ctx, "opendir for `%s' failed: %s", dir, strerror(errno)); + } +} - fastd_peer_config_new(ctx, conf); - conf->peers->name = strdup(result->d_name); - conf->peers->config_source_dir = conf->peer_dirs->str; +void fastd_read_peer_dir(fastd_context *ctx, fastd_config *conf, const char *dir) { + char *oldcwd = get_current_dir_name(); - if (!fastd_read_config(ctx, conf, result->d_name, true, depth)) { - pr_warn(ctx, "peer config `%s' will be ignored", result->d_name); - fastd_peer_config_delete(ctx, conf); - } - } + if (!chdir(dir)) { + char *newdir = get_current_dir_name(); + conf->peer_dirs = fastd_string_stack_push(conf->peer_dirs, newdir); + free(newdir); - closedir(dirh); - } - else { - pr_error(ctx, "opendir for `%s' failed: %s", dir, strerror(errno)); - } + read_peer_dir(ctx, conf, conf->peer_dirs->str); chdir(oldcwd); - free(oldcwd); } else { pr_error(ctx, "change from directory `%s' to `%s' failed: %s", oldcwd, dir, strerror(errno)); } + + free(oldcwd); } bool fastd_read_config(fastd_context *ctx, fastd_config *conf, const char *filename, bool peer_config, int depth) { @@ -248,6 +251,32 @@ bool fastd_read_config(fastd_context *ctx, fastd_config *conf, const char *filen return ret; } +static void count_peers(fastd_context *ctx, fastd_config *conf) { + conf->n_floating = 0; + conf->n_v4 = 0; + conf->n_v6 = 0; + + fastd_peer_config *peer; + for (peer = conf->peers; peer; peer = peer->next) { + switch (peer->address.sa.sa_family) { + case AF_UNSPEC: + conf->n_floating++; + break; + + case AF_INET: + conf->n_v4++; + break; + + case AF_INET6: + conf->n_v6++; + break; + + default: + exit_bug(ctx, "invalid peer address family"); + } + } +} + #define IF_OPTION(args...) if(config_match(argv[i], args, NULL) && (++i)) #define IF_OPTION_ARG(args...) if(config_match(argv[i], args, NULL) && ({ \ arg = argv[i+1]; \ @@ -309,7 +338,7 @@ void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *con } IF_OPTION_ARG("--config-peer-dir") { - fastd_read_config_dir(ctx, conf, arg, 0); + fastd_read_peer_dir(ctx, conf, arg); continue; } @@ -478,35 +507,110 @@ void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *con exit(0); } - conf->n_floating = 0; - conf->n_v4 = 0; - conf->n_v6 = 0; + if (conf->mode == MODE_TUN) { + if (!conf->peers || conf->peers->next) + exit_error(ctx, "config error: for tun mode exactly one peer must be configured"); + if (conf->peer_dirs) + exit_error(ctx, "config error: for tun mode peer directories can't be used"); + } - for (peer = conf->peers; peer; peer = peer->next) { - switch (peer->address.sa.sa_family) { - case AF_UNSPEC: - conf->n_floating++; - break; + count_peers(ctx, conf); +} - case AF_INET: - conf->n_v4++; - break; +static void reconfigure_read_peer_dirs(fastd_context *ctx, fastd_config *new_conf, fastd_string_stack *dirs) { + char *oldcwd = get_current_dir_name(); - case AF_INET6: - conf->n_v6++; - break; + fastd_string_stack *dir; + for (dir = dirs; dir; dir = dir->next) { + if (!chdir(dir->str)) + read_peer_dir(ctx, new_conf, dir->str); + else + pr_error(ctx, "change from directory `%s' to `%s' failed: %s", oldcwd, dir->str, strerror(errno)); + } - default: - exit_bug(ctx, "invalid peer address family"); + chdir(oldcwd); + free(oldcwd); +} + +static void reconfigure_handle_old_peers(fastd_context *ctx, fastd_peer_config **old_peers, fastd_peer_config **new_peers) { + fastd_peer_config **peer, **next, **new_peer, **new_next; + for (peer = old_peers; *peer; peer = next) { + next = &(*peer)->next; + + /* don't touch statically configured peers */ + if (!(*peer)->config_source_dir) + continue; + + /* search for each peer in the list of new peers */ + for (new_peer = new_peers; *new_peer; new_peer = new_next) { + new_next = &(*new_peer)->next; + + if (((*peer)->config_source_dir == (*new_peer)->config_source_dir) && strequal((*peer)->name, (*new_peer)->name)) { + if (fastd_peer_config_equal(*peer, *new_peer)) { + pr_debug(ctx, "peer `%s' unchanged", (*peer)->name); + + fastd_peer_config *free_peer = *new_peer; + *new_peer = *new_next; + fastd_peer_config_free(free_peer); + peer = NULL; + } + else { + pr_debug(ctx, "peer `%s' changed, resetting", (*peer)->name); + new_peer = NULL; + } + + break; + } + } + + /* no new peer was found, or the old one has changed */ + if (peer && (!new_peer || !*new_peer)) { + pr_debug(ctx, "removing peer `%s'", (*peer)->name); + + fastd_peer_config *free_peer = *peer; + *peer = *next; + next = peer; + + fastd_peer_config_purge(ctx, free_peer); } } +} - if (conf->mode == MODE_TUN) { - if (!conf->peers || conf->peers->next) - exit_error(ctx, "config error: for tun mode exactly one peer must be configured"); - if (conf->peer_dirs) - exit_error(ctx, "config error: for tun mode peer directories can't be used"); +static void reconfigure_reset_waiting(fastd_context *ctx) { + fastd_peer *peer; + for (peer = ctx->peers; peer; peer = peer->next) { + if (fastd_peer_is_waiting(peer)) + fastd_peer_reset(ctx, peer); + } +} + +static void reconfigure_handle_new_peers(fastd_context *ctx, fastd_peer_config **peers, fastd_peer_config *new_peers) { + fastd_peer_config *peer, *next; + for (peer = new_peers; peer; peer = next) { + next = peer->next; + + ctx->conf->protocol->peer_configure(ctx, peer); + if (peer->enabled) + fastd_peer_add(ctx, peer); + + peer->next = *peers; + *peers = peer; } +} + +void fastd_reconfigure(fastd_context *ctx, fastd_config *conf) { + pr_info(ctx, "reconfigure triggered"); + + fastd_config temp_conf; + temp_conf.peers = NULL; + + reconfigure_read_peer_dirs(ctx, &temp_conf, conf->peer_dirs); + reconfigure_handle_old_peers(ctx, &conf->peers, &temp_conf.peers); + + reconfigure_reset_waiting(ctx); + + reconfigure_handle_new_peers(ctx, &conf->peers, temp_conf.peers); + + count_peers(ctx, conf); - conf->protocol->init(ctx, conf); } diff --git a/src/config.y b/src/config.y index 8b5e49e..332ad77 100644 --- a/src/config.y +++ b/src/config.y @@ -250,7 +250,7 @@ include: TOK_PEER TOK_STRING maybe_as { YYERROR; } | TOK_PEERS TOK_FROM TOK_STRING { - fastd_read_config_dir(ctx, conf, $3->str, depth); + fastd_read_peer_dir(ctx, conf, $3->str); } | TOK_STRING { if (!fastd_read_config(ctx, conf, $1->str, false, depth)) diff --git a/src/fastd.c b/src/fastd.c index 3185186..f38bf60 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -35,12 +35,32 @@ #include <linux/if_tun.h> #include <net/if.h> #include <poll.h> +#include <signal.h> #include <string.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <unistd.h> +static bool sighup = false; + + +static void on_sighup(int signo) { + sighup = true; +} + + +static void init_signals(fastd_context *ctx) { + struct sigaction action; + + action.sa_handler = on_sighup; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + + if(sigaction(SIGHUP, &action, NULL)) + exit_errno(ctx, "sigaction"); +} + static void init_tuntap(fastd_context *ctx) { struct ifreq ifr; @@ -161,6 +181,8 @@ static void on_up(fastd_context *ctx) { static void init_peers(fastd_context *ctx) { fastd_peer_config *peer_conf; for (peer_conf = ctx->conf->peers; peer_conf; peer_conf = peer_conf->next) { + ctx->conf->protocol->peer_configure(ctx, peer_conf); + if (peer_conf->enabled) fastd_peer_add(ctx, peer_conf); } @@ -280,8 +302,14 @@ static void handle_tun(fastd_context *ctx) { fastd_buffer buffer = fastd_buffer_alloc(max_len, ctx->conf->protocol->min_encrypt_head_space(ctx), 0); ssize_t len = read(ctx->tunfd, buffer.data, max_len); - if (len < 0) + if (len < 0) { + if (errno == EINTR) { + fastd_buffer_free(buffer); + return; + } + exit_errno(ctx, "read"); + } buffer.len = len; @@ -339,7 +367,9 @@ static void handle_socket(fastd_context *ctx, int sockfd) { ssize_t len = recvmsg(sockfd, &msg, 0); if (len < 0) { - pr_warn(ctx, "recvfrom: %s", strerror(errno)); + if (errno != EINTR) + pr_warn(ctx, "recvfrom: %s", strerror(errno)); + fastd_buffer_free(buffer); return; } @@ -423,8 +453,12 @@ static void handle_input(fastd_context *ctx) { timeout = 60000; /* call maintenance at least once a minute */ int ret = poll(fds, 3, timeout); - if (ret < 0) + if (ret < 0) { + if (errno == EINTR) + return; + exit_errno(ctx, "poll"); + } update_time(ctx); @@ -470,6 +504,10 @@ int main(int argc, char *argv[]) { fastd_configure(&ctx, &conf, argc, argv); ctx.conf = &conf; + conf.protocol_config = conf.protocol->init(&ctx); + + init_signals(&ctx); + update_time(&ctx); init_peers(&ctx); @@ -484,6 +522,11 @@ int main(int argc, char *argv[]) { handle_input(&ctx); maintenance(&ctx); + + if (sighup) { + sighup = false; + fastd_reconfigure(&ctx, &conf); + } } return 0; diff --git a/src/fastd.h b/src/fastd.h index 4a4bd49..f07f108 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -59,7 +59,9 @@ struct _fastd_eth_addr { struct _fastd_protocol { const char *name; - void (*init)(fastd_context *ctx, fastd_config *conf); + fastd_protocol_config* (*init)(fastd_context *ctx); + void (*peer_configure)(fastd_context *ctx, fastd_peer_config *peer_conf); + void (*peer_config_purged)(fastd_context *ctx, fastd_peer_config *peer_conf); size_t (*max_packet_size)(fastd_context *ctx); size_t (*min_encrypt_head_space)(fastd_context *ctx); @@ -140,9 +142,10 @@ struct _fastd_string_stack { void fastd_printf(const fastd_context *ctx, const char *format, ...); -void fastd_read_config_dir(fastd_context *ctx, fastd_config *conf, const char *dir, int depth); +void fastd_read_peer_dir(fastd_context *ctx, fastd_config *conf, const char *dir); bool fastd_read_config(fastd_context *ctx, fastd_config *conf, const char *filename, bool peer_config, int depth); void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *const argv[]); +void fastd_reconfigure(fastd_context *ctx, fastd_config *conf); void fastd_random_bytes(fastd_context *ctx, void *buffer, size_t len, bool secure); @@ -265,4 +268,11 @@ static inline int timespec_diff(const struct timespec *tp1, const struct timespe return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6; } +static inline bool strequal(const char *str1, const char *str2) { + if (str1 && str2) + return (!strcmp(str1, str2)); + else + return (str1 == str2); +} + #endif /* _FASTD_FASTD_H_ */ @@ -95,16 +95,69 @@ fastd_peer_config* fastd_peer_config_new(fastd_context *ctx, fastd_config *conf) return peer; } -void fastd_peer_config_delete(fastd_context *ctx, fastd_config *conf) { - fastd_peer_config *peer = conf->peers, *next = peer->next; - +void fastd_peer_config_free(fastd_peer_config *peer) { free(peer->name); free(peer->key); + free(peer->protocol_config); free(peer); +} +void fastd_peer_config_delete(fastd_context *ctx, fastd_config *conf) { + fastd_peer_config *peer = conf->peers, *next = peer->next; + fastd_peer_config_free(peer); conf->peers = next; } +void fastd_peer_config_purge(fastd_context *ctx, fastd_peer_config *conf) { + fastd_peer *peer, *next; + for (peer = ctx->peers; peer; peer = next) { + next = peer->next; + + if (peer->config == conf) { + reset_peer(ctx, peer); + delete_peer(ctx, peer); + continue; + } + } + + ctx->conf->protocol->peer_config_purged(ctx, conf); + fastd_peer_config_free(conf); +} + +static bool fastd_peer_addr_equal(const fastd_peer_address *addr1, const fastd_peer_address *addr2) { + if (addr1->sa.sa_family != addr2->sa.sa_family) + return false; + + switch (addr1->sa.sa_family) { + case AF_UNSPEC: + break; + + case AF_INET: + if (addr1->in.sin_addr.s_addr != addr2->in.sin_addr.s_addr) + return false; + break; + + case AF_INET6: + if (!IN6_ARE_ADDR_EQUAL(&addr1->in6.sin6_addr, &addr2->in6.sin6_addr)) + return false; + } + + return true; +} + +bool fastd_peer_config_equal(const fastd_peer_config *peer1, const fastd_peer_config *peer2) { + if (peer1->enabled != peer2->enabled) + return false; + + if (!fastd_peer_addr_equal(&peer1->address, &peer2->address)) + return false; + + if (!strequal(peer1->key, peer2->key)) + return false; + + return true; +} + void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer) { pr_debug(ctx, "resetting peer %P", peer); @@ -116,7 +169,6 @@ void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer) { setup_peer(ctx, peer); } - static fastd_peer* add_peer(fastd_context *ctx) { fastd_peer *peer = malloc(sizeof(fastd_peer)); @@ -54,7 +54,7 @@ struct _fastd_peer { struct _fastd_peer_config { fastd_peer_config *next; - char *config_source_dir; + const char *config_source_dir; bool enabled; char *name; @@ -73,7 +73,10 @@ struct _fastd_peer_eth_addr { fastd_peer_config* fastd_peer_config_new(fastd_context *ctx, fastd_config *conf); +void fastd_peer_config_free(fastd_peer_config *peer); void fastd_peer_config_delete(fastd_context *ctx, fastd_config *conf); +void fastd_peer_config_purge(fastd_context *ctx, fastd_peer_config *conf); +bool fastd_peer_config_equal(const fastd_peer_config *peer1, const fastd_peer_config *peer2); void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer); fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *conf); @@ -92,6 +95,10 @@ static inline bool fastd_peer_is_floating(const fastd_peer *peer) { return (peer->config && fastd_peer_config_is_floating(peer->config)); } +static inline bool fastd_peer_is_waiting(const fastd_peer *peer) { + return (peer->state == STATE_WAIT); +} + static inline bool fastd_peer_is_temporary(const fastd_peer *peer) { return (peer->state == STATE_TEMP); } diff --git a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c b/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c index df94d14..8dbf10e 100644 --- a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c +++ b/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c @@ -191,50 +191,39 @@ static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t return false; } -static void protocol_init(fastd_context *ctx, fastd_config *conf) { - conf->protocol_config = malloc(sizeof(fastd_protocol_config)); +static fastd_protocol_config* protocol_init(fastd_context *ctx) { + fastd_protocol_config *protocol_config = malloc(sizeof(fastd_protocol_config)); - if (!conf->secret) + if (!ctx->conf->secret) exit_error(ctx, "no secret key configured"); - if (!read_key(conf->protocol_config->secret_key.s, conf->secret)) + if (!read_key(protocol_config->secret_key.s, ctx->conf->secret)) exit_error(ctx, "invalid secret key"); ecc_25519_work work; - ecc_25519_scalarmult_base(&work, &conf->protocol_config->secret_key); - ecc_25519_store(&conf->protocol_config->public_key, &work); + ecc_25519_scalarmult_base(&work, &protocol_config->secret_key); + ecc_25519_store(&protocol_config->public_key, &work); - fastd_peer_config *peer; - for (peer = conf->peers; peer; peer = peer->next) { - ecc_public_key_256 key; - - if (!peer->key) { - pr_warn(ctx, "no key configured for %P, disabling peer", peer); - peer->enabled = false; - continue; - } + return protocol_config; +} - if (!read_key(key.p, peer->key)) { - pr_warn(ctx, "invalid key configured for %P, disabling peer", peer); - peer->enabled = false; - continue; - } +static void protocol_peer_configure(fastd_context *ctx, fastd_peer_config *peer_conf) { + ecc_public_key_256 key; - peer->protocol_config = malloc(sizeof(fastd_protocol_peer_config)); - peer->protocol_config->public_key = key; + if (!peer_conf->key) { + pr_warn(ctx, "no key configured for `%s', disabling peer", peer_conf->name); + peer_conf->enabled = false; + return; } -} - -static size_t protocol_max_packet_size(fastd_context *ctx) { - return (fastd_max_packet_size(ctx) + NONCEBYTES + crypto_secretbox_xsalsa20poly1305_ZEROBYTES - crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); -} -static size_t protocol_min_encrypt_head_space(fastd_context *ctx) { - return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; -} + if (!read_key(key.p, peer_conf->key)) { + pr_warn(ctx, "invalid key configured for `%s', disabling peer", peer_conf->name); + peer_conf->enabled = false; + return; + } -static size_t protocol_min_decrypt_head_space(fastd_context *ctx) { - return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); + peer_conf->protocol_config = malloc(sizeof(fastd_protocol_peer_config)); + peer_conf->protocol_config->public_key = key; } static void init_peer_state(fastd_context *ctx, fastd_peer *peer) { @@ -252,6 +241,38 @@ static inline void free_handshake(protocol_handshake *handshake) { } } +static void protocol_peer_config_purged(fastd_context *ctx, fastd_peer_config *peer_conf) { + fastd_peer *peer; + for (peer = ctx->peers; peer; peer = peer->next) { + if (!peer->protocol_state) + continue; + + if (peer->protocol_state->initiating_handshake && + peer->protocol_state->initiating_handshake->peer_config == peer_conf) { + free_handshake(peer->protocol_state->initiating_handshake); + peer->protocol_state->initiating_handshake = NULL; + } + + if (peer->protocol_state->accepting_handshake && + peer->protocol_state->accepting_handshake->peer_config == peer_conf) { + free_handshake(peer->protocol_state->accepting_handshake); + peer->protocol_state->accepting_handshake = NULL; + } + } +} + +static size_t protocol_max_packet_size(fastd_context *ctx) { + return (fastd_max_packet_size(ctx) + NONCEBYTES + crypto_secretbox_xsalsa20poly1305_ZEROBYTES - crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); +} + +static size_t protocol_min_encrypt_head_space(fastd_context *ctx) { + return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; +} + +static size_t protocol_min_decrypt_head_space(fastd_context *ctx) { + return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); +} + static protocol_handshake* new_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiate) { protocol_handshake **handshake; @@ -825,6 +846,8 @@ const fastd_protocol fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305 = { .name = "ec25519-fhmqvc-xsalsa20-poly1305", .init = protocol_init, + .peer_configure = protocol_peer_configure, + .peer_config_purged = protocol_peer_config_purged, .max_packet_size = protocol_max_packet_size, .min_encrypt_head_space = protocol_min_encrypt_head_space, diff --git a/src/protocol_null.c b/src/protocol_null.c index cc08877..b3c0b17 100644 --- a/src/protocol_null.c +++ b/src/protocol_null.c @@ -34,9 +34,17 @@ #include <arpa/inet.h> -static void protocol_init(fastd_context *ctx, fastd_config *conf) { - if (conf->n_floating > 1) +static fastd_protocol_config* protocol_init(fastd_context *ctx) { + if (ctx->conf->n_floating > 1) exit_error(ctx, "with protocol `null' use can't define more than one floating peer"); + + return NULL; +} + +static void protocol_peer_configure(fastd_context *ctx, fastd_peer_config *peer_conf) { +} + +static void protocol_peer_config_purged(fastd_context *ctx, fastd_peer_config *peer_conf) { } static size_t protocol_max_packet_size(fastd_context *ctx) { @@ -131,6 +139,8 @@ const fastd_protocol fastd_protocol_null = { .name = "null", .init = protocol_init, + .peer_configure = protocol_peer_configure, + .peer_config_purged = protocol_peer_config_purged, .max_packet_size = protocol_max_packet_size, .min_encrypt_head_space = protocol_min_head_space, |