babeld.c 28.1 KB
Newer Older
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1
/*
2
Copyright (c) 2007, 2008 by Juliusz Chroboczek
dermiste's avatar
dermiste committed
3
Copyright (c) 2010 by Vincent Gross
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
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 41

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>

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

struct timeval now;

58
unsigned char myid[8];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
59 60
int debug = 0;

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

63
int idle_time = 320;
64
int link_detect = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
65
int all_wireless = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
66 67
int wireless_hello_interval = -1;
int wired_hello_interval = -1;
68
int idle_hello_interval = -1;
69
int do_daemonise = 0;
70
char *logfile = NULL, *pidfile = "/var/run/babeld.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;

92
static volatile sig_atomic_t exiting = 0, dumping = 0, reopening = 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);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
100
static int reopen_logfile(void);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
101 102 103 104 105

int
main(int argc, char **argv)
{
    struct sockaddr_in6 sin6;
dermiste's avatar
dermiste committed
106
    int rc, fd, i, opt;
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
    void *vrc;
    unsigned int seed;
111
    struct interface *ifp;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
112

113 114
    gettime(&now);

115 116 117 118 119 120
    rc = read_random_bytes(&seed, sizeof(seed));
    if(rc < 0) {
        perror("read(random)");
        seed = 42;
    }

121 122 123
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

124
    parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
125
    protocol_port = 6696;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
126

127
    while(1) {
128
        opt = getopt(argc, argv, "m:p:h:H:i:k:A:PsuS:d:g:lwz:t:T:c:C:DL:I:");
129
        if(opt < 0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
130
            break;
131 132 133 134

        switch(opt) {
        case 'm':
            rc = parse_address(optarg, protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
135
            if(rc < 0)
136
                goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
137 138
            if(protocol_group[0] != 0xff) {
                fprintf(stderr,
139 140
                        "%s is not a multicast address\n", optarg);
                goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
141 142 143 144
            }
            if(protocol_group[1] != 2) {
                fprintf(stderr,
                        "Warning: %s is not a link-local multicast address\n",
145
                        optarg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
146
            }
147 148 149 150 151 152
            break;
        case 'p':
            protocol_port = atoi(optarg);
            break;
        case 'h':
            wireless_hello_interval = parse_msec(optarg);
153 154
            if(wireless_hello_interval <= 0 ||
               wireless_hello_interval > 0xFFFF * 10)
155 156 157 158
                goto usage;
            break;
        case 'H':
            wired_hello_interval = parse_msec(optarg);
159
            if(wired_hello_interval <= 0 || wired_hello_interval > 0xFFFF * 10)
160 161 162 163
                goto usage;
            break;
        case 'i':
            idle_hello_interval = parse_msec(optarg);
164
            if(idle_hello_interval <= 0 || idle_hello_interval > 0xFFFF * 10)
165 166 167 168
                goto usage;
            break;
        case 'k':
            kernel_metric = atoi(optarg);
169
            if(kernel_metric < 0 || kernel_metric > 0xFFFF)
170 171
                goto usage;
            break;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
172 173 174 175 176
        case 'A':
            allow_duplicates = atoi(optarg);
            if(allow_duplicates < 0 || allow_duplicates > 0xFFFF)
                goto usage;
            break;
177
        case 'P':
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
178
            parasitic = 1;
179 180
            break;
        case 's':
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
181
            split_horizon = 0;
182
            break;
183 184 185
        case 'u':
            keep_unfeasible = 1;
            break;
186 187 188 189 190 191 192 193 194 195 196
        case 'S':
            state_file = optarg;
            break;
        case 'd':
            debug = atoi(optarg);
            break;
        case 'g':
#ifdef NO_LOCAL_INTERFACE
            fprintf(stderr, "Warning: no local interface in this version.\n");
#else
            local_server_port = atoi(optarg);
197
#endif
198 199
            break;
        case 'l':
200
            link_detect = 1;
201 202
            break;
        case 'w':
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
203
            all_wireless = 1;
204
            break;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
205 206 207 208 209 210 211 212 213 214 215 216
        case 'z':
            {
                char *comma = strchr(optarg, ',');
                diversity_kind = atoi(optarg);
                if(comma == NULL)
                    diversity_factor = 128;
                else
                    diversity_factor = atoi(comma + 1);
                if(diversity_factor <= 0 || diversity_factor > 256)
                    goto usage;
            }
            break;
217 218
        case 't':
            export_table = atoi(optarg);
219
            if(export_table < 0 || export_table > 0xFFFF)
220 221 222 223
                goto usage;
            break;
        case 'T':
            import_table = atoi(optarg);
224
            if(import_table < 0 || import_table > 0xFFFF)
225 226 227 228 229 230 231
                goto usage;
            break;
        case 'c':
            config_file = optarg;
            break;
        case 'C':
            rc = parse_config_from_string(optarg);
232 233 234 235 236
            if(rc < 0) {
                fprintf(stderr,
                        "Couldn't parse configuration from command line.\n");
                exit(1);
            }
237 238
            break;
        case 'D':
239
            do_daemonise = 1;
240 241 242 243 244 245 246 247 248
            break;
        case 'L':
            logfile = optarg;
            break;
        case 'I':
            pidfile = optarg;
            break;
        default:
            goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
249 250 251
        }
    }

252
    if(!config_file) {
253
        if(access("/etc/babeld.conf", F_OK) >= 0)
254
            config_file = "/etc/babeld.conf";
255 256 257 258 259
    }
    if(config_file) {
        rc = parse_config_from_file(config_file);
        if(rc < 0) {
            fprintf(stderr,
260 261
                    "Couldn't parse configuration from file %s.\n",
                    config_file);
262 263
            exit(1);
        }
264 265 266 267
    } else {
        if(access("/etc/babel.conf", F_OK) >= 0)
            fprintf(stderr,
                    "Warning: /etc/babel.conf exists, it will be ignored.\n");
268 269
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
270
    if(wireless_hello_interval <= 0)
271
        wireless_hello_interval = 4000;
272
    wireless_hello_interval = MAX(wireless_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
273 274

    if(wired_hello_interval <= 0)
275
        wired_hello_interval = 4000;
276
    wired_hello_interval = MAX(wired_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
277

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
278 279 280 281 282 283
    if(parasitic && allow_duplicates >= 0) {
        /* Too difficult to get right. */
        fprintf(stderr, "Sorry, -P and -A are incompatible.\n");
        exit(1);
    }

