babel.c 29 KB
Newer Older
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1
/*
2
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <assert.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>

41
#include "babel.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
42 43 44
#include "util.h"
#include "net.h"
#include "kernel.h"
45
#include "network.h"
46
#include "source.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
47 48 49 50
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
51
#include "resend.h"
52
#include "filter.h"
53
#include "local.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
54 55 56 57 58 59

struct timeval now;

unsigned char myid[16];
int debug = 0;

60
time_t reboot_time;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
61

62
int idle_time = 320;
63
int link_detect = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
64
int all_wireless = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
65 66
int wireless_hello_interval = -1;
int wired_hello_interval = -1;
67
int idle_hello_interval = -1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
68
int update_interval = -1;
69
int do_daemonise = 0;
70
char *logfile = NULL, *pidfile = "/var/run/babel.pid";
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
71

72 73 74
unsigned char *receive_buffer = NULL;
int receive_buffer_size = 0;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
75
const unsigned char zeroes[16] = {0};
76 77 78
const unsigned char ones[16] =
    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
79

80
char *state_file = "/var/lib/babel-state";
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
81 82 83 84

int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
85
int kernel_socket = -1;
86
static int kernel_routes_changed = 0;
87 88
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
89

90 91
struct timeval check_neighbours_timeout;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
92
static volatile sig_atomic_t exiting = 0, dumping = 0, changed = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
93

94
int local_server_socket = -1, local_socket = -1;
95
int local_server_port = -1;
96

97
static int kernel_routes_callback(int changed, void *closure);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
98
static void init_signals(void);
99
static void dump_tables(FILE *out);
100
static int reopen_logfile();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
101 102 103 104 105

int
main(int argc, char **argv)
{
    struct sockaddr_in6 sin6;
106
    int i, rc, fd, rfd, have_id = 0;
107
    time_t expiry_time, source_expiry_time, kernel_dump_time;
108
    char *config_file = NULL;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
109 110 111
    void *vrc;
    unsigned int seed;
    char **arg;
112
    struct network *net;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
113

114
    parse_address("ff02::cca6:c0f9:e182:5373", protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
115 116
    protocol_port = 8475;

117
#define SHIFT() do { arg++; } while(0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
118 119 120 121 122 123 124 125 126 127 128 129
#define SHIFTE() do { arg++; if(*arg == NULL) goto syntax; } while(0)

    arg = argv;

    SHIFTE();

    while((*arg)[0] == '-') {
        if(strcmp(*arg, "--") == 0) {
            SHIFTE();
            break;
        } else if(strcmp(*arg, "-m") == 0) {
            SHIFTE();
130
            rc = parse_address(*arg, protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
            if(rc < 0)
                goto syntax;
            if(protocol_group[0] != 0xff) {
                fprintf(stderr,
                        "%s is not a multicast address\n", *arg);
                goto syntax;
            }
            if(protocol_group[1] != 2) {
                fprintf(stderr,
                        "Warning: %s is not a link-local multicast address\n",
                        *arg);
            }
        } else if(strcmp(*arg, "-p") == 0) {
            SHIFTE();
            protocol_port = atoi(*arg);
        } else if(strcmp(*arg, "-h") == 0) {
            SHIFTE();
148 149 150
            wireless_hello_interval = parse_msec(*arg);
            if(wireless_hello_interval <= 0)
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
151 152
        } else if(strcmp(*arg, "-H") == 0) {
            SHIFTE();
153 154 155
            wired_hello_interval = parse_msec(*arg);
            if(wired_hello_interval <= 0)
                goto syntax;
156 157
        } else if(strcmp(*arg, "-i") == 0) {
            SHIFTE();
158 159 160
            idle_hello_interval = parse_msec(*arg);
            if(idle_hello_interval <= 0)
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
161 162
        } else if(strcmp(*arg, "-u") == 0) {
            SHIFTE();
163 164 165
            update_interval = parse_msec(*arg);
            if(update_interval <= 0)
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
166 167 168
        } else if(strcmp(*arg, "-k") == 0) {
            SHIFTE();
            kernel_metric = atoi(*arg);
169
            if(kernel_metric < 0 || kernel_metric > 0xFFFF)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
170 171 172 173 174 175 176 177 178 179 180
                goto syntax;
        } else if(strcmp(*arg, "-P") == 0) {
            parasitic = 1;
        } else if(strcmp(*arg, "-s") == 0) {
            split_horizon = 0;
        } else if(strcmp(*arg, "-S") == 0) {
            SHIFTE();
            state_file = *arg;
        } else if(strcmp(*arg, "-d") == 0) {
            SHIFTE();
            debug = atoi(*arg);
181
#ifndef NO_LOCAL_INTERFACE
182 183 184
        } else if(strcmp(*arg, "-g") == 0) {
            SHIFTE();
            local_server_port = atoi(*arg);
185
#endif
186 187
        } else if(strcmp(*arg, "-l") == 0) {
            link_detect = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
188 189
        } else if(strcmp(*arg, "-w") == 0) {
            all_wireless = 1;
190 191 192 193 194 195 196 197 198 199
        } else if(strcmp(*arg, "-t") == 0) {
            SHIFTE();
            export_table = atoi(*arg);
            if(export_table < 0 || export_table > 0xFFFF)
                goto syntax;
        } else if(strcmp(*arg, "-T") == 0) {
            SHIFTE();
            import_table = atoi(*arg);
            if(import_table < 0 || import_table > 0xFFFF)
                goto syntax;
200 201 202 203 204 205 206 207 208 209 210 211
        } else if(strcmp(*arg, "-c") == 0) {
            SHIFTE();
            config_file = *arg;
        } else if(strcmp(*arg, "-C") == 0) {
            int rc;
            SHIFTE();
            rc = parse_config_from_string(*arg);
            if(rc < 0) {
                fprintf(stderr,
                        "Couldn't parse configuration from command line.\n");
                exit(1);
            }
212 213 214 215 216 217 218 219
        } else if(strcmp(*arg, "-D") == 0) {
            do_daemonise = 1;
        } else if(strcmp(*arg, "-L") == 0) {
            SHIFTE();
            logfile = *arg;
        } else if(strcmp(*arg, "-I") == 0) {
            SHIFTE();
            pidfile = *arg;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
220 221 222 223 224 225
        } else {
            goto syntax;
        }
        SHIFTE();
    }

226 227 228 229 230 231 232 233 234

    if(!config_file) {
        if(access("/etc/babel.conf", R_OK) >= 0)
            config_file = "/etc/babel.conf";
    }
    if(config_file) {
        rc = parse_config_from_file(config_file);
        if(rc < 0) {
            fprintf(stderr,
235 236
                    "Couldn't parse configuration from file %s.\n",
                    config_file);
237 238 239 240
            exit(1);
        }
    }

241 242 243 244 245 246
    rc = finalise_filters();
    if(rc < 0) {
        fprintf(stderr, "Couldn't finalise filters.\n");
        exit(1);
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
247
    if(wireless_hello_interval <= 0)
248
        wireless_hello_interval = 4000;
249
    wireless_hello_interval = MAX(wireless_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
250 251

    if(wired_hello_interval <= 0)
252
        wired_hello_interval = 20000;
253
    wired_hello_interval = MAX(wired_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
254 255 256

    if(update_interval <= 0)
        update_interval =
257
            MIN(MIN(wireless_hello_interval * 5, wired_hello_interval * 2),
258
                70000);
259
    update_interval = MAX(update_interval, 70);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
260 261

    if(seqno_interval <= 0)
262
        seqno_interval = MAX(40000, update_interval * 9 / 10);
263
    seqno_interval = MAX(seqno_interval, 20);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
264

265 266 267 268 269
    if(do_daemonise) {
        if(logfile == NULL)
            logfile = "/var/log/babel.log";
    }

270 271 272 273
    rc = reopen_logfile();
    if(rc < 0) {
        perror("reopen_logfile()");
        goto fail;
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    }

    fd = open("/dev/null", O_RDONLY);
    if(fd < 0) {
        perror("open(null)");
        goto fail;
    }

    rc = dup2(fd, 0);
    if(rc < 0) {
        perror("dup2(null, 0)");
        goto fail;
    }

    close(fd);

    if(do_daemonise) {
        rc = daemonise();
        if(rc < 0) {
            perror("daemonise");
            goto fail_nopid;
        }
    }

    if(pidfile) {
        int pfd, len;
        char buf[100];

        len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
        if(len < 0 || len >= 100) {
            perror("snprintf(getpid)");
            goto fail_nopid;
        }

        pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
        if(pfd < 0) {
            perror("creat(pidfile)");
            goto fail_nopid;
        }

        rc = write(pfd, buf, len);
        if(rc < len) {
            perror("write(pidfile)");
            goto fail;
        }

        close(pfd);
    }

323 324 325 326 327 328 329 330 331 332 333 334
    rc = kernel_setup(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
        exit(1);
    }

    rc = kernel_setup_socket(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup_socket failed.\n");
        kernel_setup(0);
        exit(1);
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
335

336
    gettime(&now);
337

338 339
    rfd = open("/dev/urandom", O_RDONLY);
    if(rfd < 0) {
340
        perror("open(random)");
341 342 343 344 345 346 347 348 349 350 351 352 353
    }

    rc = parse_address(*arg, myid, NULL);
    if(rc >= 0) {
        have_id = 1;
        /* Cannot use SHIFTE -- need to goto fail */
        SHIFT();
        if(*arg == NULL) {
            fprintf(stderr, "No interfaces given.\n");
            goto fail;
        }
    } else {
        struct kernel_route routes[240];
354
        rc = kernel_addresses(NULL, 0, routes, 240);
355 356 357
        if(rc < 0) {
            perror("kernel_addresses");
        }
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
        if(rc > 0) {
            /* Search for a global IPv6 address */
            for(i = 0; i < rc; i++) {
                if(martian_prefix(routes[i].prefix, routes[i].plen))
                    continue;
                if(routes[i].plen == 128 &&
                   (routes[i].prefix[0] & 0xE0) == 0x20) {
                    memcpy(myid, routes[i].prefix, 16);
                    have_id = 1;
                    break;
                }
            }
            /* Try a global Ipv4 address */
            if(!have_id) {
                for(i = 0; i < rc; i++) {
                    if(martian_prefix(routes[i].prefix, routes[i].plen))
                        continue;
                    if(routes[i].plen == 128 &&
                       v4mapped(routes[i].prefix) &&
                       routes[i].prefix[12] != 10 &&
                       (routes[i].prefix[12] != 172 ||
379
                        (routes[i].prefix[13] & 0xF0) != 16) &&
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
                       (routes[i].prefix[12] != 192 ||
                        routes[i].prefix[13] != 168)) {
                        memcpy(myid, routes[i].prefix, 16);
                        have_id = 1;
                        break;
                    }
                }
            }
        }
    }

    if(!have_id) {
        if(rfd < 0) {
            fprintf(stderr, "Couldn't find suitable router-id.\n");
            goto fail;
        }
        fprintf(stderr,
                "Warning: couldn't find suitable router-id, "
                "using random value.\n");
        rc = read(rfd, myid, 16);
        if(rc < 16) {
            perror("read(random)");
            goto fail;
        } else {
            have_id = 1;
        }
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
408
    if(rfd < 0) {
409 410
        memcpy(&seed, myid + 12, 4);
    } else {
411
        rc = read(rfd, &seed, sizeof(unsigned int));
412 413
        if(rc < sizeof(unsigned int)) {
            perror("read(random)");
414
            memcpy(&seed, myid + 12, 4);
415
        }
416 417
        close(rfd);
        rfd = -1;
418
    }
419

420 421 422
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
423
    reboot_time = now.tv_sec;
424
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
425 426 427

    fd = open(state_file, O_RDONLY);
    if(fd < 0 && errno != ENOENT)
428
        perror("open(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
429
    rc = unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
430
    if(fd >= 0 && rc < 0) {
431
        perror("unlink(babel-state)");
432
        /* If we couldn't unlink it, it's probably stale. */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
433 434 435 436 437
        close(fd);
        fd = -1;
    }
    if(fd >= 0) {
        char buf[100];
438 439 440
        char buf2[100];
        int s;
        long t;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
441 442
        rc = read(fd, buf, 99);
        if(rc < 0) {
443
            perror("read(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
444 445
        } else {
            buf[rc] = '\0';
446 447 448
            rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
            if(rc == 3 && s >= 0 && s <= 0xFFFF) {
                unsigned char sid[16];
449
                rc = parse_address(buf2, sid, NULL);
450 451 452
                if(rc < 0) {
                    fprintf(stderr, "Couldn't parse babel-state.\n");
                } else {
453
                    struct timeval realnow;
454 455
                    debugf("Got %s %d %ld from babel-state.\n",
                           format_address(sid), s, t);
456
                    gettimeofday(&realnow, NULL);
457 458 459 460
                    if(memcmp(sid, myid, 16) == 0)
                        myseqno = seqno_plus(s, 1);
                    else
                        fprintf(stderr, "ID mismatch in babel-state.\n");
461 462 463
                    /* Convert realtime into monotonic time. */
                    if(t >= 1176800000L && t <= realnow.tv_sec)
                        reboot_time = now.tv_sec - (realnow.tv_sec - t);
464
                }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
465
            } else {
466
                fprintf(stderr, "Couldn't parse babel-state.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
467 468 469
            }
        }
        close(fd);
470
        fd = -1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
471 472
    }

473 474 475 476
    if(reboot_time + silent_time > now.tv_sec)
        fprintf(stderr, "Respecting %ld second silent time.\n",
                (long int)(reboot_time + silent_time - now.tv_sec));

477
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
478 479 480 481 482 483
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

    while(*arg) {
484 485
        debugf("Adding network %s.\n", *arg);
        vrc = add_network(*arg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
486 487 488 489 490
        if(vrc == NULL)
            goto fail;
        SHIFT();
    }

491
#ifndef NO_LOCAL_INTERFACE
492 493 494 495 496 497
    if(local_server_port >= 0) {
        local_server_socket = tcp_server_socket(local_server_port, 1);
        if(local_server_socket < 0) {
            perror("local_server_socket");
            goto fail;
        }
498
    }
499
#endif
500

501
    init_signals();
502
    resize_receive_buffer(1500);
503
    check_networks();
504 505 506
    if(receive_buffer == NULL)
        goto fail;

507 508 509
    rc = check_xroutes(0);
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check exported routes.\n");
510
    kernel_routes_changed = 0;
511 512
    kernel_link_changed = 0;
    kernel_addr_changed = 0;
513
    kernel_dump_time = now.tv_sec + roughly(30);
514
    schedule_neighbours_check(5000, 1);
515 516
    expiry_time = now.tv_sec + roughly(30);
    source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
517

518
    /* Make some noise so that others notice us */
519 520
    FOR_ALL_NETS(net) {
        if(!net->up)
521
            continue;
522
        /* Apply jitter before we send the first message. */
523
        usleep(roughly(10000));
524
        gettime(&now);
525 526 527
        send_hello(net);
        send_self_update(net, 0);
        send_request(net, NULL, 0, 0, 0, 0);
528
        flushupdates();
529
        flushbuf(net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
530 531 532 533 534 535 536 537
    }

    debugf("Entering main loop.\n");

    while(1) {
        struct timeval tv;
        fd_set readfds;

538
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
539

540
        tv = check_neighbours_timeout;
541
        timeval_min_sec(&tv, expiry_time);
542
        timeval_min_sec(&tv, source_expiry_time);
543
        timeval_min_sec(&tv, kernel_dump_time);
544
        timeval_min(&tv, &resend_time);
545 546
        FOR_ALL_NETS(net) {
            if(!net->up)
547
                continue;
548 549 550 551 552
            timeval_min(&tv, &net->flush_timeout);
            timeval_min(&tv, &net->hello_timeout);
            if(!network_idle(net)) {
                timeval_min(&tv, &net->self_update_timeout);
                timeval_min(&tv, &net->update_timeout);
553
            }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
554
        }
555
        timeval_min(&tv, &update_flush_timeout);
556
        timeval_min(&tv, &unicast_flush_timeout);
557
        FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
558
        if(timeval_compare(&tv, &now) > 0) {
559
            int maxfd = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
560 561
            timeval_minus(&tv, &tv, &now);
            FD_SET(protocol_socket, &readfds);
562
            maxfd = MAX(maxfd, protocol_socket);
563
            if(kernel_socket < 0) kernel_setup_socket(1);
564
            if(kernel_socket >= 0) {
565
                FD_SET(kernel_socket, &readfds);
566 567
                maxfd = MAX(maxfd, kernel_socket);
            }
568
#ifndef NO_LOCAL_INTERFACE
569 570 571 572 573 574 575
            if(local_socket >= 0) {
                FD_SET(local_socket, &readfds);
                maxfd = MAX(maxfd, local_socket);
            } else if(local_server_socket >= 0) {
                FD_SET(local_server_socket, &readfds);
                maxfd = MAX(maxfd, local_server_socket);
            }
576
#endif
577
            rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
578
            if(rc < 0) {
579
                if(errno != EINTR) {
580
                    perror("select");
581
                    sleep(1);
582
                }
583 584
                rc = 0;
                FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
585 586 587
            }
        }

588
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
589 590 591 592

        if(exiting)
            break;

593
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds))
594
            kernel_callback(kernel_routes_callback, NULL);
595

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
596
        if(FD_ISSET(protocol_socket, &readfds)) {
597 598 599
            rc = babel_recv(protocol_socket,
                            receive_buffer, receive_buffer_size,
                            (struct sockaddr*)&sin6, sizeof(sin6));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
600 601 602
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
603
                    sleep(1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
604 605
                }
            } else {
606 607
                FOR_ALL_NETS(net) {
                    if(!net->up)
608
                        continue;
609 610
                    if(net->ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, net,
611 612 613
                                     receive_buffer, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
                                                    receive_buffer_size);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
614 615 616 617 618 619
                        break;
                    }
                }
            }
        }

620
#ifndef NO_LOCAL_INTERFACE
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
        if(local_server_socket >= 0 &&
           FD_ISSET(local_server_socket, &readfds)) {
            if(local_socket >= 0) {
                close(local_socket);
                local_socket = -1;
            }
            local_socket = accept(local_server_socket, NULL, NULL);
            if(local_socket < 0) {
                if(errno != EINTR && errno != EAGAIN)
                    perror("accept(local_server_socket)");
            } else {
                local_dump();
            }
        }

        if(local_socket >= 0 && FD_ISSET(local_socket, &readfds)) {
            rc = local_read(local_socket);
            if(rc <= 0) {
                if(rc < 0)
                    perror("read(local_socket)");
                close(local_socket);
                local_socket = -1;
            }
        }
645
#endif
646

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
647 648
        if(changed) {
            kernel_dump_time = now.tv_sec;
649
            check_neighbours_timeout = now;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
650
            expiry_time = now.tv_sec;
651 652 653 654 655
            rc = reopen_logfile();
            if(rc < 0) {
                perror("reopen_logfile");
                break;
            }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
656 657 658
            changed = 0;
        }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
659
        if(kernel_link_changed || kernel_addr_changed) {
660 661 662 663
            check_networks();
            kernel_link_changed = 0;
        }

664 665
        if(kernel_routes_changed || kernel_addr_changed ||
           now.tv_sec >= kernel_dump_time) {
666 667
            rc = check_xroutes(1);
            if(rc < 0)
668
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
669
            kernel_routes_changed = kernel_addr_changed = 0;
670
            if(kernel_socket >= 0)
671
                kernel_dump_time = now.tv_sec + roughly(300);
672
            else
673
                kernel_dump_time = now.tv_sec + roughly(30);
674 675
        }

676
        if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
677 678
            int msecs;
            msecs = check_neighbours();
679 680
            msecs = MAX(msecs, 10);
            schedule_neighbours_check(msecs, 1);
681 682 683
        }

        if(now.tv_sec >= expiry_time) {
684
            check_networks();
685
            expire_routes();
686
            expire_resend();
687
            expiry_time = now.tv_sec + roughly(30);
688 689
        }

690 691
        if(now.tv_sec >= source_expiry_time) {
            expire_sources();
692
            source_expiry_time = now.tv_sec + roughly(300);
693 694
        }

695 696
        FOR_ALL_NETS(net) {
            if(!net->up)
697
                continue;
698 699 700 701 702 703 704
            if(timeval_compare(&now, &net->hello_timeout) >= 0)
                send_hello(net);
            if(!network_idle(net)) {
                if(timeval_compare(&now, &net->update_timeout) >= 0)
                    send_update(net, 0, NULL, 0);
                if(timeval_compare(&now, &net->self_update_timeout) >= 0)
                    send_self_update(net, 0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
705 706 707
            }
        }

708 709 710 711
        if(resend_time.tv_sec != 0) {
            if(timeval_compare(&now, &resend_time) >= 0)
                do_resend();
        }
712

713
        if(update_flush_timeout.tv_sec != 0) {
714
            if(timeval_compare(&now, &update_flush_timeout) >= 0)
715 716 717
                flushupdates();
        }

718 719 720 721 722
        if(unicast_flush_timeout.tv_sec != 0) {
            if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
                flush_unicast(1);
        }

723 724
        FOR_ALL_NETS(net) {
            if(!net->up)
725
                continue;
726 727 728
            if(net->flush_timeout.tv_sec != 0) {
                if(timeval_compare(&now, &net->flush_timeout) >= 0)
                    flushbuf(net);
729 730 731
            }
        }

732
        if(debug || dumping) {
733
            dump_tables(stdout);
734 735
            dumping = 0;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
736 737 738
    }

    debugf("Exiting...\n");
739
    usleep(roughly(10000));
740
    gettime(&now);
741

742
    /* Uninstall and retract all routes. */
743 744 745 746
    while(numroutes > 0) {
        if(routes[0].installed) {
            uninstall_route(&routes[0]);
            send_update(NULL, 1, routes[0].src->prefix, routes[0].src->plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
747
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
748
        /* We need to flush the route so network_up won't reinstall it */
749
        flush_route(&routes[0]);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
750
    }
751 752 753 754 755 756 757 758
    while(numxroutes > 0) {
        xroutes[0].metric = INFINITY;
        send_update(NULL, 1, xroutes[0].prefix, xroutes[0].plen);
        flush_xroute(&xroutes[0]);
    }

    flushupdates();

759 760
    FOR_ALL_NETS(net) {
        if(!net->up)
761
            continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
762
        /* Make sure that we expire quickly from our neighbours'
763 764
           association caches. */
        send_hello_noupdate(net, 10);
765
        flushbuf(net);
766
        usleep(roughly(1000));
767
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
768
    }
769
    FOR_ALL_NETS(net) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
770
        if(!net->up)
771
            continue;
772
        /* Make sure they got it. */
773 774
        send_hello_noupdate(net, 1);
        flushbuf(net);
775
        usleep(roughly(10000));
776
        gettime(&now);
777
        network_up(net, 0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
778
    }
779
    kernel_setup_socket(0);
780
    kernel_setup(0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
781 782 783

    fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if(fd < 0) {
784
        perror("creat(babel-state)");
785
        unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
786
    } else {
787
        struct timeval realnow;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
788
        char buf[100];
789
        gettimeofday(&realnow, NULL);
790
        rc = snprintf(buf, 100, "%s %d %ld\n",
791 792
                      format_address(myid), (int)myseqno,
                      (long)realnow.tv_sec);
793 794
        if(rc < 0 || rc >= 100) {
            fprintf(stderr, "write(babel-state): overflow.\n");
795
            unlink(state_file);
796 797 798 799 800 801 802
        } else {
            rc = write(fd, buf, rc);
            if(rc < 0) {
                perror("write(babel-state)");
                unlink(state_file);
            }
            fsync(fd);
803
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
804 805
        close(fd);
    }
806 807
    if(pidfile)
        unlink(pidfile);
808
    debugf("Done.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
809 810 811 812 813 814 815
    return 0;

 syntax:
    fprintf(stderr,
            "Syntax: %s "
            "[-m multicast_address] [-p port] [-S state-file]\n"
            "                "
816
            "[-h hello] [-H wired_hello] [-i idle_hello] [-u update]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
817
            "                "
818
            "[-k metric] [-s] [-p] [-l] [-w] [-d level] [-g port]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
819
            "                "
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
820
            "[-t table] [-T table] [-c file] [-C statement]\n"
821
            "                "
822 823
            "[-D] [-L logfile] [-I pidfile]\n"
            "                "
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
824
            "[id] interface...\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
825 826 827 828
            argv[0]);
    exit(1);

 fail:
829 830 831
    if(pidfile)
        unlink(pidfile);
 fail_nopid:
832 833
    FOR_ALL_NETS(net) {
        if(!net->up)
834
            continue;
835
        network_up(net, 0);
836
    }
837
    kernel_setup_socket(0);
838
    kernel_setup(0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
839 840 841
    exit(1);
}

842 843 844 845 846 847
/* Schedule a neighbours check after roughly 3/2 msecs have elapsed. */
void
schedule_neighbours_check(int msecs, int override)
{
    struct timeval timeout;

848
    timeval_plus_msec(&timeout, &now, roughly(msecs * 3 / 2));
849 850 851 852 853 854
    if(override)
        check_neighbours_timeout = timeout;
    else
        timeval_min(&check_neighbours_timeout, &timeout);
}

855 856 857 858 859 860 861 862
void
resize_receive_buffer(int size)
{
    if(size <= receive_buffer_size)
        return;

    if(receive_buffer == NULL) {
        receive_buffer = malloc(size);
863 864
        if(receive_buffer == NULL) {
            perror("malloc(receive_buffer)");
865
            return;
866 867 868 869 870 871 872 873 874
        }
        receive_buffer_size = size;
    } else {
        unsigned char *new;
        new = realloc(receive_buffer, size);
        if(new == NULL) {
            perror("realloc(receive_buffer)");
            return;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
875
        receive_buffer = new;
876 877 878 879
        receive_buffer_size = size;
    }
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
880 881 882 883 884 885
static void
sigexit(int signo)
{
    exiting = 1;
}

886 887 888 889 890 891
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
892 893 894 895 896 897
static void
sigchanged(int signo)
{
    changed = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
static void
init_signals(void)
{
    struct sigaction sa;
    sigset_t ss;

    sigemptyset(&ss);
    sa.sa_handler = sigexit;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGTERM, &sa, NULL);

    sigemptyset(&ss);
    sa.sa_handler = sigexit;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGHUP, &sa, NULL);

    sigemptyset(&ss);
    sa.sa_handler = sigexit;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);
921

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
922 923 924 925 926 927
    sigemptyset(&ss);
    sa.sa_handler = SIG_IGN;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

928 929 930 931 932
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
933

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
934 935 936 937 938 939
    sigemptyset(&ss);
    sa.sa_handler = sigchanged;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR2, &sa, NULL);

940 941 942 943 944 945 946
#ifdef SIGINFO
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGINFO, &sa, NULL);
#endif
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
947 948
}

949 950 951
static void
dump_tables(FILE *out)
{
952
    struct neighbour *neigh;
953 954 955
    int i;

    fprintf(out, "\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
956

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
957
    fprintf(out, "My id %s seqno %d\n", format_address(myid), myseqno);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
958

959 960
    FOR_ALL_NEIGHBOURS(neigh) {
        fprintf(out, "Neighbour %s ", format_address(neigh->id));
961
        fprintf(out, "at %s dev %s reach %04x rxcost %d txcost %d%s.\n",
962 963 964 965 966 967
                format_address(neigh->address),
                neigh->network->ifname,
                neigh->reach,
                neighbour_rxcost(neigh),
                neigh->txcost,
                neigh->network->up ? "" : " (down)");
968 969
    }
    for(i = 0; i < numxroutes; i++) {
970
        fprintf(out, "%s metric %d (exported)\n",
971
                format_prefix(xroutes[i].prefix, xroutes[i].plen),
972
                xroutes[i].metric);
973 974
    }
    for(i = 0; i < numroutes; i++) {
975 976
        int id =
            routes[i].src->plen != 128 ||
977
            memcmp(routes[i].src->prefix, routes[i].src->id, 16) != 0;
978 979 980
        const unsigned char *nexthop =
            memcmp(routes[i].nexthop, routes[i].neigh->address, 16) == 0 ?
            NULL : routes[i].nexthop;
981
        fprintf(out, "%s metric %d refmetric %d %s%s seqno %d age %d "
982
                "via %s neigh %s%s%s%s\n",
983
                format_prefix(routes[i].src->prefix, routes[i].src->plen),
984 985
                routes[i].metric, routes[i].refmetric,
                id ? "id " : "",
986
                id ? format_address(routes[i].src->id) : "",
987
                (int)routes[i].seqno,
988
                (int)(now.tv_sec - routes[i].time),
989 990
                routes[i].neigh->network->ifname,
                format_address(routes[i].neigh->address),
991 992
                nexthop ? " nexthop " : "",
                nexthop ? format_address(nexthop) : "",
993 994
                routes[i].installed ? " (installed)" :
                route_feasible(&routes[i]) ? " (feasible)" : "");
995 996 997 998
    }
    fflush(out);
}

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
static int
reopen_logfile()
{
    int lfd, rc;

    if(logfile == NULL)
        return 0;

    lfd = open(logfile, O_CREAT | O_WRONLY | O_APPEND, 0644);
    if(lfd < 0)
        return -1;

    fflush(stdout);
    fflush(stderr);

    rc = dup2(lfd, 1);
    if(rc < 0)
        return -1;

    rc = dup2(lfd, 2);
    if(rc < 0)
        return -1;

    if(lfd > 2)
        close(lfd);

    return 1;
}

1028
static int
1029
kernel_routes_callback(int changed, void *closure)
1030
{
1031 1032 1033 1034 1035 1036
    if (changed & CHANGE_LINK)
        kernel_link_changed = 1;
    if (changed & CHANGE_ADDR)
        kernel_addr_changed = 1;
    if (changed & CHANGE_ROUTE)
        kernel_routes_changed = 1;
1037
    return 1;
1038
}