diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | pingu.c | 17 | ||||
-rw-r--r-- | pingu.conf | 44 | ||||
-rw-r--r-- | pingu_conf.c | 208 | ||||
-rw-r--r-- | pingu_conf.h | 6 | ||||
-rw-r--r-- | pingu_host.c | 220 | ||||
-rw-r--r-- | pingu_host.h | 15 | ||||
-rw-r--r-- | pingu_iface.c | 50 | ||||
-rw-r--r-- | pingu_iface.h | 7 |
9 files changed, 323 insertions, 245 deletions
@@ -18,6 +18,7 @@ pingu_OBJS = \ log.o \ pingu.o \ pingu_burst.o \ + pingu_conf.o \ pingu_host.o \ pingu_iface.o \ pingu_netlink.o \ @@ -12,13 +12,11 @@ #include <ev.h> -#include "icmp.h" -#include "pingu.h" -#include "xlib.h" #include "log.h" -#include "list.h" +#include "pingu_conf.h" #include "pingu_host.h" +#include "pingu_iface.h" #include "pingu_netlink.h" #ifndef DEFAULT_CONFIG @@ -146,7 +144,16 @@ int main(int argc, char *argv[]) log_init(verbose); loop = ev_default_loop(0); - pingu_host_init(loop, config_file); + + if (pingu_conf_read(config_file) < 0) + return 1; + + if (pingu_iface_init(loop) < 0) + return 1; + + if (pingu_host_init(loop) < 0) + return 1; + kernel_init(loop); if (pingu_daemonize) { @@ -1,30 +1,30 @@ # comments are prefixed with # -# global option -interval 10 -retry 3 +# global options. Those can bi overidden in the host definition +interval 30 +retry 5 +required 3 timeout 1.0 -# route-script will be executed every time anything changes status. -# All gateways that are "up" are sent as parameter. -route-script /etc/pingu/route-script + +# interface definitions +interface eth1 { + # define a route table to use. If unset it will be picked auto. + # Setting this to 0 means do not manage routes for this interface + # route-table 1 +} + +interface dummy0 { +} # host to ping -host 10.65.67.1 -name ISP-1 -# bind to interface -interface eth1 -# set the source ip -source-ip 10.65.67.12 -timeout 0.3 -retry 2 -up-action echo "isp 1 went up" -down-action echo "iso 1 went down" +host 10.65.0.1 { + label ISP1 + bind-interface eth1 +} -#host 10.2.0.1 -#name ISP-2 -#interface eth0 -#source-ip 10.2.0.3 -#up-action /etc/pingu/isp2 up -#down-action /etc/pingu/isp2 down +host 192.168.1.5 { + label Dummy Local + bind-interface dummy0 +} diff --git a/pingu_conf.c b/pingu_conf.c new file mode 100644 index 0000000..be92483 --- /dev/null +++ b/pingu_conf.c @@ -0,0 +1,208 @@ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "log.h" +#include "pingu_iface.h" +#include "pingu_host.h" +#include "xlib.h" + +static float default_burst_interval = 30.0; +static float default_timeout = 1.0; +static int default_max_retries = 5; +static int default_required_replies = 3; +static char *default_up_action = NULL; +static char *default_down_action = NULL; + +/* note: this overwrite the line buffer */ +static void parse_line(char *line, char **key, char **value) +{ + char *p; + + (*value) = NULL; + (*key) = NULL; + + /* strip comments and trailng \n */ + p = strpbrk(line, "#\n"); + if (p) + *p = '\0'; + + /* skip leading whitespace */ + while (isspace(*line)) + line++; + if (*line == '\0') + return; + + (*key) = line; + + /* find space between keyword and value */ + p = line; + while (!isspace(*p)) { + if (*p == '\0') + return; + p++; + } + *p++ = '\0'; + + /* find value */ + while (isspace(*p)) + p++; + if (*p == '\0') + return; + + (*value) = p; +} + +static FILE *pingu_conf_open(const char *filename) +{ + FILE *f = fopen(filename, "r"); + if (f == NULL) + log_perror(filename); + return f; +} + +static char *chomp_bracket(char *str) +{ + char *p = str; + /* chomp */ + while (!isspace(*p)) + p++; + *p = '\0'; + return str; +} + +static char *pingu_conf_get_key_value(FILE *f, char **key, char **value, int *lineno) +{ + static char line[1024]; + char *k = NULL, *v = NULL; + while (k == NULL || *k == '\0') { + if (fgets(line, sizeof(line), f) == NULL) + return NULL; + (*lineno)++; + parse_line(line, &k, &v); + } + *key = k; + *value = v; + return line; +} + + + +static int pingu_conf_read_iface(FILE *f, char *ifname, int *lineno) +{ + struct pingu_iface *iface; + char *key, *value; + + iface = pingu_iface_get_by_name(value); + if (iface != NULL) { + log_error("Interface %s already declared"); + return -1; + } + + iface = pingu_iface_get_by_name_or_new(ifname); + if (iface == NULL) + return -1; + + while (pingu_conf_get_key_value(f, &key, &value, lineno)) { + if (key == NULL || key[0] == '}') + break; + if (strcmp(key, "route-table") == 0) { + pingu_iface_set_route_table(iface, atoi(value)); + } else { + log_error("Unknown keyword '%s' on line %i", key, + lineno); + } + } + return 0; +} + +static int pingu_conf_read_host(FILE *f, char *hoststr, int *lineno) +{ + char *key, *value; + struct pingu_host *host; + + host = pingu_host_new(xstrdup(hoststr), default_burst_interval, + default_max_retries, default_required_replies, + default_timeout, default_up_action, + default_down_action); + while (pingu_conf_get_key_value(f, &key, &value, lineno)) { + if (key == NULL || key[0] == '}') + break; + if (strcmp(key, "bind-interface") == 0) { + host->iface = pingu_iface_get_by_name_or_new(value); + if (host->iface == NULL) { + log_error("Undefined interface %s on line %i", + value, lineno); + return -1; + } + } else if (strcmp(key, "label") == 0) { + host->label = xstrdup(value); + } else if (strcmp(key, "up-action") == 0) { + host->up_action = xstrdup(value); + } else if (strcmp(key, "down-action") == 0) { + host->down_action = xstrdup(value); + } else if (strcmp(key, "retry") == 0) { + host->max_retries = atoi(value); + } else if (strcmp(key, "required") == 0) { + host->required_replies = atoi(value); + } else if (strcmp(key, "timeout") == 0) { + host->timeout = atof(value); + } else if (strcmp(key, "interval") == 0) { + host->burst_interval = atof(value); + } else { + log_error("Unknown keyword '%s' on line %i", key, + lineno); + } + } + if (host->iface == NULL) + host->iface = pingu_iface_get_by_name_or_new(NULL); + return 0; +} + +int pingu_conf_read(const char *filename) +{ + int lineno = 0; + char line[256]; + int r = 0; + FILE *f = pingu_conf_open(filename); + + if (f == NULL) + return -1; + + while (fgets(line, sizeof(line), f)) { + char *key, *value; + lineno++; + parse_line(line, &key, &value); + if (key == NULL) + continue; + if (strcmp(key, "interface") == 0) { + r = pingu_conf_read_iface(f, chomp_bracket(value), &lineno); + if (r < 0) + break; + } else if (strcmp(key, "host") == 0) { + r = pingu_conf_read_host(f, chomp_bracket(value), &lineno); + if (r < 0) + break; + } else if (strcmp(key, "interval") == 0) { + default_burst_interval = atof(value); + } else if (strcmp(key, "retry") == 0) { + default_max_retries = atoi(value); + } else if (strcmp(key, "required") == 0) { + default_required_replies = atoi(value); + } else if (strcmp(key, "timeout") == 0) { + default_timeout = atof(value); + } else if (strcmp(key, "up-action") == 0) { + default_up_action = xstrdup(value); + } else if (strcmp(key, "down-action") == 0) { + default_down_action = xstrdup(value); + } else { + log_error("Unknown keyword '%s' on line %i", key, + lineno); + r = -1; + break; + } + } + return r; +} diff --git a/pingu_conf.h b/pingu_conf.h new file mode 100644 index 0000000..8962475 --- /dev/null +++ b/pingu_conf.h @@ -0,0 +1,6 @@ +#ifndef PINGU_CONF_H +#define PINGU_CONF_H + +int pingu_conf_read(const char *filename); + +#endif diff --git a/pingu_host.c b/pingu_host.c index efc3f43..1f1b534 100644 --- a/pingu_host.c +++ b/pingu_host.c @@ -2,7 +2,6 @@ #include <arpa/inet.h> #include <linux/rtnetlink.h> -#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -18,188 +17,6 @@ #include "xlib.h" static struct list_head host_list = LIST_INITIALIZER(host_list); -float default_burst_interval = 30.0; -float default_timeout = 1.0; -int default_max_retries = 5; -int default_required_replies = 2; -char *default_up_action = NULL; -char *default_down_action = NULL; -char *default_route_script = NULL; -int default_route_table = 10; - -/* note: this overwrite the line buffer */ -static void parse_line(char *line, char **key, char **value) -{ - char *p; - - /* strip comments and trailng \n */ - p = strpbrk(line, "#\n"); - if (p) - *p = '\0'; - - (*value) = NULL; - if (line[0] == '\0') { - (*key) = NULL; - return; - } - - /* skip leading whitespace */ - while (isspace(*p)) { - if (*p == '\0') - return; - p++; - } - (*key) = line; - - /* find space between keyword and value */ - p = line; - while (!isspace(*p)) { - if (*p == '\0') - return; - p++; - } - *p++ = '\0'; - - /* find value */ - while (isspace(*p)) { - if (*p == '\0') - return; - p++; - } - (*value) = p; -} - -int pingu_host_read_config(const char *file) -{ - FILE *f = fopen(file, "r"); - struct pingu_host *p = NULL; - int lineno = 0; - char line[256]; - if (f == NULL) { - log_perror(file); - return -1; - } - while (fgets(line, sizeof(line), f)) { - char *key, *value; - lineno++; - parse_line(line, &key, &value); - if (key == NULL) - continue; - - if (strcmp(key, "host") == 0) { - p = xmalloc(sizeof(struct pingu_host)); - memset(p, 0, sizeof(struct pingu_host)); - p->host = xstrdup(value); - p->gateway = xstrdup(value); - p->status = 1; /* online by default */ - p->max_retries = default_max_retries; - p->timeout = default_timeout; - p->up_action = default_up_action; - p->down_action = default_down_action; - p->required_replies = default_required_replies; - p->burst_interval = default_burst_interval; - list_add(&p->host_list_entry, &host_list); - continue; - } - if (p == NULL) { - if (strcmp(key, "interval") == 0) { - default_burst_interval = atof(value); - } else if (strcmp(key, "retry") == 0) { - default_max_retries = atoi(value); - } else if (strcmp(key, "required") == 0) { - default_required_replies = atoi(value); - } else if (strcmp(key, "timeout") == 0) { - default_timeout = atof(value); - } else if (strcmp(key, "up-action") == 0) { - default_up_action = xstrdup(value); - } else if (strcmp(key, "down-action") == 0) { - default_down_action = xstrdup(value); - } else if (strcmp(key, "route-script") == 0) { - default_route_script = xstrdup(value); - } else if (strcmp(key, "route-table") == 0) { - default_route_table = atoi(value); - } else - log_error("host not specified"); - } else if (strcmp(key, "interface") == 0) { - p->interface = xstrdup(value); - } else if (strcmp(key, "gateway") == 0) { - if (p->gateway) - free(p->gateway); - p->gateway = xstrdup(value); - } else if ((strcmp(key, "name") == 0) || (strcmp(key, "label") == 0)) { - p->label = xstrdup(value); - } else if (strcmp(key, "up-action") == 0) { - p->up_action = xstrdup(value); - } else if (strcmp(key, "down-action") == 0) { - p->down_action = xstrdup(value); - } else if (strcmp(key, "retry") == 0) { - p->max_retries = atoi(value); - } else if (strcmp(key, "required") == 0) { - p->required_replies = atoi(value); - } else if (strcmp(key, "timeout") == 0) { - p->timeout = atof(value); - } else if (strcmp(key, "source-ip") == 0) { - p->source_ip = xstrdup(value); - } else if (strcmp(key, "interval") == 0) { - p->burst_interval = atof(value); - } else if (strcmp(key, "route-table") == 0) { - p->iface_route_table = atoi(value); - } else { - log_error("Unknown keyword '%s' on line %i", key, - lineno); - } - } - return 0; -} - -static char *get_provider_gateway(struct pingu_host *p) -{ - if (p->gateway != NULL) - return p->gateway; - return p->host; -} - -static void exec_route_change_hook(void) -{ - struct pingu_host *host; - struct list_head *n; - char **args; - int i = 0; - pid_t pid; - - if (default_route_script == NULL) - return; - - list_for_each(n, &host_list) - i++; - - args = malloc(sizeof(char *) * (i + 2)); - if (args == NULL) { - log_perror("malloc"); - return; - } - - i = 0; - args[i++] = default_route_script; - list_for_each_entry(host, &host_list, host_list_entry) { - if (host->status) - args[i++] = get_provider_gateway(host); - } - args[i] = NULL; - pid = fork(); - if (pid < 0) { - log_perror("fork"); - free(args); - return; - } - if (pid == 0) { - /* the child */ - execvp(default_route_script, args); - log_perror(args[0]); - exit(1); - } - /* libev reaps all children */ -} static void execute_action(const char *action) { @@ -245,7 +62,6 @@ int pingu_host_set_status(struct pingu_host *host, int status) if (action != NULL) execute_action(action); pingu_iface_update_routes(host->iface, route_action); - exec_route_change_hook(); return status; } @@ -260,16 +76,38 @@ int pingu_host_verify_status(struct ev_loop *loop, struct pingu_host *host) return 0; } -int pingu_host_init(struct ev_loop *loop, const char *config) +struct pingu_host *pingu_host_new(char *hoststr, float burst_interval, + int max_retries, int required_replies, + float timeout, + const char *up_action, + const char *down_action) { - struct pingu_host *host; - if (pingu_host_read_config(config) < 0) - return -1; - - if (pingu_iface_init(loop, &host_list) < 0) - return -1; + struct pingu_host *host = calloc(1, sizeof(struct pingu_host)); + if (host == NULL) { + log_perror(hoststr); + return NULL; + } + + host->host = hoststr; + host->status = 1; /* online by default */ + host->burst_interval = burst_interval; + host->max_retries = max_retries; + host->required_replies = required_replies; + host->timeout = timeout; + host->up_action = up_action; + host->down_action = down_action; + + list_add(&host->host_list_entry, &host_list); + return host; +} + +int pingu_host_init(struct ev_loop *loop) +{ + struct pingu_host *host; list_for_each_entry(host, &host_list, host_list_entry) { + if (host->label == NULL) + host->label = host->host; ev_timer_init(&host->burst_timeout_watcher, pingu_burst_timeout_cb, 0, host->burst_interval); ev_timer_start(loop, &host->burst_timeout_watcher); diff --git a/pingu_host.h b/pingu_host.h index f19c461..71e83a7 100644 --- a/pingu_host.h +++ b/pingu_host.h @@ -8,14 +8,10 @@ struct pingu_host { struct list_head host_list_entry; char *host; - char *source_ip; char *label; - char *interface; - char *gateway; - char *up_action; - char *down_action; + const char *up_action; + const char *down_action; int status; - int iface_route_table; int max_retries; int required_replies; ev_tstamp timeout; @@ -26,8 +22,13 @@ struct pingu_host { struct pingu_iface *iface; }; +struct pingu_host *pingu_host_new(char *hoststr, float burst_interval, + int max_retries, int required_replies, + float timeout, + const char *up_action, + const char *down_action); int pingu_host_set_status(struct pingu_host *host, int status); -int pingu_host_init(struct ev_loop *loop, const char *config_file); +int pingu_host_init(struct ev_loop *loop); int pingu_host_verify_status(struct ev_loop *loop, struct pingu_host *host); #endif diff --git a/pingu_iface.c b/pingu_iface.c index 85d2524..8fc56d4 100644 --- a/pingu_iface.c +++ b/pingu_iface.c @@ -22,6 +22,10 @@ static struct list_head iface_list = LIST_INITIALIZER(iface_list); +#define PINGU_ROUTE_TABLE_MIN 1 +#define PINGU_ROUTE_TABLE_MAX 253 +unsigned char used_route_table[256]; + static void pingu_iface_socket_cb(struct ev_loop *loop, struct ev_io *w, int revents) { @@ -71,10 +75,11 @@ struct pingu_iface *pingu_iface_get_by_name(const char *name) struct pingu_iface *iface; list_for_each_entry(iface, &iface_list, iface_list_entry) { if (name == NULL) { - if (iface->name[0] == '\n') + if (iface->name[0] == '\0') return iface; - } else if (strncmp(name, iface->name, sizeof(iface->name)) == 0) + } else if (strncmp(name, iface->name, sizeof(iface->name)) == 0) { return iface; + } } return NULL; } @@ -89,7 +94,7 @@ struct pingu_iface *pingu_iface_get_by_index(int index) return NULL; } -struct pingu_iface *pingu_iface_new(const char *name) +struct pingu_iface *pingu_iface_get_by_name_or_new(const char *name) { struct pingu_iface *iface = pingu_iface_get_by_name(name); if (iface != NULL) @@ -254,27 +259,36 @@ void pingu_iface_update_routes(struct pingu_iface *iface, int action) } } -int pingu_iface_init(struct ev_loop *loop, struct list_head *host_list) +int pingu_iface_set_route_table(struct pingu_iface *iface, int table) { - struct pingu_host *host; - struct pingu_iface *iface; - int autotbl = 10; - list_for_each_entry(host, host_list, host_list_entry) { - iface = pingu_iface_get_by_name(host->interface); - if (iface == NULL) { - iface = pingu_iface_new(host->interface); - iface->route_table = autotbl++; - } - if (iface == NULL) - return -1; - host->iface = iface; + static int initialized = 0; + int i = 1; + if (!initialized) { + memset(used_route_table, 0, sizeof(used_route_table)); + initialized = 1; + } + if (table == PINGU_ROUTE_TABLE_AUTO) { + while (i < 253 && used_route_table[i]) + i++; + table = i; } + if (table < PINGU_ROUTE_TABLE_MIN || table >= PINGU_ROUTE_TABLE_MAX) { + log_error("Invalid route table %i", table); + return -1; + } + used_route_table[table] = 1; + iface->route_table = table; + return table; +} +int pingu_iface_init(struct ev_loop *loop) +{ + struct pingu_iface *iface; list_for_each_entry(iface, &iface_list, iface_list_entry) { + if (iface->route_table == 0) + pingu_iface_set_route_table(iface, PINGU_ROUTE_TABLE_AUTO); if (pingu_iface_init_socket(loop, iface) == -1) return -1; } - return 0; } - diff --git a/pingu_iface.h b/pingu_iface.h index 68d4b32..8107601 100644 --- a/pingu_iface.h +++ b/pingu_iface.h @@ -7,6 +7,8 @@ #include "sockaddr_util.h" #include "list.h" +#define PINGU_ROUTE_TABLE_AUTO -1 + struct pingu_gateway { union sockaddr_any gw_addr; union sockaddr_any dest; @@ -37,13 +39,14 @@ struct pingu_iface { struct pingu_iface *pingu_iface_get_by_name(const char *name); struct pingu_iface *pingu_iface_get_by_index(int index); -struct pingu_iface *pingu_iface_new(const char *name); +struct pingu_iface *pingu_iface_get_by_name_or_new(const char *name); int pingu_iface_bind_socket(struct pingu_iface *iface, int log_error); int pingu_iface_usable(struct pingu_iface *iface); -int pingu_iface_init(struct ev_loop *loop, struct list_head *host_list); +int pingu_iface_init(struct ev_loop *loop); void pingu_iface_set_addr(struct pingu_iface *iface, int family, void *data, int len); +int pingu_iface_set_route_table(struct pingu_iface *iface, int table); void pingu_iface_gw_action(struct pingu_iface *iface, struct pingu_gateway *gw, int action); |