aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--pingu.c17
-rw-r--r--pingu.conf44
-rw-r--r--pingu_conf.c208
-rw-r--r--pingu_conf.h6
-rw-r--r--pingu_host.c220
-rw-r--r--pingu_host.h15
-rw-r--r--pingu_iface.c50
-rw-r--r--pingu_iface.h7
9 files changed, 323 insertions, 245 deletions
diff --git a/Makefile b/Makefile
index 5d9d233..e054c85 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
diff --git a/pingu.c b/pingu.c
index 6da05c6..b124d4f 100644
--- a/pingu.c
+++ b/pingu.c
@@ -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) {
diff --git a/pingu.conf b/pingu.conf
index 55f7ee2..b54861a 100644
--- a/pingu.conf
+++ b/pingu.conf
@@ -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);