babel.c 27.4 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 "config.h"
53
#include "local.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
54 55 56

struct timeval now;

57
unsigned char myid[8];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
58 59
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;
68
int do_daemonise = 0;
69
char *logfile = NULL, *pidfile = "/var/run/babel.pid";
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
70

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

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
74
const unsigned char zeroes[16] = {0};
75 76 77
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
78

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

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

89 90
struct timeval check_neighbours_timeout;

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

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

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

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

113 114 115 116 117 118 119 120 121 122 123 124 125 126
    gettime(&now);

    rfd = open("/dev/urandom", O_RDONLY);
    if(rfd < 0) {
        perror("open(random)");
    } else {
        rc = read(rfd, &seed, sizeof(unsigned int));
        if(rc < sizeof(unsigned int)) {
            perror("read(random)");
        }
    }
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

127
    parse_address("ff02::cca6:c0f9:e182:5373", protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
128 129
    protocol_port = 8475;

130
#define SHIFT() do { arg++; } while(0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
131 132 133 134
#define SHIFTE() do { arg++; if(*arg == NULL) goto syntax; } while(0)

    arg = argv;

135
    SHIFT();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
136

137
    while(*arg && (*arg)[0] == '-') {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
138 139 140 141 142
        if(strcmp(*arg, "--") == 0) {
            SHIFTE();
            break;
        } else if(strcmp(*arg, "-m") == 0) {
            SHIFTE();
143
            rc = parse_address(*arg, protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
            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();
161
            wireless_hello_interval = parse_msec(*arg);
162 163
            if(wireless_hello_interval <= 0 ||
               wireless_hello_interval > 0xFFFF * 10)
164
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
165 166
        } else if(strcmp(*arg, "-H") == 0) {
            SHIFTE();
167
            wired_hello_interval = parse_msec(*arg);
168
            if(wired_hello_interval <= 0 || wired_hello_interval > 0xFFFF * 10)
169
                goto syntax;
170 171
        } else if(strcmp(*arg, "-i") == 0) {
            SHIFTE();
172
            idle_hello_interval = parse_msec(*arg);
173
            if(idle_hello_interval <= 0 || idle_hello_interval > 0xFFFF * 10)
174
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
175 176 177
        } else if(strcmp(*arg, "-k") == 0) {
            SHIFTE();
            kernel_metric = atoi(*arg);
178
            if(kernel_metric < 0 || kernel_metric > 0xFFFF)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
179 180 181 182 183 184 185 186 187 188 189
                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);
190
#ifndef NO_LOCAL_INTERFACE
191 192 193
        } else if(strcmp(*arg, "-g") == 0) {
            SHIFTE();
            local_server_port = atoi(*arg);
194
#endif
195 196
        } else if(strcmp(*arg, "-l") == 0) {
            link_detect = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
197 198
        } else if(strcmp(*arg, "-w") == 0) {
            all_wireless = 1;
199 200 201 202 203 204 205 206 207 208
        } 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;
209 210 211 212 213 214 215 216 217 218 219
        } else if(strcmp(*arg, "-c") == 0) {
            SHIFTE();
            config_file = *arg;
        } else if(strcmp(*arg, "-C") == 0) {
            SHIFTE();
            rc = parse_config_from_string(*arg);
            if(rc < 0) {
                fprintf(stderr,
                        "Couldn't parse configuration from command line.\n");
                exit(1);
            }
220 221 222 223 224 225 226 227
        } 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
228 229 230
        } else {
            goto syntax;
        }
231
        SHIFT();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
232 233
    }

234 235 236 237 238 239 240 241 242

    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,
243 244
                    "Couldn't parse configuration from file %s.\n",
                    config_file);
245 246 247 248
            exit(1);
        }
    }

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

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

257 258 259 260 261
    if(do_daemonise) {
        if(logfile == NULL)
            logfile = "/var/log/babel.log";
    }

