summaryrefslogtreecommitdiffstats
path: root/src/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.c')
-rw-r--r--src/config.c240
1 files changed, 172 insertions, 68 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);
}