284 285
    if(do_daemonise) {
        if(logfile == NULL)
286
            logfile = "/var/log/babeld.log";
287 288
    }

289 290 291
    rc = reopen_logfile();
    if(rc < 0) {
        perror("reopen_logfile()");
292
        exit(1);
293 294 295 296 297
    }

    fd = open("/dev/null", O_RDONLY);
    if(fd < 0) {
        perror("open(null)");
298
        exit(1);
299 300 301 302 303
    }

    rc = dup2(fd, 0);
    if(rc < 0) {
        perror("dup2(null, 0)");
304
        exit(1);
305 306 307 308 309 310 311 312
    }

    close(fd);

    if(do_daemonise) {
        rc = daemonise();
        if(rc < 0) {
            perror("daemonise");
313
            exit(1);
314 315 316
        }
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
317
    if(pidfile && pidfile[0] != '\0') {
318 319 320 321 322 323
        int pfd, len;
        char buf[100];

        len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
        if(len < 0 || len >= 100) {
            perror("snprintf(getpid)");
324
            exit(1);
325 326 327 328
        }

        pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
        if(pfd < 0) {
329 330 331 332
            char buf[40];
            snprintf(buf, 40, "creat(%s)", pidfile);
            buf[39] = '\0';
            perror(buf);
333
            exit(1);
334 335 336 337 338
        }

        rc = write(pfd, buf, len);
        if(rc < len) {
            perror("write(pidfile)");
339
            goto fail_pid;
340 341 342 343 344
        }

        close(pfd);
    }

345 346 347
    rc = kernel_setup(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
348
        goto fail_pid;
349 350 351 352 353 354
    }

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

358 359 360
    rc = finalise_config();
    if(rc < 0) {
        fprintf(stderr, "Couldn't finalise configuration.\n");
361
        goto fail;
362 363
    }

364
    for(i = optind; i < argc; i++) {
365
        vrc = add_interface(argv[i], NULL);
366
        if(vrc == NULL)
367
            goto fail;
368 369
    }

370
    if(interfaces == NULL) {
371 372 373 374
        fprintf(stderr, "Eek... asked to run on no interfaces!\n");
        goto fail;
    }

375 376 377
    FOR_ALL_INTERFACES(ifp) {
        /* ifp->ifindex is not necessarily valid at this point */
        int ifindex = if_nametoindex(ifp->name);
378 379
        if(ifindex > 0) {
            unsigned char eui[8];
380
            rc = if_eui64(ifp->name, ifindex, eui);
381 382 383 384
            if(rc < 0)
                continue;
            memcpy(myid, eui, 8);
            goto have_id;
385 386 387
        }
    }

388
    /* We failed to get a global EUI64 from the interfaces we were given.
389 390
       Let's try to find an interface with a MAC address. */
    for(i = 1; i < 256; i++) {
391 392 393 394 395 396 397 398 399 400 401 402
        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;
    }

403 404
    fprintf(stderr,
            "Warning: couldn't find router id -- using random value.\n");
405 406 407

    rc = read_random_bytes(myid, 8);
    if(rc < 0) {
dermiste's avatar
dermiste committed
408
        perror("read(random)");
409
        goto fail;
410
    }
411 412
    /* Clear group and global bits */
    myid[0] &= ~3;
413

414
 have_id:
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
415
    reboot_time = now.tv_sec;
416
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
417 418 419

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

465
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
466 467 468 469 470
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

471
#ifndef NO_LOCAL_INTERFACE
472 473 474 475 476 477
    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;
        }
