Refactor bind address configuration

This commit is contained in:
Matthias Schiffer 2012-11-01 11:04:39 +01:00
parent 921485a5bc
commit 80cbd0c9bd
5 changed files with 172 additions and 188 deletions

View file

@ -224,6 +224,22 @@ bool fastd_config_crypto(fastd_context *ctx, fastd_config *conf, const char *alg
return false; 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) { bool fastd_config_add_log_file(fastd_context *ctx, fastd_config *conf, const char *name, int level) {
char *name2 = strdup(name); char *name2 = strdup(name);
char *name3 = 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) { static void count_peers(fastd_context *ctx, fastd_config *conf) {
conf->n_peers = 0;
conf->n_floating = 0; conf->n_floating = 0;
conf->n_v4 = 0; conf->n_v4 = 0;
conf->n_v6 = 0; conf->n_v6 = 0;
@ -427,6 +444,8 @@ static void count_peers(fastd_context *ctx, fastd_config *conf) {
fastd_peer_config *peer; fastd_peer_config *peer;
for (peer = conf->peers; peer; peer = peer->next) { for (peer = conf->peers; peer; peer = peer->next) {
conf->n_peers++;
switch (peer->address.sa.sa_family) { switch (peer->address.sa.sa_family) {
case AF_UNSPEC: case AF_UNSPEC:
if (peer->hostname) if (peer->hostname)
@ -629,35 +648,29 @@ static void option_bind(fastd_context *ctx, fastd_config *conf, const char *arg)
l = 0; l = 0;
} }
fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address)); fastd_peer_address addr = {};
addr->next = conf->bind_addrs;
conf->bind_addrs = addr;
if (strcmp(addrstr, "any") == 0) { if (strcmp(addrstr, "any") == 0) {
/* nothing to do */ /* nothing to do */
} }
else if (arg[0] == '[') { else if (arg[0] == '[') {
addr->addr.in6.sin6_family = AF_INET6; addr.in6.sin6_family = AF_INET6;
addr->addr.in6.sin6_port = htons(l); 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); exit_error(ctx, "invalid bind address `%s'", addrstr);
} }
else { else {
addr->addr.in.sin_family = AF_INET; addr.in.sin_family = AF_INET;
addr->addr.in.sin_port = htons(l); 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); 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); 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) { static void option_protocol(fastd_context *ctx, fastd_config *conf, const char *arg) {

View file

@ -104,12 +104,12 @@ default { TOKEN(TOK_DEFAULT); }
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} { [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} {
UPDATE_LOCATION; UPDATE_LOCATION;
if (!inet_pton(AF_INET, yytext, &yylval->addr)) { if (!inet_pton(AF_INET, yytext, &yylval->addr4)) {
yylval->error = "invalid address"; yylval->error = "invalid address";
return -1; return -1;
} }
return TOK_ADDR; return TOK_ADDR4;
} }
[;:\{\}] { UPDATE_LOCATION; return yytext[0]; } [;:\{\}] { UPDATE_LOCATION; return yytext[0]; }

View file

@ -45,8 +45,9 @@
fastd_string_stack *str; fastd_string_stack *str;
char *error; char *error;
bool boolean; bool boolean;
struct in_addr addr; struct in_addr addr4;
struct in6_addr addr6; struct in6_addr addr6;
fastd_peer_address addr;
} }
%token START_CONFIG %token START_CONFIG
@ -99,7 +100,7 @@
%token TOK_USE %token TOK_USE
%token TOK_DEFAULT %token TOK_DEFAULT
%token <addr> TOK_ADDR %token <addr4> TOK_ADDR4
%token <addr6> TOK_ADDR6 %token <addr6> TOK_ADDR6
@ -121,6 +122,10 @@
%type <str> maybe_as %type <str> maybe_as
%type <num> maybe_af %type <num> maybe_af
%type <boolean> maybe_float %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 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); } interface: TOK_STRING { free(conf->ifname); conf->ifname = strdup($1->str); }
; ;
bind_new: { bind: bind_address maybe_bind_interface maybe_bind_default {
fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address)); fastd_config_bind_address(ctx, conf, &$1, $2 ? $2->str : NULL, $3 == AF_UNSPEC || $3 == AF_INET, $3 == AF_UNSPEC || $3 == AF_INET6);
addr->next = conf->bind_addrs;
conf->bind_addrs = addr;
} }
; ;
bind: bind_new TOK_ADDR maybe_port maybe_bind_to_device maybe_bind_default { bind_address:
conf->bind_addrs->addr.in.sin_family = AF_INET; TOK_ADDR4 maybe_port {
conf->bind_addrs->addr.in.sin_addr = $2; $$ = (fastd_peer_address){ .in = { .sin_family = AF_INET, .sin_addr = $1, .sin_port = htons($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_new TOK_ADDR6 maybe_port maybe_bind_to_device maybe_bind_default { | TOK_ADDR6 maybe_port {
conf->bind_addrs->addr.in6.sin6_family = AF_INET6; $$ = (fastd_peer_address){ .in6 = { .sin6_family = AF_INET6, .sin6_addr = $1, .sin6_port = htons($2) } };
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;
} }
| bind_new TOK_ANY maybe_port maybe_bind_to_device maybe_bind_default { | TOK_ANY maybe_port {
conf->bind_addrs->addr.in.sin_port = htons($3); $$ = (fastd_peer_address){ .in = { .sin_family = AF_UNSPEC, .sin_port = htons($2) } };
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;
} }
; ;
maybe_bind_to_device: maybe_bind_interface:
TOK_INTERFACE TOK_STRING { TOK_INTERFACE TOK_STRING {
conf->bind_addrs->bindtodev = strdup($2->str); $$ = $2;
}
| {
$$ = NULL;
} }
|
; ;
maybe_bind_default: maybe_bind_default:
TOK_DEFAULT bind_default TOK_DEFAULT bind_default {
| $$ = $2;
}
| {
$$ = -1;
}
; ;
bind_default: bind_default:
TOK_IPV4 { TOK_IPV4 {
if (conf->bind_addrs->addr.sa.sa_family == AF_INET6) { $$ = AF_INET;
fastd_config_error(&@$, ctx, conf, filename, depth, "tried to set IPv6 bind as IPv4 default");
YYERROR;
}
conf->bind_addr_default_v4 = conf->bind_addrs;
} }
| TOK_IPV6 { | TOK_IPV6 {
if (conf->bind_addrs->addr.sa.sa_family == AF_INET) { $$ = AF_INET6;
fastd_config_error(&@$, ctx, conf, filename, depth, "tried to set IPv4 bind as IPv6 default");
YYERROR;
}
conf->bind_addr_default_v6 = conf->bind_addrs;
} }
| { | {
if (conf->bind_addrs->addr.sa.sa_family != AF_INET6) $$ = AF_UNSPEC;
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;
} }
; ;
@ -340,7 +325,7 @@ peer_statement: TOK_REMOTE peer_remote ';'
| TOK_INCLUDE peer_include ';' | TOK_INCLUDE peer_include ';'
; ;
peer_remote: TOK_ADDR port { peer_remote: TOK_ADDR4 port {
free(conf->peers->hostname); free(conf->peers->hostname);
conf->peers->hostname = NULL; conf->peers->hostname = NULL;

View file

@ -147,48 +147,38 @@ static void crypto_free(fastd_context *ctx) {
} }
static unsigned max_sockets(const fastd_config *conf) { static void init_sockets(fastd_context *ctx) {
unsigned n = 0; ctx->socks = malloc(ctx->conf->n_bind_addrs * sizeof(fastd_socket));
fastd_bind_address *addr;
for (addr = conf->bind_addrs; addr; addr = addr->next) unsigned i;
n++; 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};
return n; if (addr == ctx->conf->bind_addr_default_v4)
ctx->sock_default_v4 = &ctx->socks[i];
if (addr == ctx->conf->bind_addr_default_v6)
ctx->sock_default_v6 = &ctx->socks[i];
addr = addr->next;
} }
static void init_sockets(fastd_context *ctx) { ctx->n_socks = ctx->conf->n_bind_addrs;
static const fastd_bind_address bind_any = {}; }
const fastd_bind_address *addr = ctx->conf->bind_addrs; static int bind_socket(fastd_context *ctx, const fastd_bind_address *addr, bool warn) {
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 n_v4 = 0, n_v6 = 0;
bool info_ipv6 = true;
ctx->socks = calloc(max_sockets(ctx->conf), sizeof(fastd_socket));
while (addr) {
int fd = -1; int fd = -1;
int af = AF_UNSPEC; int af = AF_UNSPEC;
if (addr->addr.sa.sa_family != AF_INET) { if (addr->addr.sa.sa_family != AF_INET) {
fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) { 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; af = AF_INET6;
int val = (addr->addr.sa.sa_family == AF_INET6); int val = (addr->addr.sa.sa_family == AF_INET6);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
if (warn)
pr_warn_errno(ctx, "setsockopt"); pr_warn_errno(ctx, "setsockopt");
goto error; goto error;
} }
@ -207,6 +197,7 @@ static void init_sockets(fastd_context *ctx) {
if (addr->bindtodev) { if (addr->bindtodev) {
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(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"); pr_warn_errno(ctx, "setsockopt: unable to bind to device");
goto error; goto error;
} }
@ -225,36 +216,12 @@ static void init_sockets(fastd_context *ctx) {
} }
if (bind(fd, (struct sockaddr*)&bind_address, af == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { if (bind(fd, (struct sockaddr*)&bind_address, af == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
if (warn)
pr_warn(ctx, "bind"); pr_warn(ctx, "bind");
goto error; goto error;
} }
ctx->socks[ctx->n_socks] = (fastd_socket){fd, addr}; return fd;
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];
n_v4++;
}
}
else {
if (addr == default_v4)
ctx->sock_default_v4 = &ctx->socks[ctx->n_socks];
n_v4++;
}
ctx->n_socks++;
addr = addr->next;
continue;
error: error:
if (fd >= 0) { if (fd >= 0) {
@ -262,25 +229,32 @@ static void init_sockets(fastd_context *ctx) {
pr_error_errno(ctx, "close"); pr_error_errno(ctx, "close");
} }
if (warn) {
if (addr->bindtodev) if (addr->bindtodev)
pr_warn(ctx, "unable to bind to %I on `%s'", &addr->addr, 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); 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");
addr = addr->next;
} }
if (!ctx->n_socks) return -1;
exit_error(ctx, "all bind attempts failed"); }
if (!n_v4 && ctx->conf->n_v4) static void bind_sockets(fastd_context *ctx) {
pr_warn(ctx, "there are IPv4 peers defined, but there was no successful IPv4 bind"); unsigned i;
if (!n_v6 && ctx->conf->n_v6) for (i = 0; i < ctx->n_socks; i++) {
pr_warn(ctx, "there are IPv6 peers defined, but there was no successful IPv4 bind"); if (ctx->socks[i].fd >= 0)
continue;
ctx->socks[i].fd = bind_socket(ctx, ctx->socks[i].addr, ctx->socks[i].fd < -1);
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) { 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); 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; ifr.ifr_mtu = ctx->conf->mtu;
if (ioctl(ctl_sock, SIOCSIFMTU, &ifr) < 0) if (ioctl(ctl_sock, SIOCSIFMTU, &ifr) < 0)
exit_errno(ctx, "SIOCSIFMTU ioctl failed"); exit_errno(ctx, "SIOCSIFMTU ioctl failed");
if (close(ctl_sock))
pr_error_errno(ctx, "close");
pr_debug(ctx, "tun/tap device initialized."); pr_debug(ctx, "tun/tap device initialized.");
} }
@ -924,6 +903,8 @@ int main(int argc, char *argv[]) {
pr_info(&ctx, "fastd " FASTD_VERSION " starting"); pr_info(&ctx, "fastd " FASTD_VERSION " starting");
init_sockets(&ctx); init_sockets(&ctx);
bind_sockets(&ctx);
init_tuntap(&ctx); init_tuntap(&ctx);
init_peers(&ctx); init_peers(&ctx);
@ -949,6 +930,8 @@ int main(int argc, char *argv[]) {
while (!terminate) { while (!terminate) {
handle_tasks(&ctx); handle_tasks(&ctx);
bind_sockets(&ctx);
handle_input(&ctx); handle_input(&ctx);
maintenance(&ctx); maintenance(&ctx);

View file

@ -160,6 +160,7 @@ struct _fastd_config {
char *ifname; char *ifname;
unsigned n_bind_addrs;
fastd_bind_address *bind_addrs; fastd_bind_address *bind_addrs;
fastd_bind_address *bind_addr_default_v4; fastd_bind_address *bind_addr_default_v4;
@ -187,6 +188,7 @@ struct _fastd_config {
fastd_string_stack *peer_dirs; fastd_string_stack *peer_dirs;
fastd_peer_config *peers; fastd_peer_config *peers;
unsigned n_peers;
unsigned n_floating; unsigned n_floating;
unsigned n_v4; unsigned n_v4;
unsigned n_v6; 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_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_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); 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_configure(fastd_context *ctx, fastd_config *conf, int argc, char *const argv[]);
void fastd_reconfigure(fastd_context *ctx, fastd_config *conf); void fastd_reconfigure(fastd_context *ctx, fastd_config *conf);
void fastd_config_release(fastd_context *ctx, fastd_config *conf); void fastd_config_release(fastd_context *ctx, fastd_config *conf);