diff options
-rw-r--r-- | src/config.c | 43 | ||||
-rw-r--r-- | src/config.l | 4 | ||||
-rw-r--r-- | src/config.y | 77 | ||||
-rw-r--r-- | src/fastd.c | 191 | ||||
-rw-r--r-- | src/fastd.h | 3 |
5 files changed, 151 insertions, 167 deletions
diff --git a/src/config.c b/src/config.c index be67e2b..b0f4754 100644 --- a/src/config.c +++ b/src/config.c @@ -224,6 +224,22 @@ bool fastd_config_crypto(fastd_context *ctx, fastd_config *conf, const char *alg return false; } +void fastd_config_bind_address(fastd_context *ctx, fastd_config *conf, const fastd_peer_address *address, const char *bindtodev, bool default_v4, bool default_v6) { + fastd_bind_address *addr = malloc(sizeof(fastd_bind_address)); + addr->next = conf->bind_addrs; + conf->bind_addrs = addr; + conf->n_bind_addrs++; + + addr->addr = *address; + addr->bindtodev = bindtodev ? strdup(bindtodev) : NULL; + + if (address->sa.sa_family != AF_INET6 && (default_v4 || !conf->bind_addr_default_v4)) + conf->bind_addr_default_v4 = addr; + + if (address->sa.sa_family != AF_INET && (default_v6 || !conf->bind_addr_default_v6)) + conf->bind_addr_default_v6 = addr; +} + bool fastd_config_add_log_file(fastd_context *ctx, fastd_config *conf, const char *name, int level) { char *name2 = strdup(name); char *name3 = strdup(name); @@ -418,6 +434,7 @@ bool fastd_read_config(fastd_context *ctx, fastd_config *conf, const char *filen } static void count_peers(fastd_context *ctx, fastd_config *conf) { + conf->n_peers = 0; conf->n_floating = 0; conf->n_v4 = 0; conf->n_v6 = 0; @@ -427,6 +444,8 @@ static void count_peers(fastd_context *ctx, fastd_config *conf) { fastd_peer_config *peer; for (peer = conf->peers; peer; peer = peer->next) { + conf->n_peers++; + switch (peer->address.sa.sa_family) { case AF_UNSPEC: if (peer->hostname) @@ -629,35 +648,29 @@ static void option_bind(fastd_context *ctx, fastd_config *conf, const char *arg) l = 0; } - fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address)); - addr->next = conf->bind_addrs; - conf->bind_addrs = addr; + fastd_peer_address addr = {}; if (strcmp(addrstr, "any") == 0) { /* nothing to do */ } else if (arg[0] == '[') { - addr->addr.in6.sin6_family = AF_INET6; - addr->addr.in6.sin6_port = htons(l); + addr.in6.sin6_family = AF_INET6; + addr.in6.sin6_port = htons(l); - if (inet_pton(AF_INET6, addrstr, &addr->addr.in6.sin6_addr) != 1) + if (inet_pton(AF_INET6, addrstr, &addr.in6.sin6_addr) != 1) exit_error(ctx, "invalid bind address `%s'", addrstr); } else { - addr->addr.in.sin_family = AF_INET; - addr->addr.in.sin_port = htons(l); + addr.in.sin_family = AF_INET; + addr.in.sin_port = htons(l); - if (inet_pton(AF_INET, addrstr, &addr->addr.in.sin_addr) != 1) + if (inet_pton(AF_INET, addrstr, &addr.in.sin_addr) != 1) exit_error(ctx, "invalid bind address `%s'", addrstr); } - if (!conf->bind_addr_default_v4 && addr->addr.sa.sa_family != AF_INET6) - conf->bind_addr_default_v4 = addr; - - if (!conf->bind_addr_default_v6 && addr->addr.sa.sa_family != AF_INET) - conf->bind_addr_default_v6 = addr; - free(addrstr); + + fastd_config_bind_address(ctx, conf, &addr, NULL, false, false); } static void option_protocol(fastd_context *ctx, fastd_config *conf, const char *arg) { diff --git a/src/config.l b/src/config.l index 404384e..9e82239 100644 --- a/src/config.l +++ b/src/config.l @@ -104,12 +104,12 @@ default { TOKEN(TOK_DEFAULT); } [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} { UPDATE_LOCATION; - if (!inet_pton(AF_INET, yytext, &yylval->addr)) { + if (!inet_pton(AF_INET, yytext, &yylval->addr4)) { yylval->error = "invalid address"; return -1; } - return TOK_ADDR; + return TOK_ADDR4; } [;:\{\}] { UPDATE_LOCATION; return yytext[0]; } diff --git a/src/config.y b/src/config.y index 59fc296..5bf374e 100644 --- a/src/config.y +++ b/src/config.y @@ -45,8 +45,9 @@ fastd_string_stack *str; char *error; bool boolean; - struct in_addr addr; + struct in_addr addr4; struct in6_addr addr6; + fastd_peer_address addr; } %token START_CONFIG @@ -99,7 +100,7 @@ %token TOK_USE %token TOK_DEFAULT -%token <addr> TOK_ADDR +%token <addr4> TOK_ADDR4 %token <addr6> TOK_ADDR6 @@ -121,6 +122,10 @@ %type <str> maybe_as %type <num> maybe_af %type <boolean> maybe_float +%type <addr> bind_address +%type <str> maybe_bind_interface +%type <num> maybe_bind_default +%type <num> bind_default %% start: START_CONFIG config @@ -188,70 +193,50 @@ log_level: TOK_FATAL { $$ = LOG_CRIT; } interface: TOK_STRING { free(conf->ifname); conf->ifname = strdup($1->str); } ; -bind_new: { - fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address)); - addr->next = conf->bind_addrs; - conf->bind_addrs = addr; +bind: bind_address maybe_bind_interface maybe_bind_default { + fastd_config_bind_address(ctx, conf, &$1, $2 ? $2->str : NULL, $3 == AF_UNSPEC || $3 == AF_INET, $3 == AF_UNSPEC || $3 == AF_INET6); } ; -bind: bind_new TOK_ADDR maybe_port maybe_bind_to_device maybe_bind_default { - conf->bind_addrs->addr.in.sin_family = AF_INET; - conf->bind_addrs->addr.in.sin_addr = $2; - conf->bind_addrs->addr.in.sin_port = htons($3); - if (!conf->bind_addr_default_v4) - conf->bind_addr_default_v4 = conf->bind_addrs; +bind_address: + TOK_ADDR4 maybe_port { + $$ = (fastd_peer_address){ .in = { .sin_family = AF_INET, .sin_addr = $1, .sin_port = htons($2) } }; } - | bind_new TOK_ADDR6 maybe_port maybe_bind_to_device maybe_bind_default { - conf->bind_addrs->addr.in6.sin6_family = AF_INET6; - conf->bind_addrs->addr.in6.sin6_addr = $2; - conf->bind_addrs->addr.in6.sin6_port = htons($3); - if (!conf->bind_addr_default_v6) - conf->bind_addr_default_v6 = conf->bind_addrs; + | TOK_ADDR6 maybe_port { + $$ = (fastd_peer_address){ .in6 = { .sin6_family = AF_INET6, .sin6_addr = $1, .sin6_port = htons($2) } }; } - | bind_new TOK_ANY maybe_port maybe_bind_to_device maybe_bind_default { - conf->bind_addrs->addr.in.sin_port = htons($3); - if (!conf->bind_addr_default_v4) - conf->bind_addr_default_v4 = conf->bind_addrs; - if (!conf->bind_addr_default_v6) - conf->bind_addr_default_v6 = conf->bind_addrs; + | TOK_ANY maybe_port { + $$ = (fastd_peer_address){ .in = { .sin_family = AF_UNSPEC, .sin_port = htons($2) } }; } ; -maybe_bind_to_device: +maybe_bind_interface: TOK_INTERFACE TOK_STRING { - conf->bind_addrs->bindtodev = strdup($2->str); + $$ = $2; + } + | { + $$ = NULL; } - | ; maybe_bind_default: - TOK_DEFAULT bind_default - | + TOK_DEFAULT bind_default { + $$ = $2; + } + | { + $$ = -1; + } ; bind_default: TOK_IPV4 { - if (conf->bind_addrs->addr.sa.sa_family == AF_INET6) { - fastd_config_error(&@$, ctx, conf, filename, depth, "tried to set IPv6 bind as IPv4 default"); - YYERROR; - } - - conf->bind_addr_default_v4 = conf->bind_addrs; + $$ = AF_INET; } | TOK_IPV6 { - if (conf->bind_addrs->addr.sa.sa_family == AF_INET) { - fastd_config_error(&@$, ctx, conf, filename, depth, "tried to set IPv4 bind as IPv6 default"); - YYERROR; - } - - conf->bind_addr_default_v6 = conf->bind_addrs; + $$ = AF_INET6; } | { - if (conf->bind_addrs->addr.sa.sa_family != AF_INET6) - conf->bind_addr_default_v4 = conf->bind_addrs; - if (conf->bind_addrs->addr.sa.sa_family != AF_INET) - conf->bind_addr_default_v6 = conf->bind_addrs; + $$ = AF_UNSPEC; } ; @@ -340,7 +325,7 @@ peer_statement: TOK_REMOTE peer_remote ';' | TOK_INCLUDE peer_include ';' ; -peer_remote: TOK_ADDR port { +peer_remote: TOK_ADDR4 port { free(conf->peers->hostname); conf->peers->hostname = NULL; diff --git a/src/fastd.c b/src/fastd.c index 88e7401..a117b1e 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -147,140 +147,114 @@ static void crypto_free(fastd_context *ctx) { } -static unsigned max_sockets(const fastd_config *conf) { - unsigned n = 0; - fastd_bind_address *addr; - - for (addr = conf->bind_addrs; addr; addr = addr->next) - n++; - - return n; -} - static void init_sockets(fastd_context *ctx) { - static const fastd_bind_address bind_any = {}; + ctx->socks = malloc(ctx->conf->n_bind_addrs * sizeof(fastd_socket)); - const fastd_bind_address *addr = ctx->conf->bind_addrs; - const fastd_bind_address *default_v4 = ctx->conf->bind_addr_default_v4; - const fastd_bind_address *default_v6 = ctx->conf->bind_addr_default_v6; - - if (!addr) - addr = default_v4 = default_v6 = &bind_any; + unsigned i; + fastd_bind_address *addr = ctx->conf->bind_addrs; + for (i = 0; i < ctx->conf->n_bind_addrs; i++) { + ctx->socks[i] = (fastd_socket){-2, addr}; - unsigned n_v4 = 0, n_v6 = 0; - bool info_ipv6 = true; + if (addr == ctx->conf->bind_addr_default_v4) + ctx->sock_default_v4 = &ctx->socks[i]; - ctx->socks = calloc(max_sockets(ctx->conf), sizeof(fastd_socket)); + if (addr == ctx->conf->bind_addr_default_v6) + ctx->sock_default_v6 = &ctx->socks[i]; - while (addr) { - int fd = -1; - int af = AF_UNSPEC; + addr = addr->next; + } - if (addr->addr.sa.sa_family != AF_INET) { - fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - if (info_ipv6) { - pr_warn(ctx, "there seems to be no IPv6 support; explicitely bind to an IPv4 address (or 0.0.0.0) to disable this warning"); - info_ipv6 = false; - } - } - else { - af = AF_INET6; + ctx->n_socks = ctx->conf->n_bind_addrs; +} - int val = (addr->addr.sa.sa_family == AF_INET6); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { - pr_warn_errno(ctx, "setsockopt"); - goto error; - } - } - } - if (fd < 0 && addr->addr.sa.sa_family != AF_INET6) { - fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) - exit_errno(ctx, "unable to create socket"); - else - af = AF_INET; - } +static int bind_socket(fastd_context *ctx, const fastd_bind_address *addr, bool warn) { + int fd = -1; + int af = AF_UNSPEC; - if (fd < 0) - goto error; + if (addr->addr.sa.sa_family != AF_INET) { + fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (fd >= 0) { + af = AF_INET6; - if (addr->bindtodev) { - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(addr->bindtodev))) { - pr_warn_errno(ctx, "setsockopt: unable to bind to device"); + int val = (addr->addr.sa.sa_family == AF_INET6); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { + if (warn) + pr_warn_errno(ctx, "setsockopt"); goto error; } } + } + if (fd < 0 && addr->addr.sa.sa_family != AF_INET6) { + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) + exit_errno(ctx, "unable to create socket"); + else + af = AF_INET; + } - fastd_peer_address bind_address = addr->addr; - - if (bind_address.sa.sa_family == AF_UNSPEC) { - memset(&bind_address, 0, sizeof(bind_address)); - bind_address.sa.sa_family = af; - - if (af == AF_INET6) - bind_address.in6.sin6_port = addr->addr.in.sin_port; - else - bind_address.in.sin_port = addr->addr.in.sin_port; - } + if (fd < 0) + goto error; - if (bind(fd, (struct sockaddr*)&bind_address, af == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - pr_warn(ctx, "bind"); + if (addr->bindtodev) { + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(addr->bindtodev))) { + if (warn) + pr_warn_errno(ctx, "setsockopt: unable to bind to device"); goto error; } + } - ctx->socks[ctx->n_socks] = (fastd_socket){fd, addr}; - - if (af == AF_INET6) { - if (addr == default_v6) - ctx->sock_default_v6 = &ctx->socks[ctx->n_socks]; - - n_v6++; - - if (addr->addr.sa.sa_family == AF_UNSPEC) { - if (addr == default_v4) - ctx->sock_default_v4 = &ctx->socks[ctx->n_socks]; + fastd_peer_address bind_address = addr->addr; - n_v4++; - } - } - else { - if (addr == default_v4) - ctx->sock_default_v4 = &ctx->socks[ctx->n_socks]; + if (bind_address.sa.sa_family == AF_UNSPEC) { + memset(&bind_address, 0, sizeof(bind_address)); + bind_address.sa.sa_family = af; - n_v4++; - } + if (af == AF_INET6) + bind_address.in6.sin6_port = addr->addr.in.sin_port; + else + bind_address.in.sin_port = addr->addr.in.sin_port; + } - ctx->n_socks++; + if (bind(fd, (struct sockaddr*)&bind_address, af == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { + if (warn) + pr_warn(ctx, "bind"); + goto error; + } - addr = addr->next; - continue; + return fd; - error: - if (fd >= 0) { - if (close(fd)) - pr_error_errno(ctx, "close"); - } + error: + if (fd >= 0) { + if (close(fd)) + pr_error_errno(ctx, "close"); + } + if (warn) { if (addr->bindtodev) pr_warn(ctx, "unable to bind to %I on `%s'", &addr->addr, addr->bindtodev); - else + else pr_warn(ctx, "unable to bind to %I", &addr->addr); + } - if (addr == default_v4 || addr == default_v6) - exit_error(ctx, "unable to bind to default address"); + return -1; +} - addr = addr->next; - } +static void bind_sockets(fastd_context *ctx) { + unsigned i; - if (!ctx->n_socks) - exit_error(ctx, "all bind attempts failed"); + for (i = 0; i < ctx->n_socks; i++) { + if (ctx->socks[i].fd >= 0) + continue; - if (!n_v4 && ctx->conf->n_v4) - pr_warn(ctx, "there are IPv4 peers defined, but there was no successful IPv4 bind"); + ctx->socks[i].fd = bind_socket(ctx, ctx->socks[i].addr, ctx->socks[i].fd < -1); - if (!n_v6 && ctx->conf->n_v6) - pr_warn(ctx, "there are IPv6 peers defined, but there was no successful IPv4 bind"); + if (ctx->socks[i].fd >= 0) { + if (ctx->socks[i].addr->bindtodev) + pr_info(ctx, "successfully bound to %I on `%s'", &ctx->socks[i].addr->addr, ctx->socks[i].addr->bindtodev); + else + pr_info(ctx, "successfully bound to %I", &ctx->socks[i].addr->addr); + } + } } static void init_tuntap(fastd_context *ctx) { @@ -315,12 +289,17 @@ static void init_tuntap(fastd_context *ctx) { ctx->ifname = strndup(ifr.ifr_name, IFNAMSIZ); - int ctl_sock = ctx->socks[0].fd; + int ctl_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (ctl_sock < 0) + exit_errno(ctx, "socket"); ifr.ifr_mtu = ctx->conf->mtu; if (ioctl(ctl_sock, SIOCSIFMTU, &ifr) < 0) exit_errno(ctx, "SIOCSIFMTU ioctl failed"); + if (close(ctl_sock)) + pr_error_errno(ctx, "close"); + pr_debug(ctx, "tun/tap device initialized."); } @@ -924,6 +903,8 @@ int main(int argc, char *argv[]) { pr_info(&ctx, "fastd " FASTD_VERSION " starting"); init_sockets(&ctx); + bind_sockets(&ctx); + init_tuntap(&ctx); init_peers(&ctx); @@ -949,6 +930,8 @@ int main(int argc, char *argv[]) { while (!terminate) { handle_tasks(&ctx); + + bind_sockets(&ctx); handle_input(&ctx); maintenance(&ctx); diff --git a/src/fastd.h b/src/fastd.h index cf5aaf1..9f84ae7 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -160,6 +160,7 @@ struct _fastd_config { char *ifname; + unsigned n_bind_addrs; fastd_bind_address *bind_addrs; fastd_bind_address *bind_addr_default_v4; @@ -187,6 +188,7 @@ struct _fastd_config { fastd_string_stack *peer_dirs; fastd_peer_config *peers; + unsigned n_peers; unsigned n_floating; unsigned n_v4; unsigned n_v6; @@ -277,6 +279,7 @@ bool fastd_config_protocol(fastd_context *ctx, fastd_config *conf, const char *n bool fastd_config_method(fastd_context *ctx, fastd_config *conf, const char *name); bool fastd_config_crypto(fastd_context *ctx, fastd_config *conf, const char *alg, const char *impl); bool fastd_config_add_log_file(fastd_context *ctx, fastd_config *conf, const char *name, int level); +void fastd_config_bind_address(fastd_context *ctx, fastd_config *conf, const fastd_peer_address *address, const char *bindtodev, bool default_v4, bool default_v6); 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_config_release(fastd_context *ctx, fastd_config *conf); |