262 263 264
    rc = reopen_logfile();
    if(rc < 0) {
        perror("reopen_logfile()");
265
        exit(1);
266 267 268 269 270
    }

    fd = open("/dev/null", O_RDONLY);
    if(fd < 0) {
        perror("open(null)");
271
        exit(1);
272 273 274 275 276
    }

    rc = dup2(fd, 0);
    if(rc < 0) {
        perror("dup2(null, 0)");
277
        exit(1);
278 279 280 281 282 283 284 285
    }

    close(fd);

    if(do_daemonise) {
        rc = daemonise();
        if(rc < 0) {
            perror("daemonise");
286
            exit(1);
287 288 289
        }
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
290
    if(pidfile && pidfile[0] != '\0') {
291 292 293 294 295 296
        int pfd, len;
        char buf[100];

        len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
        if(len < 0 || len >= 100) {
            perror("snprintf(getpid)");
297
            exit(1);
298 299 300 301
        }

        pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
        if(pfd < 0) {
302 303 304 305
            char buf[40];
            snprintf(buf, 40, "creat(%s)", pidfile);
            buf[39] = '\0';
            perror(buf);
306
            exit(1);
307 308 309 310 311
        }

        rc = write(pfd, buf, len);
        if(rc < len) {
            perror("write(pidfile)");
312
            goto fail_pid;
313 314 315 316 317
        }

        close(pfd);
    }

318 319 320
    rc = kernel_setup(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
321
        goto fail_pid;
322 323 324 325 326 327
    }

    rc = kernel_setup_socket(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup_socket failed.\n");
        kernel_setup(0);
328
        goto fail_pid;
329
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
330

331
    if(*arg) {
332 333 334 335
        unsigned char dummy[16];
        rc = parse_address(*arg, dummy, NULL);
        if(rc >= 0) {
            fprintf(stderr, "Warning: obsolete router-id given.\n");
336
            SHIFT();
337 338 339
        }
    }

340 341 342
    rc = finalise_config();
    if(rc < 0) {
        fprintf(stderr, "Couldn't finalise configuration.\n");
343
        goto fail;
344 345
    }

346 347
    while(*arg) {
        debugf("Adding network %s.\n", *arg);
348
        vrc = add_network(*arg, NULL);
349
        if(vrc == NULL)
350
            goto fail;
351 352 353
        SHIFT();
    }

354 355 356 357 358
    if(networks == NULL) {
        fprintf(stderr, "Eek... asked to run on no interfaces!\n");
        goto fail;
    }

359 360 361 362 363 364 365 366 367 368
    FOR_ALL_NETS(net) {
        /* net->ifindex is not necessarily valid at this point */
        int ifindex = if_nametoindex(net->ifname);
        if(ifindex > 0) {
            unsigned char eui[8];
            rc = if_eui64(net->ifname, ifindex, eui);
            if(rc < 0)
                continue;
            memcpy(myid, eui, 8);
            goto have_id;
369 370 371
        }
    }

372
    /* We failed to get a global EUI64 from the interfaces we were given.
373 374
       Let's try to find an interface with a MAC address. */
    for(i = 1; i < 256; i++) {
375 376 377 378 379 380 381 382 383 384 385 386
        char buf[IF_NAMESIZE], *ifname;
        unsigned char eui[8];
        ifname = if_indextoname(i, buf);
        if(ifname == NULL)
            continue;
        rc = if_eui64(ifname, i, eui);
        if(rc < 0)
            continue;
        memcpy(myid, eui, 8);
        goto have_id;
    }

387 388 389 390 391
    fprintf(stderr,
            "Warning: couldn't find router id -- using random value.\n");
    if(rfd >= 0) {
        rc = read(rfd, myid, 8);
        if(rc < 8) {
392
            perror("read(random)");
393
            goto fail;
394
        }
395 396
    } else {
        goto fail;
397
    }
398 399
    /* Clear group and global bits */
    myid[0] &= ~3;
400

401 402 403 404
 have_id:
    if(rfd >= 0)
        close(rfd);
    rfd = -1;
405

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
406
    reboot_time = now.tv_sec;
407
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
408 409 410

    fd = open(state_file, O_RDONLY);
    if(fd < 0 && errno != ENOENT)
411
        perror("open(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
412
    rc = unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
413
    if(fd >= 0 && rc < 0) {
414
        perror("unlink(babel-state)");
415
        /* If we couldn't unlink it, it's probably stale. */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
416 417 418 419 420
        close(fd);
        fd = -1;
    }
    if(fd >= 0) {
        char buf[100];
421 422 423
        char buf2[100];
        int s;
        long t;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
424 425
        rc = read(fd, buf, 99);
        if(rc < 0) {
426
            perror("read(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
427 428
        } else {
            buf[rc] = '\0';
429 430
            rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
            if(rc == 3 && s >= 0 && s <= 0xFFFF) {
431 432
                unsigned char sid[8];
                rc = parse_eui64(buf2, sid);
433 434 435
                if(rc < 0) {
                    fprintf(stderr, "Couldn't parse babel-state.\n");
                } else {
436
                    struct timeval realnow;
437 438
                    debugf("Got %s %d %ld from babel-state.\n",
                           format_address(sid), s, t);
439
                    gettimeofday(&realnow, NULL);
440
                    if(memcmp(sid, myid, 8) == 0)
441 442 443
                        myseqno = seqno_plus(s, 1);
                    else
                        fprintf(stderr, "ID mismatch in babel-state.\n");
444 445 446
                    /* Convert realtime into monotonic time. */
                    if(t >= 1176800000L && t <= realnow.tv_sec)
                        reboot_time = now.tv_sec - (realnow.tv_sec - t);
447
                }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
448
            } else {
449
                fprintf(stderr, "Couldn't parse babel-state.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
450 451 452
            }
        }
        close(fd);
453
        fd = -1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
454 455
    }

456
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
457 458 459 460 461
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

462
#ifndef NO_LOCAL_INTERFACE
463 464 465 466 467 468
    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;
        }
469
    }
470
#endif
471

472
    init_signals();
473 474 475
    rc = resize_receive_buffer(1500);
    if(rc < 0)
        goto fail;
476
    check_networks();
477 478 479
    if(receive_buffer == NULL)
        goto fail;

480 481 482
    rc = check_xroutes(0);
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check exported routes.\n");
483
    kernel_routes_changed = 0;
484 485
    kernel_link_changed = 0;
    kernel_addr_changed = 0;
486
    kernel_dump_time = now.tv_sec + roughly(30);
487
    schedule_neighbours_check(5000, 1);
488 489
    expiry_time = now.tv_sec + roughly(30);
    source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
490

491 492
    /* Make some noise so that others notice us, and send retractions in
       case we were restarted recently */
493
    FOR_ALL_NETS(net) {
494
        if(!net_up(net))
495
            continue;
496
        /* Apply jitter before we send the first message. */
497
        usleep(roughly(10000));
498
        gettime(&now);
499
        send_hello(net);
500
        send_wildcard_retraction(net);
501 502 503 504 505 506 507 508 509
    }

    FOR_ALL_NETS(net) {
        if(!net_up(net))
            continue;
        usleep(roughly(10000));
        gettime(&now);
        send_hello(net);
        send_wildcard_retraction(net);
510
        send_self_update(net);
511
        send_request(net, NULL, 0);
512
        flushupdates(net);
513
        flushbuf(net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
514 515 516 517 518 519 520 521
    }

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

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

522
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
523

524
        tv = check_neighbours_timeout;
525
        timeval_min_sec(&tv, expiry_time);
526
        timeval_min_sec(&tv, source_expiry_time);
527
        timeval_min_sec(&tv, kernel_dump_time);
528
        timeval_min(&tv, &resend_time);
529
        FOR_ALL_NETS(net) {
530
            if(!net_up(net))
531
                continue;
532 533
            timeval_min(&tv, &net->flush_timeout);
            timeval_min(&tv, &net->hello_timeout);
534
            timeval_min(&tv, &net->update_timeout);
535
            timeval_min(&tv, &net->update_flush_timeout);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
536
        }
537
        timeval_min(&tv, &unicast_flush_timeout);
538
        FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
539
        if(timeval_compare(&tv, &now) > 0) {
540
            int maxfd = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
541 542
            timeval_minus(&tv, &tv, &now);
            FD_SET(protocol_socket, &readfds);
543
            maxfd = MAX(maxfd, protocol_socket);
544
            if(kernel_socket < 0) kernel_setup_socket(1);
545
            if(kernel_socket >= 0) {
546
                FD_SET(kernel_socket, &readfds);
547 548
                maxfd = MAX(maxfd, kernel_socket);
            }
549
#ifndef NO_LOCAL_INTERFACE
550 551 552 553 554 555 556
            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);
            }
557
#endif
558
            rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
559
            if(rc < 0) {
560
                if(errno != EINTR) {
561
                    perror("select");
562
                    sleep(1);
563
                }
564 565
                rc = 0;
                FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
566 567 568
            }
        }

569
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
570 571 572 573

        if(exiting)
            break;

574
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds))
575
            kernel_callback(kernel_routes_callback, NULL);