478
    }
479
#endif
480

481
    init_signals();
482 483 484
    rc = resize_receive_buffer(1500);
    if(rc < 0)
        goto fail;
485
    check_interfaces();
486 487 488
    if(receive_buffer == NULL)
        goto fail;

489 490 491
    rc = check_xroutes(0);
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check exported routes.\n");
492
    kernel_routes_changed = 0;
493 494
    kernel_link_changed = 0;
    kernel_addr_changed = 0;
495
    kernel_dump_time = now.tv_sec + roughly(30);
496
    schedule_neighbours_check(5000, 1);
497 498
    expiry_time = now.tv_sec + roughly(30);
    source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
499

500 501
    /* Make some noise so that others notice us, and send retractions in
       case we were restarted recently */
502 503
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
504
            continue;
505
        /* Apply jitter before we send the first message. */
506
        usleep(roughly(10000));
507
        gettime(&now);
508 509
        send_hello(ifp);
        send_wildcard_retraction(ifp);
510 511
    }

512 513
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
514 515 516
            continue;
        usleep(roughly(10000));
        gettime(&now);
517 518 519 520 521 522
        send_hello(ifp);
        send_wildcard_retraction(ifp);
        send_self_update(ifp);
        send_request(ifp, NULL, 0);
        flushupdates(ifp);
        flushbuf(ifp);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
523 524 525 526 527 528 529 530
    }

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

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

531
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
532

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

578
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
579 580 581 582

        if(exiting)
            break;

583
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds))
584
            kernel_callback(kernel_routes_callback, NULL);
585

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
586
        if(FD_ISSET(protocol_socket, &readfds)) {
587 588 589
            rc = babel_recv(protocol_socket,
                            receive_buffer, receive_buffer_size,
                            (struct sockaddr*)&sin6, sizeof(sin6));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
590 591 592
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
593
                    sleep(1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
594 595
                }
            } else {
596 597
                FOR_ALL_INTERFACES(ifp) {
                    if(!if_up(ifp))
598
                        continue;
599 600
                    if(ifp->ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
601 602 603
                                     receive_buffer, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
                                                    receive_buffer_size);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
604 605 606 607 608 609
                        break;
                    }
                }
            }
        }

610
#ifndef NO_LOCAL_INTERFACE
611 612 613 614 615 616 617 618 619 620 621
        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 {
622
                local_notify_all();
623 624 625 626 627 628 629 630 631 632 633 634
            }
        }

        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;
            }
        }
635
#endif
636

637
        if(reopening) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
638
            kernel_dump_time = now.tv_sec;
639
            check_neighbours_timeout = now;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
640
            expiry_time = now.tv_sec;
641 642 643 644 645
            rc = reopen_logfile();
            if(rc < 0) {
                perror("reopen_logfile");
                break;
            }
646
            reopening = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
647 648
        }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
649
        if(kernel_link_changed || kernel_addr_changed) {
650
            check_interfaces();
651 652 653
            kernel_link_changed = 0;
        }

654 655
        if(kernel_routes_changed || kernel_addr_changed ||
           now.tv_sec >= kernel_dump_time) {
656 657
            rc = check_xroutes(1);
            if(rc < 0)
658
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
659
            kernel_routes_changed = kernel_addr_changed = 0;
660
            if(kernel_socket >= 0)
661
                kernel_dump_time = now.tv_sec + roughly(300);
662
            else
663
                kernel_dump_time = now.tv_sec + roughly(30);
664 665
        }

666
        if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
667 668
            int msecs;
            msecs = check_neighbours();
669 670
            msecs = MAX(msecs, 10);
            schedule_neighbours_check(msecs, 1);
671 672 673
        }

        if(now.tv_sec >= expiry_time) {
674
            check_interfaces();
675
            expire_routes();
676
            expire_resend();
677
            expiry_time = now.tv_sec + roughly(30);
678 679
        }

680 681
        if(now.tv_sec >= source_expiry_time) {
            expire_sources();
682
            source_expiry_time = now.tv_sec + roughly(300);
683 684
        }

685 686
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
687
                continue;
688 689 690 691 692 693
            if(timeval_compare(&now, &ifp->hello_timeout) >= 0)
                send_hello(ifp);
            if(timeval_compare(&now, &ifp->update_timeout) >= 0)
                send_update(ifp, 0, NULL, 0);
            if(timeval_compare(&now, &ifp->update_flush_timeout) >= 0)
                flushupdates(ifp);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
