123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- /*
- Copyright (c) 2016 Jan-Philipp Litza <janphilipp@litza.de>
- Copyright (c) 2017 Sven Eckelmann <sven@narfation.org>
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <errno.h>
- #include <signal.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <sys/select.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <net/ethernet.h>
- #include <net/if.h>
- #include <linux/filter.h>
- #include <linux/if_packet.h>
- #include <linux/limits.h>
- #include <netinet/icmp6.h>
- #include <netinet/in.h>
- #include <netinet/ip6.h>
- #include <netlink/netlink.h>
- #include <netlink/genl/genl.h>
- #include <netlink/genl/ctrl.h>
- #include <batadv-genl.h>
- #include "mac.h"
- // Recheck TQs after this time even if no RA was received
- #define MAX_INTERVAL 60
- // Recheck TQs at most this often, even if new RAs were received (they won't
- // become the preferred routers until the TQs have been rechecked)
- // Also, the first update will take at least this long
- #define MIN_INTERVAL 15
- // max execution time of a single ebtables call in nanoseconds
- #define EBTABLES_TIMEOUT 500000000 // 500ms
- // TQ value assigned to local routers
- #define LOCAL_TQ 512
- #define BUFSIZE 1500
- #ifdef DEBUG
- #define CHECK(stmt) \
- if(!(stmt)) { \
- fprintf(stderr, "check failed: " #stmt "\n"); \
- goto check_failed; \
- }
- #define DEBUG_MSG(msg, ...) fprintf(stderr, msg "\n", ##__VA_ARGS__)
- #else
- #define CHECK(stmt) if(!(stmt)) goto check_failed;
- #define DEBUG_MSG(msg, ...) do {} while(0)
- #endif
- #ifndef ARRAY_SIZE
- #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
- #endif
- #define foreach(item, list) \
- for((item) = (list); (item) != NULL; (item) = (item)->next)
- #define foreach_safe(item, safe, list) \
- for ((item) = (list); \
- (item) && (((safe) = item->next) || 1); \
- (item) = (safe))
- struct router {
- struct router *next;
- struct ether_addr src;
- struct timespec eol;
- struct ether_addr originator;
- uint16_t tq;
- };
- static struct global {
- int sock;
- struct router *routers;
- const char *mesh_iface;
- const char *chain;
- uint16_t max_tq;
- uint16_t hysteresis_thresh;
- struct router *best_router;
- volatile sig_atomic_t stop_daemon;
- } G = {
- .mesh_iface = "bat0",
- };
- static int fork_execvp_timeout(struct timespec *timeout, const char *file,
- const char *const argv[]);
- static void error_message(int status, int errnum, char *message, ...) {
- va_list ap;
- va_start(ap, message);
- fflush(stdout);
- vfprintf(stderr, message, ap);
- va_end(ap);
- if (errnum)
- fprintf(stderr, ": %s", strerror(errnum));
- fprintf(stderr, "\n");
- if (status)
- exit(status);
- }
- static int timespec_diff(struct timespec *tv1, struct timespec *tv2,
- struct timespec *tvdiff)
- {
- tvdiff->tv_sec = tv1->tv_sec - tv2->tv_sec;
- if (tv1->tv_nsec < tv2->tv_nsec) {
- tvdiff->tv_nsec = 1000000000 + tv1->tv_nsec - tv2->tv_nsec;
- tvdiff->tv_sec -= 1;
- } else {
- tvdiff->tv_nsec = tv1->tv_nsec - tv2->tv_nsec;
- }
- return (tvdiff->tv_sec >= 0);
- }
- static void cleanup(void) {
- struct router *router;
- struct timespec timeout = {
- .tv_nsec = EBTABLES_TIMEOUT,
- };
- close(G.sock);
- while (G.routers != NULL) {
- router = G.routers;
- G.routers = router->next;
- free(router);
- }
- if (G.chain) {
- /* Reset chain to accept everything again */
- if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
- { "ebtables", "--concurrent", "-F", G.chain, NULL }))
- DEBUG_MSG("warning: flushing ebtables chain %s failed, not adding a new rule", G.chain);
- if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
- { "ebtables", "--concurrent", "-A", G.chain, "-j", "ACCEPT", NULL }))
- DEBUG_MSG("warning: adding new rule to ebtables chain %s failed", G.chain);
- }
- }
- static void usage(const char *msg) {
- if (msg != NULL && *msg != '\0') {
- fprintf(stderr, "ERROR: %s\n\n", msg);
- }
- fprintf(stderr,
- "Usage: %s [-m <mesh_iface>] [-t <thresh>] -c <chain> -i <iface>\n\n"
- " -m <mesh_iface> B.A.T.M.A.N. advanced mesh interface used to get metric\n"
- " information (\"TQ\") for the available gateways. Default: bat0\n"
- " -t <thresh> Minimum TQ difference required to switch the gateway.\n"
- " Default: 0\n"
- " -c <chain> ebtables chain that should be managed by the daemon. The\n"
- " chain already has to exist on program invocation and should\n"
- " have a DROP policy. It will be flushed by the program!\n"
- " -i <iface> Interface to listen on for router advertisements. Should be\n"
- " <mesh_iface> or a bridge on top of it, as no metric\n"
- " information will be available for hosts on other interfaces.\n\n",
- program_invocation_short_name);
- cleanup();
- if (msg == NULL)
- exit(EXIT_SUCCESS);
- else
- exit(EXIT_FAILURE);
- }
- #define exit_errmsg(message, ...) { \
- fprintf(stderr, message "\n", ##__VA_ARGS__); \
- cleanup(); \
- exit(1); \
- }
- static inline void exit_errno(const char *message) {
- cleanup();
- error_message(1, errno, "error: %s", message);
- }
- static inline void warn_errno(const char *message) {
- error_message(0, errno, "warning: %s", message);
- }
- static int init_packet_socket(unsigned int ifindex) {
- struct sock_filter radv_filter_code[] = {
- // check that this is an ICMPv6 packet
- BPF_STMT(BPF_LD|BPF_B|BPF_ABS, offsetof(struct ip6_hdr, ip6_nxt)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_ICMPV6, 0, 7),
- // check that this is a router advertisement
- BPF_STMT(BPF_LD|BPF_B|BPF_ABS, sizeof(struct ip6_hdr) + offsetof(struct icmp6_hdr, icmp6_type)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ND_ROUTER_ADVERT, 0, 5),
- // check that the code field in the ICMPv6 header is 0
- BPF_STMT(BPF_LD|BPF_B|BPF_ABS, sizeof(struct ip6_hdr) + offsetof(struct nd_router_advert, nd_ra_code)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0, 0, 3),
- // check that this is a default route (lifetime > 0)
- BPF_STMT(BPF_LD|BPF_H|BPF_ABS, sizeof(struct ip6_hdr) + offsetof(struct nd_router_advert, nd_ra_router_lifetime)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0, 1, 0),
- // return true
- BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
- // return false
- BPF_STMT(BPF_RET|BPF_K, 0),
- };
- struct sock_fprog radv_filter = {
- .len = ARRAY_SIZE(radv_filter_code),
- .filter = radv_filter_code,
- };
- int sock = socket(AF_PACKET, SOCK_DGRAM|SOCK_CLOEXEC, htons(ETH_P_IPV6));
- if (sock < 0)
- exit_errno("can't open packet socket");
- int ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &radv_filter, sizeof(radv_filter));
- if (ret < 0)
- exit_errno("can't attach socket filter");
- struct sockaddr_ll bind_iface = {
- .sll_family = AF_PACKET,
- .sll_protocol = htons(ETH_P_IPV6),
- .sll_ifindex = ifindex,
- };
- ret = bind(sock, (struct sockaddr *)&bind_iface, sizeof(bind_iface));
- if (ret < 0)
- exit_errno("can't bind socket");
- return sock;
- }
- static void parse_cmdline(int argc, char *argv[]) {
- int c;
- unsigned int ifindex;
- unsigned long int threshold;
- char *endptr;
- while ((c = getopt(argc, argv, "c:hi:m:t:")) != -1) {
- switch (c) {
- case 'i':
- if (G.sock >= 0)
- usage("-i given more than once");
- ifindex = if_nametoindex(optarg);
- if (ifindex == 0)
- exit_errmsg("Unknown interface: %s", optarg);
- G.sock = init_packet_socket(ifindex);
- break;
- case 'm':
- G.mesh_iface = optarg;
- break;
- case 'c':
- G.chain = optarg;
- break;
- case 't':
- threshold = strtoul(optarg, &endptr, 10);
- if (*endptr != '\0')
- exit_errmsg("Threshold must be a number: %s", optarg);
- if (threshold >= LOCAL_TQ)
- exit_errmsg("Threshold too large: %ld (max is %d)", threshold, LOCAL_TQ);
- G.hysteresis_thresh = (uint16_t) threshold;
- break;
- case 'h':
- usage(NULL);
- break;
- default:
- usage("");
- break;
- }
- }
- }
- static struct router *router_find_src(const struct ether_addr *src) {
- struct router *router;
- foreach(router, G.routers) {
- if (ether_addr_equal(router->src, *src))
- return router;
- }
- return NULL;
- }
- static struct router *router_find_orig(const struct ether_addr *orig) {
- struct router *router;
- foreach(router, G.routers) {
- if (ether_addr_equal(router->originator, *orig))
- return router;
- }
- return NULL;
- }
- static struct router *router_add(const struct ether_addr *mac) {
- struct router *router;
- router = malloc(sizeof(*router));
- if (!router)
- return NULL;
- router->src = *mac;
- router->next = G.routers;
- G.routers = router;
- router->eol.tv_sec = 0;
- router->eol.tv_nsec = 0;
- memset(&router->originator, 0, sizeof(router->originator));
- return router;
- }
- static void router_update(const struct ether_addr *mac, uint16_t timeout) {
- struct router *router;
- router = router_find_src(mac);
- if (!router)
- router = router_add(mac);
- if (!router)
- return;
- clock_gettime(CLOCK_MONOTONIC, &router->eol);
- router->eol.tv_sec += timeout;
- }
- static void handle_ra(int sock) {
- struct sockaddr_ll src;
- struct ether_addr mac;
- socklen_t addr_size = sizeof(src);
- ssize_t len;
- struct {
- struct ip6_hdr ip6;
- struct nd_router_advert ra;
- } pkt;
- len = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *)&src, &addr_size);
- CHECK(len >= 0);
- // BPF already checked that this is an ICMPv6 RA of a default router
- CHECK((size_t)len >= sizeof(pkt));
- CHECK(ntohs(pkt.ip6.ip6_plen) + sizeof(struct ip6_hdr) >= sizeof(pkt));
- memcpy(&mac, src.sll_addr, sizeof(mac));
- DEBUG_MSG("received valid RA from " F_MAC, F_MAC_VAR(mac));
- router_update(&mac, ntohs(pkt.ra.nd_ra_router_lifetime));
- check_failed:
- return;
- }
- static void expire_routers(void) {
- struct router **prev_ptr = &G.routers;
- struct router *router;
- struct router *safe;
- struct timespec now;
- struct timespec diff;
- clock_gettime(CLOCK_MONOTONIC, &now);
- foreach_safe(router, safe, G.routers) {
- if (timespec_diff(&now, &router->eol, &diff)) {
- DEBUG_MSG("router " F_MAC " expired", F_MAC_VAR(router->src));
- *prev_ptr = router->next;
- if (G.best_router == router)
- G.best_router = NULL;
- free(router);
- } else {
- prev_ptr = &router->next;
- }
- }
- }
- static int parse_tt_global(struct nl_msg *msg,
- void *arg __attribute__((unused)))
- {
- static const enum batadv_nl_attrs mandatory[] = {
- BATADV_ATTR_TT_ADDRESS,
- BATADV_ATTR_ORIG_ADDRESS,
- };
- struct nlattr *attrs[BATADV_ATTR_MAX + 1];
- struct nlmsghdr *nlh = nlmsg_hdr(msg);
- struct ether_addr mac_a, mac_b;
- struct genlmsghdr *ghdr;
- struct router *router;
- uint8_t *addr;
- uint8_t *orig;
- // parse netlink entry
- if (!genlmsg_valid_hdr(nlh, 0))
- return NL_OK;
- ghdr = nlmsg_data(nlh);
- if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL)
- return NL_OK;
- if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
- genlmsg_len(ghdr), batadv_genl_policy)) {
- return NL_OK;
- }
- if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
- return NL_OK;
- addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
- orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
- if (!attrs[BATADV_ATTR_FLAG_BEST])
- return NL_OK;
- MAC2ETHER(mac_a, addr);
- MAC2ETHER(mac_b, orig);
- // update router
- router = router_find_src(&mac_a);
- if (!router)
- return NL_OK;
- DEBUG_MSG("Found originator for " F_MAC ", it's " F_MAC,
- F_MAC_VAR(router->src), F_MAC_VAR(mac_b));
- router->originator = mac_b;
- return NL_OK;
- }
- static int parse_originator(struct nl_msg *msg,
- void *arg __attribute__((unused)))
- {
- static const enum batadv_nl_attrs mandatory[] = {
- BATADV_ATTR_ORIG_ADDRESS,
- BATADV_ATTR_TQ,
- };
- struct nlattr *attrs[BATADV_ATTR_MAX + 1];
- struct nlmsghdr *nlh = nlmsg_hdr(msg);
- struct ether_addr mac_a;
- struct genlmsghdr *ghdr;
- struct router *router;
- uint8_t *orig;
- uint8_t tq;
- // parse netlink entry
- if (!genlmsg_valid_hdr(nlh, 0))
- return NL_OK;
- ghdr = nlmsg_data(nlh);
- if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS)
- return NL_OK;
- if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
- genlmsg_len(ghdr), batadv_genl_policy)) {
- return NL_OK;
- }
- if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
- return NL_OK;
- orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
- tq = nla_get_u8(attrs[BATADV_ATTR_TQ]);
- if (!attrs[BATADV_ATTR_FLAG_BEST])
- return NL_OK;
- MAC2ETHER(mac_a, orig);
- // update router
- router = router_find_orig(&mac_a);
- if (!router)
- return NL_OK;
- DEBUG_MSG("Found TQ for router " F_MAC " (originator " F_MAC "), it's %d",
- F_MAC_VAR(router->src), F_MAC_VAR(router->originator), tq);
- router->tq = tq;
- if (router->tq > G.max_tq)
- G.max_tq = router->tq;
- return NL_OK;
- }
- static int parse_tt_local(struct nl_msg *msg,
- void *arg __attribute__((unused)))
- {
- static const enum batadv_nl_attrs mandatory[] = {
- BATADV_ATTR_TT_ADDRESS,
- };
- struct nlattr *attrs[BATADV_ATTR_MAX + 1];
- struct nlmsghdr *nlh = nlmsg_hdr(msg);
- struct ether_addr mac_a;
- struct genlmsghdr *ghdr;
- struct router *router;
- uint8_t *addr;
- // parse netlink entry
- if (!genlmsg_valid_hdr(nlh, 0))
- return NL_OK;
- ghdr = nlmsg_data(nlh);
- if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL)
- return NL_OK;
- if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
- genlmsg_len(ghdr), batadv_genl_policy)) {
- return NL_OK;
- }
- if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
- return NL_OK;
- addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
- MAC2ETHER(mac_a, addr);
- // update router
- router = router_find_src(&mac_a);
- if (!router)
- return NL_OK;
- DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning TQ %d",
- F_MAC_VAR(router->src), LOCAL_TQ);
- router->tq = LOCAL_TQ;
- if (router->tq > G.max_tq)
- G.max_tq = router->tq;
- return NL_OK;
- }
- static void update_tqs(void) {
- struct router *router;
- bool update_originators = false;
- struct ether_addr unspec;
- struct batadv_nlquery_opts opts;
- int ret;
- // reset TQs
- memset(&unspec, 0, sizeof(unspec));
- foreach(router, G.routers) {
- router->tq = 0;
- if (ether_addr_equal(router->originator, unspec))
- update_originators = true;
- }
- // translate all router's MAC addresses to originators simultaneously
- if (update_originators) {
- opts.err = 0;
- ret = batadv_genl_query(G.mesh_iface,
- BATADV_CMD_GET_TRANSTABLE_GLOBAL,
- parse_tt_global, NLM_F_DUMP, &opts);
- if (ret < 0)
- fprintf(stderr, "Parsing of global translation table failed\n");
- }
- // look up TQs of originators
- G.max_tq = 0;
- opts.err = 0;
- ret = batadv_genl_query(G.mesh_iface,
- BATADV_CMD_GET_ORIGINATORS,
- parse_originator, NLM_F_DUMP, &opts);
- if (ret < 0)
- fprintf(stderr, "Parsing of originators failed\n");
- // if all routers have a TQ value, we don't need to check translocal
- foreach(router, G.routers) {
- if (router->tq == 0)
- break;
- }
- if (router != NULL) {
- opts.err = 0;
- ret = batadv_genl_query(G.mesh_iface,
- BATADV_CMD_GET_TRANSTABLE_LOCAL,
- parse_tt_local, NLM_F_DUMP, &opts);
- if (ret < 0)
- fprintf(stderr, "Parsing of global translation table failed\n");
- }
- foreach(router, G.routers) {
- if (router->tq == 0) {
- if (ether_addr_equal(router->originator, unspec))
- fprintf(stderr,
- "Unable to find router " F_MAC " in transtable_{global,local}\n",
- F_MAC_VAR(router->src));
- else
- fprintf(stderr,
- "Unable to find TQ for originator " F_MAC " (router " F_MAC ")\n",
- F_MAC_VAR(router->originator),
- F_MAC_VAR(router->src));
- }
- }
- }
- static int fork_execvp_timeout(struct timespec *timeout, const char *file, const char *const argv[]) {
- int ret;
- pid_t child;
- siginfo_t info;
- sigset_t signals, oldsignals;
- sigemptyset(&signals);
- sigaddset(&signals, SIGCHLD);
- sigprocmask(SIG_BLOCK, &signals, &oldsignals);
- child = fork();
- if (child == 0) {
- sigprocmask(SIG_SETMASK, &oldsignals, NULL);
- // casting discards const, but should be safe
- // (see http://stackoverflow.com/q/36925388)
- execvp(file, (char**) argv);
- fprintf(stderr, "can't execvp(\"%s\", ...): %s\n", file, strerror(errno));
- _exit(1);
- }
- else if (child < 0) {
- perror("Failed to fork()");
- return -1;
- }
- ret = sigtimedwait(&signals, &info, timeout);
- sigprocmask(SIG_SETMASK, &oldsignals, NULL);
- if (ret == SIGCHLD) {
- if (info.si_pid != child) {
- cleanup();
- error_message(1, 0,
- "BUG: We received a SIGCHLD from a child we didn't spawn (expected PID %d, got %d)",
- child, info.si_pid);
- }
- waitpid(child, NULL, 0);
- return info.si_status;
- }
- if (ret < 0 && errno == EAGAIN)
- error_message(0, 0, "warning: child %d took too long, killing", child);
- else if (ret < 0)
- warn_errno("sigtimedwait failed, killing child");
- else
- error_message(1, 0,
- "BUG: sigtimedwait() returned some other signal than SIGCHLD: %d",
- ret);
- kill(child, SIGKILL);
- kill(child, SIGCONT);
- waitpid(child, NULL, 0);
- return -1;
- }
- static bool election_required(void)
- {
- if (!G.best_router)
- return true;
- /* should never happen. G.max_tq also contains G.best_router->tq */
- if (G.max_tq < G.best_router->tq)
- return false;
- if ((G.max_tq - G.best_router->tq) <= G.hysteresis_thresh)
- return false;
- return true;
- }
- static void update_ebtables(void) {
- struct timespec timeout = {
- .tv_nsec = EBTABLES_TIMEOUT,
- };
- char mac[F_MAC_LEN + 1];
- struct router *router;
- if (!election_required()) {
- DEBUG_MSG(F_MAC " is still good enough with TQ=%d (max_tq=%d), not executing ebtables",
- F_MAC_VAR(G.best_router->src),
- G.best_router->tq,
- G.max_tq);
- return;
- }
- foreach(router, G.routers) {
- if (router->tq == G.max_tq) {
- snprintf(mac, sizeof(mac), F_MAC, F_MAC_VAR(router->src));
- break;
- }
- }
- if (G.best_router)
- fprintf(stderr, "Switching from " F_MAC " (TQ=%d) to %s (TQ=%d)\n",
- F_MAC_VAR(G.best_router->src),
- G.best_router->tq,
- mac,
- G.max_tq);
- else
- fprintf(stderr, "Switching to %s (TQ=%d)\n",
- mac,
- G.max_tq);
- G.best_router = router;
- if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
- { "ebtables", "--concurrent", "-F", G.chain, NULL }))
- error_message(0, 0, "warning: flushing ebtables chain %s failed, not adding a new rule", G.chain);
- else if (fork_execvp_timeout(&timeout, "ebtables", (const char *[])
- { "ebtables", "--concurrent", "-A", G.chain, "-s", mac, "-j", "ACCEPT", NULL }))
- error_message(0, 0, "warning: adding new rule to ebtables chain %s failed", G.chain);
- }
- static void sighandler(int sig __attribute__((unused)))
- {
- G.stop_daemon = 1;
- }
- int main(int argc, char *argv[]) {
- int retval;
- fd_set rfds;
- struct timeval tv;
- struct timespec next_update;
- struct timespec now;
- struct timespec diff;
- clock_gettime(CLOCK_MONOTONIC, &next_update);
- next_update.tv_sec += MIN_INTERVAL;
- G.sock = -1;
- parse_cmdline(argc, argv);
- if (G.sock < 0)
- usage("No interface set!");
- if (G.chain == NULL)
- usage("No chain set!");
- G.stop_daemon = 0;
- signal(SIGINT, sighandler);
- signal(SIGTERM, sighandler);
- while (!G.stop_daemon) {
- FD_ZERO(&rfds);
- FD_SET(G.sock, &rfds);
- tv.tv_sec = MAX_INTERVAL;
- tv.tv_usec = 0;
- retval = select(G.sock + 1, &rfds, NULL, NULL, &tv);
- if (retval < 0) {
- if (errno != EINTR)
- exit_errno("select() failed");
- } else if (retval) {
- if (FD_ISSET(G.sock, &rfds)) {
- handle_ra(G.sock);
- }
- }
- else
- DEBUG_MSG("select() timeout expired");
- clock_gettime(CLOCK_MONOTONIC, &now);
- if (G.routers != NULL &&
- timespec_diff(&now, &next_update, &diff)) {
- expire_routers();
- // all routers could have expired, check again
- if (G.routers != NULL) {
- update_tqs();
- update_ebtables();
- next_update = now;
- next_update.tv_sec += MIN_INTERVAL;
- }
- }
- }
- cleanup();
- return 0;
- }
|