576

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
577
        if(FD_ISSET(protocol_socket, &readfds)) {
578 579 580
            rc = babel_recv(protocol_socket,
                            receive_buffer, receive_buffer_size,
                            (struct sockaddr*)&sin6, sizeof(sin6));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
581 582 583
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
584
                    sleep(1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
585 586
                }
            } else {
587
                FOR_ALL_NETS(net) {
588
                    if(!net_up(net))
589
                        continue;
590 591
                    if(net->ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, net,
592 593 594
                                     receive_buffer, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
                                                    receive_buffer_size);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
595 596 597 598 599 600
                        break;
                    }
                }
            }
        }

601
#ifndef NO_LOCAL_INTERFACE
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        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;
            }
        }
626
#endif
627

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
628 629
        if(changed) {
            kernel_dump_time = now.tv_sec;
630
            check_neighbours_timeout = now;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
631
            expiry_time = now.tv_sec;
632 633 634 635 636
            rc = reopen_logfile();
            if(rc < 0) {
                perror("reopen_logfile");
                break;
            }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
637 638 639
            changed = 0;
        }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
640
        if(kernel_link_changed || kernel_addr_changed) {
641 642 643 644
            check_networks();
            kernel_link_changed = 0;
        }

645 646
        if(kernel_routes_changed || kernel_addr_changed ||
           now.tv_sec >= kernel_dump_time) {
647 648
            rc = check_xroutes(1);
            if(rc < 0)
649
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
650
            kernel_routes_changed = kernel_addr_changed = 0;
651
            if(kernel_socket >= 0)
652
                kernel_dump_time = now.tv_sec + roughly(300);
653
            else
654
                kernel_dump_time = now.tv_sec + roughly(30);
655 656
        }