694 695
        }

696 697 698 699
        if(resend_time.tv_sec != 0) {
            if(timeval_compare(&now, &resend_time) >= 0)
                do_resend();
        }
700

701 702 703 704 705
        if(unicast_flush_timeout.tv_sec != 0) {
            if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
                flush_unicast(1);
        }

706 707
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
708
                continue;
709 710 711
            if(ifp->flush_timeout.tv_sec != 0) {
                if(timeval_compare(&now, &ifp->flush_timeout) >= 0)
                    flushbuf(ifp);
712 713 714
            }
        }

715
        if(UNLIKELY(debug || dumping)) {
716
            dump_tables(stdout);
717 718
            dumping = 0;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
719 720 721
    }

    debugf("Exiting...\n");
722
    usleep(roughly(10000));
723
    gettime(&now);
724

725 726
    /* We need to flush so interface_up won't try to reinstall. */
    flush_all_routes();
727

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

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

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

 fail:
800 801
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
802
            continue;
803
        interface_up(ifp, 0);
804
    }
805
    kernel_setup_socket(0);
806
    kernel_setup(0);
807 808 809
 fail_pid:
    if(pidfile)
        unlink(pidfile);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
810 811 812
    exit(1);
}

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

819
    timeval_add_msec(&timeout, &now, roughly(msecs * 3 / 2));
820 821 822 823 824 825
    if(override)
        check_neighbours_timeout = timeout;
    else
        timeval_min(&check_neighbours_timeout, &timeout);
}

826
int
827 828 829
resize_receive_buffer(int size)
{
    if(size <= receive_buffer_size)
830
        return 0;
831 832 833

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

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
852 853 854 855 856 857
static void
sigexit(int signo)
{
    exiting = 1;
}

858 859 860 861 862 863
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
864
static void
865
sigreopening(int signo)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
866
{
867
    reopening = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
868 869
}

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

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
894 895 896 897 898 899
    sigemptyset(&ss);
    sa.sa_handler = SIG_IGN;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

900 901 902 903 904
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
905

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
906
    sigemptyset(&ss);
907
    sa.sa_handler = sigreopening;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
908 909 910 911
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR2, &sa, NULL);

912 913 914 915 916 917 918
#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
919 920
}

921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
static void
dump_route_callback(struct route *route, void *closure)
{
    FILE *out = (FILE*)closure;
    const unsigned char *nexthop =
        memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
        NULL : route->nexthop;
    char channels[100];

    if(route->channels[0] == 0)
        channels[0] = '\0';
    else {
        int k, j = 0;
        snprintf(channels, 100, " chan (");
        j = strlen(channels);
        for(k = 0; k < DIVERSITY_HOPS; k++) {
            if(route->channels[k] == 0)
                break;
            if(k > 0)
                channels[j++] = ',';
            snprintf(channels + j, 100 - j, "%d", route->channels[k]);
            j = strlen(channels);
        }
        snprintf(channels + j, 100 - j, ")");
        if(k == 0)
            channels[0] = '\0';
    }

    fprintf(out, "%s metric %d refmetric %d id %s seqno %d%s age %d "
            "via %s neigh %s%s%s%s\n",
            format_prefix(route->src->prefix, route->src->plen),
            route_metric(route), route->refmetric,
            format_eui64(route->src->id),
            (int)route->seqno,
            channels,
            (int)(now.tv_sec - route->time),
            route->neigh->ifp->name,
            format_address(route->neigh->address),
            nexthop ? " nexthop " : "",
            nexthop ? format_address(nexthop) : "",
            route->installed ? " (installed)" :
            route_feasible(route) ? " (feasible)" : "");
}

965 966 967 968 969 970 971 972 973
static void
dump_xroute_callback(struct xroute *xroute, void *closure)
{
    FILE *out = (FILE*)closure;
    fprintf(out, "%s metric %d (exported)\n",
            format_prefix(xroute->prefix, xroute->plen),
            xroute->metric);
}

974 975 976
static void
dump_tables(FILE *out)
{
977
    struct neighbour *neigh;
978 979

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

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

983
    FOR_ALL_NEIGHBOURS(neigh) {
984
        fprintf(out, "Neighbour %s dev %s reach %04x rxcost %d txcost %d chan %d%s.\n",
985
                format_address(neigh->address),
986
                neigh->ifp->name,
987 988 989
                neigh->reach,
                neighbour_rxcost(neigh),
                neigh->txcost,
990 991
                neigh->ifp->channel,
                if_up(neigh->ifp) ? "" : " (down)");
992
    }
993
    for_all_xroutes(dump_xroute_callback, out);
994
    for_all_routes(dump_route_callback, out);
995 996 997
    fflush(out);
}

998 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
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;
}

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