657
        if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
658 659
            int msecs;
            msecs = check_neighbours();
660 661
            msecs = MAX(msecs, 10);
            schedule_neighbours_check(msecs, 1);
662 663 664
        }

        if(now.tv_sec >= expiry_time) {
665
            check_networks();
666
            expire_routes();
667
            expire_resend();
668
            expiry_time = now.tv_sec + roughly(30);
669 670
        }

671 672
        if(now.tv_sec >= source_expiry_time) {
            expire_sources();
673
            source_expiry_time = now.tv_sec + roughly(300);
674 675
        }

676
        FOR_ALL_NETS(net) {
677
            if(!net_up(net))
678
                continue;
679 680
            if(timeval_compare(&now, &net->hello_timeout) >= 0)
                send_hello(net);
681 682
            if(timeval_compare(&now, &net->update_timeout) >= 0)
                send_update(net, 0, NULL, 0);
683 684
            if(timeval_compare(&now, &net->update_flush_timeout) >= 0)
                flushupdates(net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
685 686
        }

687 688 689 690
        if(resend_time.tv_sec != 0) {
            if(timeval_compare(&now, &resend_time) >= 0)
                do_resend();
        }
691

692 693 694 695 696
        if(unicast_flush_timeout.tv_sec != 0) {
            if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
                flush_unicast(1);
        }

697
        FOR_ALL_NETS(net) {
698
            if(!net_up(net))
699
                continue;
700 701 702
            if(net->flush_timeout.tv_sec != 0) {
                if(timeval_compare(&now, &net->flush_timeout) >= 0)
                    flushbuf(net);
703 704 705
            }
        }

706
        if(UNLIKELY(debug || dumping)) {
707
            dump_tables(stdout);
708 709
            dumping = 0;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
710 711 712
    }

    debugf("Exiting...\n");
713
    usleep(roughly(10000));
714
    gettime(&now);
715

716
    /* Uninstall and flush all routes. */
717
    while(numroutes > 0) {
718
        if(routes[0].installed)
719
            uninstall_route(&routes[0]);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
720
        /* We need to flush the route so network_up won't reinstall it */
721
        flush_route(&routes[0]);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
722
    }
723

724
    FOR_ALL_NETS(net) {
725
        if(!net_up(net))
726
            continue;
727
        send_wildcard_retraction(net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
728
        /* Make sure that we expire quickly from our neighbours'
729 730
           association caches. */
        send_hello_noupdate(net, 10);
731
        flushbuf(net);
732
        usleep(roughly(1000));
733
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
734
    }
735
    FOR_ALL_NETS(net) {
736
        if(!net_up(net))
737
            continue;
738
        /* Make sure they got it. */
739
        send_wildcard_retraction(net);
740 741
        send_hello_noupdate(net, 1);
        flushbuf(net);
742
        usleep(roughly(10000));
743
        gettime(&now);
744
        network_up(net, 0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
745
    }
746
    kernel_setup_socket(0);
747
    kernel_setup(0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
748 749 750

    fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if(fd < 0) {
751
        perror("creat(babel-state)");
752
        unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
753
    } else {
754
        struct timeval realnow;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
755
        char buf[100];
756
        gettimeofday(&realnow, NULL);
757
        rc = snprintf(buf, 100, "%s %d %ld\n",
758
                      format_eui64(myid), (int)myseqno,
759
                      (long)realnow.tv_sec);
760 761
        if(rc < 0 || rc >= 100) {
            fprintf(stderr, "write(babel-state): overflow.\n");
762
            unlink(state_file);
763 764 765 766 767 768 769
        } else {
            rc = write(fd, buf, rc);
            if(rc < 0) {
                perror("write(babel-state)");
                unlink(state_file);
            }
            fsync(fd);
770
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
771 772
        close(fd);
    }
773 774
    if(pidfile)
        unlink(pidfile);
775
    debugf("Done.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
776 777 778 779 780 781 782
    return 0;

 syntax:
    fprintf(stderr,
            "Syntax: %s "
            "[-m multicast_address] [-p port] [-S state-file]\n"
            "                "
783
            "[-h hello] [-H wired_hello] [-i idle_hello] [-u update]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
784
            "                "
785
            "[-k metric] [-s] [-p] [-l] [-w] [-d level] [-g port]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
786
            "                "
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
787
            "[-t table] [-T table] [-c file] [-C statement]\n"
788
            "                "
789 790
            "[-D] [-L logfile] [-I pidfile]\n"
            "                "
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
791
            "[id] interface...\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
792 793 794 795
            argv[0]);
    exit(1);

 fail:
796
    FOR_ALL_NETS(net) {
797
        if(!net_up(net))
798
            continue;
799
        network_up(net, 0);
800
    }
801
    kernel_setup_socket(0);
802
    kernel_setup(0);
803 804 805
 fail_pid:
    if(pidfile)
        unlink(pidfile);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
806 807 808
    exit(1);
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
809
/* Schedule a neighbours check after roughly 3/2 times msecs have elapsed. */
810 811 812 813 814
void
schedule_neighbours_check(int msecs, int override)
{
    struct timeval timeout;

815
    timeval_plus_msec(&timeout, &now, roughly(msecs * 3 / 2));
816 817 818 819 820 821
    if(override)
        check_neighbours_timeout = timeout;
    else
        timeval_min(&check_neighbours_timeout, &timeout);
}

822
int
823 824 825
resize_receive_buffer(int size)
{
    if(size <= receive_buffer_size)
826
        return 0;
827 828 829

    if(receive_buffer == NULL) {
        receive_buffer = malloc(size);
830 831
        if(receive_buffer == NULL) {
            perror("malloc(receive_buffer)");
832
            return -1;
833 834 835 836 837 838 839
        }
        receive_buffer_size = size;
    } else {
        unsigned char *new;
        new = realloc(receive_buffer, size);
        if(new == NULL) {
            perror("realloc(receive_buffer)");
840
            return -1;
841
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
842
        receive_buffer = new;
843 844
        receive_buffer_size = size;
    }
845
    return 1;
846 847
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
848 849 850 851 852 853
static void
sigexit(int signo)
{
    exiting = 1;
}

854 855 856 857 858 859
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
860 861 862 863 864 865
static void
sigchanged(int signo)
{
    changed = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
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);
889

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
890 891 892 893 894 895
    sigemptyset(&ss);
    sa.sa_handler = SIG_IGN;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

896 897 898 899 900
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
901

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
902 903 904 905 906 907
    sigemptyset(&ss);
    sa.sa_handler = sigchanged;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR2, &sa, NULL);

908 909 910 911 912 913 914
#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
915 916
}

917 918 919
static void
dump_tables(FILE *out)
{
920
    struct neighbour *neigh;
921 922 923
    int i;

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

925
    fprintf(out, "My id %s seqno %d\n", format_eui64(myid), myseqno);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
926

927
    FOR_ALL_NEIGHBOURS(neigh) {
928
        fprintf(out, "Neighbour %s dev %s reach %04x rxcost %d txcost %d%s.\n",
929 930 931 932 933
                format_address(neigh->address),
                neigh->network->ifname,
                neigh->reach,
                neighbour_rxcost(neigh),
                neigh->txcost,
934
                net_up(neigh->network) ? "" : " (down)");
935 936
    }
    for(i = 0; i < numxroutes; i++) {
937
        fprintf(out, "%s metric %d (exported)\n",
938
                format_prefix(xroutes[i].prefix, xroutes[i].plen),
939
                xroutes[i].metric);
940 941
    }
    for(i = 0; i < numroutes; i++) {
942 943 944
        const unsigned char *nexthop =
            memcmp(routes[i].nexthop, routes[i].neigh->address, 16) == 0 ?
            NULL : routes[i].nexthop;
945
        fprintf(out, "%s metric %d refmetric %d id %s seqno %d age %d "
946
                "via %s neigh %s%s%s%s\n",
947
                format_prefix(routes[i].src->prefix, routes[i].src->plen),
948
                routes[i].metric, routes[i].refmetric,
949
                format_eui64(routes[i].src->id),
950
                (int)routes[i].seqno,
951
                (int)(now.tv_sec - routes[i].time),
952 953
                routes[i].neigh->network->ifname,
                format_address(routes[i].neigh->address),
954 955
                nexthop ? " nexthop " : "",
                nexthop ? format_address(nexthop) : "",
956 957
                routes[i].installed ? " (installed)" :
                route_feasible(&routes[i]) ? " (feasible)" : "");
958 959 960 961
    }
    fflush(out);
}

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
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;
}

991
static int
992
kernel_routes_callback(int changed, void *closure)
993
{
994 995 996 997 998 999
    if (changed & CHANGE_LINK)
        kernel_link_changed = 1;
    if (changed & CHANGE_ADDR)
        kernel_addr_changed = 1;
    if (changed & CHANGE_ROUTE)
        kernel_routes_changed = 1;
1000
    return 1;
1001
}