babeld.c 31.9 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"
55
#include "rule.h"
56
#include "version.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
57 58 59

struct timeval now;

60
unsigned char myid[8];
61
int have_id = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
62 63
int debug = 0;

64
int link_detect = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
65
int all_wireless = 0;
66
int has_ipv6_subtrees = 0;
67 68
int default_wireless_hello_interval = -1;
int default_wired_hello_interval = -1;
69
int resend_delay = -1;
70
int random_id = 0;
71
int do_daemonise = 0;
72
int skip_kernel_setup = 0;
73
const char *logfile = NULL,
74 75
    *pidfile = "/var/run/babeld.pid",
    *state_file = "/var/lib/babel-state";
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
76

77 78 79
unsigned char *receive_buffer = NULL;
int receive_buffer_size = 0;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
80
const unsigned char zeroes[16] = {0};
81 82 83
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
84 85 86 87

int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
88
int kernel_socket = -1;
89
static int kernel_routes_changed = 0;
90
static int kernel_rules_changed = 0;
91 92
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
93

94
struct timeval check_neighbours_timeout, check_interfaces_timeout;
95

96
static volatile sig_atomic_t exiting = 0, dumping = 0, reopening = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
97

98
static int accept_local_connections(void);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
99
static void init_signals(void);
100
static void dump_tables(FILE *out);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
101

102 103 104 105 106 107 108
static int
kernel_route_notify(struct kernel_route *route, void *closure)
{
    kernel_routes_changed = 1;
    return -1;
}

109 110 111 112 113 114 115
static int
kernel_addr_notify(struct kernel_addr *addr, void *closure)
{
    kernel_addr_changed = 1;
    return -1;
}

116 117 118 119 120 121 122 123 124 125 126 127 128
static int
kernel_link_notify(struct kernel_link *link, void *closure)
{
    struct interface *ifp;
    FOR_ALL_INTERFACES(ifp) {
        if(strcmp(ifp->name, link->ifname) == 0) {
            kernel_link_changed = 1;
            return -1;
        }
    }
    return 0;
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static int
kernel_rule_notify(struct kernel_rule *rule, void *closure)
{
    int i;
    if(martian_prefix(rule->src, rule->src_plen))
        return 0;

    i = rule->priority - src_table_prio;

    if(i < 0 || SRC_TABLE_NUM <= i)
        return 0;

    kernel_rules_changed = 1;
    return -1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
145 146 147 148
int
main(int argc, char **argv)
{
    struct sockaddr_in6 sin6;
149
    int rc, fd, i, opt;
150
    time_t expiry_time, source_expiry_time, kernel_dump_time;
151 152
    const char **config_files = NULL;
    int num_config_files = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
153 154
    void *vrc;
    unsigned int seed;
155
    struct interface *ifp;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
156

157 158
    gettime(&now);

159 160 161 162 163 164
    rc = read_random_bytes(&seed, sizeof(seed));
    if(rc < 0) {
        perror("read(random)");
        seed = 42;
    }

165 166 167
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

168
    parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
169
    protocol_port = 6696;
170
    change_smoothing_half_life(4);
171
    has_ipv6_subtrees = kernel_has_ipv6_subtrees();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
172

173
    while(1) {
174
        opt = getopt(argc, argv,
175
                     "m:p:h:H:i:k:A:srS:d:g:G:lwz:M:t:T:c:C:DL:I:V");
176
        if(opt < 0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
177
            break;
178 179 180 181

        switch(opt) {
        case 'm':
            rc = parse_address(optarg, protocol_group, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
182
            if(rc < 0)
183
                goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
184 185
            if(protocol_group[0] != 0xff) {
                fprintf(stderr,
186 187
                        "%s is not a multicast address\n", optarg);
                goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
188 189 190 191
            }
            if(protocol_group[1] != 2) {
                fprintf(stderr,
                        "Warning: %s is not a link-local multicast address\n",
192
                        optarg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
193
            }
194 195
            break;
        case 'p':
196 197 198
            protocol_port = parse_nat(optarg);
            if(protocol_port <= 0 || protocol_port > 0xFFFF)
                goto usage;
199 200
            break;
        case 'h':
201
            default_wireless_hello_interval = parse_thousands(optarg);
202 203
            if(default_wireless_hello_interval <= 0 ||
               default_wireless_hello_interval > 0xFFFF * 10)
204 205 206
                goto usage;
            break;
        case 'H':
207
            default_wired_hello_interval = parse_thousands(optarg);
208 209
            if(default_wired_hello_interval <= 0 ||
               default_wired_hello_interval > 0xFFFF * 10)
210 211 212
                goto usage;
            break;
        case 'k':
213
            kernel_metric = parse_nat(optarg);
214
            if(kernel_metric < 0 || kernel_metric > 0xFFFF)
215 216
                goto usage;
            break;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
217
        case 'A':
218
            allow_duplicates = parse_nat(optarg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
219 220 221
            if(allow_duplicates < 0 || allow_duplicates > 0xFFFF)
                goto usage;
            break;
222
        case 's':
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
223
            split_horizon = 0;
224
            break;
225 226 227
        case 'r':
            random_id = 1;
            break;
228 229 230 231
        case 'S':
            state_file = optarg;
            break;
        case 'd':
232 233 234
            debug = parse_nat(optarg);
            if(debug < 0)
                goto usage;
235 236
            break;
        case 'g':
237
        case 'G':
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
            if(opt == 'g')
                local_server_write = 0;
            else
                local_server_write = 1;
            if(optarg[0] == '/') {
                local_server_port = -1;
                free(local_server_path);
                local_server_path = strdup(optarg);
            } else {
                local_server_port = parse_nat(optarg);
                free(local_server_path);
                local_server_path = NULL;
                if(local_server_port <= 0 || local_server_port > 0xFFFF)
                    goto usage;
            }
253 254
            break;
        case 'l':
255
            link_detect = 1;
256 257
            break;
        case 'w':
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
258
            all_wireless = 1;
259
            break;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
260 261
        case 'z':
            {
262 263 264
                char *comma;
                diversity_kind = (int)strtol(optarg, &comma, 0);
                if(*comma == '\0')
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
265
                    diversity_factor = 128;
266 267
                else if(*comma == ',')
                    diversity_factor = parse_nat(comma + 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
268
                else
269
                    goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
270 271 272 273
                if(diversity_factor <= 0 || diversity_factor > 256)
                    goto usage;
            }
            break;
274 275 276 277 278 279 280
        case 'M': {
            int l = parse_nat(optarg);
            if(l < 0 || l > 3600)
                goto usage;
            change_smoothing_half_life(l);
            break;
        }
281
        case 't':
282
            export_table = parse_nat(optarg);
283
            if(export_table < 0 || export_table > 0xFFFF)
284 285 286
                goto usage;
            break;
        case 'T':
287
            if(add_import_table(parse_nat(optarg)))
288 289 290
                goto usage;
            break;
        case 'c':
291 292 293 294 295 296 297
            config_files = realloc(config_files,
                                   (num_config_files + 1) * sizeof(char*));
            if(config_files == NULL) {
                fprintf(stderr, "Couldn't allocate config file.\n");
                exit(1);
            }
            config_files[num_config_files++] = optarg;
298 299
            break;
        case 'C':
300
            rc = parse_config_from_string(optarg, strlen(optarg), NULL);
301
            if(rc != CONFIG_ACTION_DONE) {
302 303 304 305
                fprintf(stderr,
                        "Couldn't parse configuration from command line.\n");
                exit(1);
            }
306 307
            break;
        case 'D':
308
            do_daemonise = 1;
309 310 311 312 313 314 315
            break;
        case 'L':
            logfile = optarg;
            break;
        case 'I':
            pidfile = optarg;
            break;
316 317 318 319
        case 'V':
            fprintf(stderr, "%s\n", BABELD_VERSION);
            exit(0);
            break;
320 321
        default:
            goto usage;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
322 323 324
        }
    }

325 326 327 328 329 330 331 332 333
    if(num_config_files == 0) {
        if(access("/etc/babeld.conf", F_OK) >= 0) {
            config_files = malloc(sizeof(char*));
            if(config_files == NULL) {
                fprintf(stderr, "Couldn't allocate config file.\n");
                exit(1);
            }
            config_files[num_config_files++] = "/etc/babeld.conf";
        }
334
    }
335 336

    for(i = 0; i < num_config_files; i++) {
337
        int line;
338
        rc = parse_config_from_file(config_files[i], &line);
339 340
        if(rc < 0) {
            fprintf(stderr,
341 342
                    "Couldn't parse configuration from file %s "
                    "(error at line %d).\n",
343
                    config_files[i], line);
344 345 346 347
            exit(1);
        }
    }

348 349
    free(config_files);

350 351 352
    if(default_wireless_hello_interval <= 0)
        default_wireless_hello_interval = 4000;
    default_wireless_hello_interval = MAX(default_wireless_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
353

354 355 356
    if(default_wired_hello_interval <= 0)
        default_wired_hello_interval = 4000;
    default_wired_hello_interval = MAX(default_wired_hello_interval, 5);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
357

358
    resend_delay = 2000;
359 360
    resend_delay = MIN(resend_delay, default_wireless_hello_interval / 2);
    resend_delay = MIN(resend_delay, default_wired_hello_interval / 2);
361 362
    resend_delay = MAX(resend_delay, 20);

363 364
    if(do_daemonise) {
        if(logfile == NULL)
365
            logfile = "/var/log/babeld.log";
366 367
    }

368 369 370
    rc = reopen_logfile();
    if(rc < 0) {
        perror("reopen_logfile()");
371
        exit(1);
372 373 374 375 376
    }

    fd = open("/dev/null", O_RDONLY);
    if(fd < 0) {
        perror("open(null)");
377
        exit(1);
378 379 380 381 382
    }

    rc = dup2(fd, 0);
    if(rc < 0) {
        perror("dup2(null, 0)");
383
        exit(1);
384 385 386 387 388 389 390 391
    }

    close(fd);

    if(do_daemonise) {
        rc = daemonise();
        if(rc < 0) {
            perror("daemonise");
392
            exit(1);
393 394 395
        }
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
396
    if(pidfile && pidfile[0] != '\0') {
397 398 399 400 401 402
        int pfd, len;
        char buf[100];

        len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
        if(len < 0 || len >= 100) {
            perror("snprintf(getpid)");
403
            exit(1);
404 405 406 407
        }

        pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
        if(pfd < 0) {
408 409 410 411
            char buf[40];
            snprintf(buf, 40, "creat(%s)", pidfile);
            buf[39] = '\0';
            perror(buf);
412
            exit(1);
413 414 415 416 417
        }

        rc = write(pfd, buf, len);
        if(rc < len) {
            perror("write(pidfile)");
418
            goto fail_pid;
419 420 421 422 423
        }

        close(pfd);
    }

424 425 426
    rc = kernel_setup(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
427
        goto fail_pid;
428 429 430 431 432 433
    }

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

437 438 439
    rc = finalise_config();
    if(rc < 0) {
        fprintf(stderr, "Couldn't finalise configuration.\n");
440
        goto fail;
441 442
    }

443
    for(i = optind; i < argc; i++) {
444
        vrc = add_interface(argv[i], NULL);
445
        if(vrc == NULL)
446
            goto fail;
447 448
    }

449
    if(interfaces == NULL) {
450 451 452 453
        fprintf(stderr, "Eek... asked to run on no interfaces!\n");
        goto fail;
    }

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    if(!have_id && !random_id) {
        /* We use all available interfaces here, since this increases the
           chances of getting a stable router-id in case the set of Babel
           interfaces changes. */

        for(i = 1; i < 256; i++) {
            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);
            have_id = 1;
            break;
        }
472 473
    }

474 475 476 477 478 479 480 481 482 483 484 485
    if(!have_id) {
        if(!random_id)
            fprintf(stderr,
                    "Warning: couldn't find router id -- "
                    "using random value.\n");
        rc = read_random_bytes(myid, 8);
        if(rc < 0) {
            perror("read(random)");
            goto fail;
        }
        /* Clear group and global bits */
        myid[0] &= ~3;
486
    }
487

488
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
489 490 491

    fd = open(state_file, O_RDONLY);
    if(fd < 0 && errno != ENOENT)
492
        perror("open(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
493
    rc = unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
494
    if(fd >= 0 && rc < 0) {
495
        perror("unlink(babel-state)");
496
        /* If we couldn't unlink it, it's probably stale. */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
497 498 499 500 501
        close(fd);
        fd = -1;
    }
    if(fd >= 0) {
        char buf[100];
502
        int s;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
503 504
        rc = read(fd, buf, 99);
        if(rc < 0) {
505
            perror("read(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
506 507
        } else {
            buf[rc] = '\0';
508 509 510
            rc = sscanf(buf, "%d\n", &s);
            if(rc == 1 && s >= 0 && s <= 0xFFFF) {
                myseqno = seqno_plus(s, 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
511
            } else {
512
                fprintf(stderr, "Couldn't parse babel-state.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
513 514 515
            }
        }
        close(fd);
516
        fd = -1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
517 518
    }

519
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
520 521 522 523 524
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

525 526 527 528 529 530
    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;
        }
531 532 533 534 535 536
    } else if(local_server_path) {
        local_server_socket = unix_server_socket(local_server_path);
        if(local_server_socket < 0) {
            perror("local_server_socket");
            goto fail;
        }
537 538
    }

539
    init_signals();
540 541 542
    rc = resize_receive_buffer(1500);
    if(rc < 0)
        goto fail;
543 544 545
    if(receive_buffer == NULL)
        goto fail;

546 547
    check_interfaces();

548 549 550
    rc = check_xroutes(0);
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check exported routes.\n");
551 552 553
    rc = check_rules();
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check rules.\n");
554

555
    kernel_routes_changed = 0;
556
    kernel_rules_changed = 0;
557 558
    kernel_link_changed = 0;
    kernel_addr_changed = 0;
559
    kernel_dump_time = now.tv_sec + roughly(30);
560
    schedule_neighbours_check(5000, 1);
561
    schedule_interfaces_check(30000, 1);
562 563
    expiry_time = now.tv_sec + roughly(30);
    source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
564

565 566
    /* Make some noise so that others notice us, and send retractions in
       case we were restarted recently */
567 568
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
569
            continue;
570
        /* Apply jitter before we send the first message. */
571
        usleep(roughly(10000));
572
        gettime(&now);
573 574
        send_hello(ifp);
        send_wildcard_retraction(ifp);
575 576
    }

577 578
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
579 580 581
            continue;
        usleep(roughly(10000));
        gettime(&now);
582 583 584
        send_hello(ifp);
        send_wildcard_retraction(ifp);
        send_self_update(ifp);
585
        send_request(ifp, NULL, 0, NULL, 0);
586 587
        flushupdates(ifp);
        flushbuf(ifp);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
588 589 590 591 592 593 594 595
    }

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

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

596
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
597

598
        tv = check_neighbours_timeout;
599
        timeval_min(&tv, &check_interfaces_timeout);
600
        timeval_min_sec(&tv, expiry_time);
601
        timeval_min_sec(&tv, source_expiry_time);
602
        timeval_min_sec(&tv, kernel_dump_time);
603
        timeval_min(&tv, &resend_time);
604 605
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
606
                continue;
607 608 609 610
            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
611
        }
612
        timeval_min(&tv, &unicast_flush_timeout);
613
        FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
614
        if(timeval_compare(&tv, &now) > 0) {
615
            int maxfd = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
616 617
            timeval_minus(&tv, &tv, &now);
            FD_SET(protocol_socket, &readfds);
618
            maxfd = MAX(maxfd, protocol_socket);
619
            if(kernel_socket < 0) kernel_setup_socket(1);
620
            if(kernel_socket >= 0) {
621
                FD_SET(kernel_socket, &readfds);
622 623
                maxfd = MAX(maxfd, kernel_socket);
            }
624 625
            if(local_server_socket >= 0 &&
               num_local_sockets < MAX_LOCAL_SOCKETS) {
626 627 628
                FD_SET(local_server_socket, &readfds);
                maxfd = MAX(maxfd, local_server_socket);
            }
629
            for(i = 0; i < num_local_sockets; i++) {
630 631
                FD_SET(local_sockets[i].fd, &readfds);
                maxfd = MAX(maxfd, local_sockets[i].fd);
632
            }
633
            rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
634
            if(rc < 0) {
635
                if(errno != EINTR) {
636
                    perror("select");
637
                    sleep(1);
638
                }
639 640
                rc = 0;
                FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
641 642 643
            }
        }

644
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
645 646 647 648

        if(exiting)
            break;

649 650 651
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds)) {
            struct kernel_filter filter = {0};
            filter.route = kernel_route_notify;
652
            filter.addr = kernel_addr_notify;
653
            filter.link = kernel_link_notify;
654
            filter.rule = kernel_rule_notify;
655 656
            kernel_callback(&filter);
        }
657

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
658
        if(FD_ISSET(protocol_socket, &readfds)) {
659 660 661
            rc = babel_recv(protocol_socket,
                            receive_buffer, receive_buffer_size,
                            (struct sockaddr*)&sin6, sizeof(sin6));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
662 663 664
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
665
                    sleep(1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
666 667
                }
            } else {
668 669
                FOR_ALL_INTERFACES(ifp) {
                    if(!if_up(ifp))
670
                        continue;
671 672
                    if(ifp->ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
673 674 675
                                     receive_buffer, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
                                                    receive_buffer_size);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
676 677 678 679 680 681
                        break;
                    }
                }
            }
        }

682 683
        if(local_server_socket >= 0 && FD_ISSET(local_server_socket, &readfds))
           accept_local_connections();
684

685 686
        i = 0;
        while(i < num_local_sockets) {
687 688
            if(FD_ISSET(local_sockets[i].fd, &readfds)) {
                rc = local_read(&local_sockets[i]);
689
                if(rc <= 0) {
690
                    if(rc < 0) {
691
                        if(errno == EINTR || errno == EAGAIN)
692
                            continue;
693
                        perror("read(local_socket)");
694
                    }
695
                    local_socket_destroy(i);
696
                }
697
            }
698
            i++;
699 700
        }

701
        if(reopening) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
702
            kernel_dump_time = now.tv_sec;
703
            check_neighbours_timeout = now;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
704
            expiry_time = now.tv_sec;
705 706 707 708 709
            rc = reopen_logfile();
            if(rc < 0) {
                perror("reopen_logfile");
                break;
            }
710
            reopening = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
711 712
        }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
713
        if(kernel_link_changed || kernel_addr_changed) {
714
            check_interfaces();
715 716 717
            kernel_link_changed = 0;
        }

718
        if(kernel_routes_changed || kernel_addr_changed ||
719
           kernel_rules_changed || now.tv_sec >= kernel_dump_time) {
720 721
            rc = check_xroutes(1);
            if(rc < 0)
722
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
723 724 725
            rc = check_rules();
            if(rc < 0)
                fprintf(stderr, "Warning: couldn't check rules.\n");
726 727
            kernel_routes_changed = kernel_rules_changed =
                kernel_addr_changed = 0;
728
            if(kernel_socket >= 0)
729
                kernel_dump_time = now.tv_sec + roughly(300);
730
            else
731
                kernel_dump_time = now.tv_sec + roughly(30);
732 733
        }

734
        if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
735 736
            int msecs;
            msecs = check_neighbours();
737 738
            /* Multiply by 3/2 to allow neighbours to expire. */
            msecs = MAX(3 * msecs / 2, 10);
739
            schedule_neighbours_check(msecs, 1);
740 741
        }

742
        if(timeval_compare(&check_interfaces_timeout, &now) < 0) {
743
            check_interfaces();
744 745 746 747
            schedule_interfaces_check(30000, 1);
        }

        if(now.tv_sec >= expiry_time) {
748
            expire_routes();
749
            expire_resend();
750
            expiry_time = now.tv_sec + roughly(30);
751 752
        }

753 754
        if(now.tv_sec >= source_expiry_time) {
            expire_sources();
755
            source_expiry_time = now.tv_sec + roughly(300);
756 757
        }

758 759
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
760
                continue;
761 762 763
            if(timeval_compare(&now, &ifp->hello_timeout) >= 0)
                send_hello(ifp);
            if(timeval_compare(&now, &ifp->update_timeout) >= 0)
764
                send_update(ifp, 0, NULL, 0, NULL, 0);
765 766
            if(timeval_compare(&now, &ifp->update_flush_timeout) >= 0)
                flushupdates(ifp);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
767 768
        }

769 770 771 772
        if(resend_time.tv_sec != 0) {
            if(timeval_compare(&now, &resend_time) >= 0)
                do_resend();
        }
773

774 775 776 777 778
        if(unicast_flush_timeout.tv_sec != 0) {
            if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
                flush_unicast(1);
        }

779 780
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
781
                continue;
782 783 784
            if(ifp->flush_timeout.tv_sec != 0) {
                if(timeval_compare(&now, &ifp->flush_timeout) >= 0)
                    flushbuf(ifp);
785 786 787
            }
        }

788
        if(UNLIKELY(debug || dumping)) {
789
            dump_tables(stdout);
790 791
            dumping = 0;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
792 793 794
    }

    debugf("Exiting...\n");
795
    usleep(roughly(10000));
796
    gettime(&now);
797

798 799
    /* We need to flush so interface_up won't try to reinstall. */
    flush_all_routes();
800

801 802
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
803
            continue;
804
        send_wildcard_retraction(ifp);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
805
        /* Make sure that we expire quickly from our neighbours'
806
           association caches. */
807 808
        send_hello_noupdate(ifp, 10);
        flushbuf(ifp);
809
        usleep(roughly(1000));
810
        gettime(&now);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
811
    }
812 813
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
814
            continue;
815
        /* Make sure they got it. */
816 817 818
        send_wildcard_retraction(ifp);
        send_hello_noupdate(ifp, 1);
        flushbuf(ifp);
819
        usleep(roughly(10000));
820
        gettime(&now);
821
        interface_up(ifp, 0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
822
    }
823
    release_tables();
824
    kernel_setup_socket(0);
825
    kernel_setup(0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
826 827 828

    fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if(fd < 0) {
829
        perror("creat(babel-state)");
830
        unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
831
    } else {
832 833 834
        char buf[10];
        rc = snprintf(buf, 10, "%d\n", (int)myseqno);
        if(rc < 0 || rc >= 10) {
835
            fprintf(stderr, "write(babel-state): overflow.\n");
836
            unlink(state_file);
837 838 839 840 841 842 843
        } else {
            rc = write(fd, buf, rc);
            if(rc < 0) {
                perror("write(babel-state)");
                unlink(state_file);
            }
            fsync(fd);
844
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
845 846
        close(fd);
    }
847 848 849 850
    if(local_server_socket >= 0 && local_server_path) {
        unlink(local_server_path);
        free(local_server_path);
    }
851 852
    if(pidfile)
        unlink(pidfile);
853
    debugf("Done.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
854 855
    return 0;

856
 usage:
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
857
    fprintf(stderr,
858 859 860 861
            "%s\n"
            "Syntax: babeld "
            "[-V] [-m multicast_address] [-p port] [-S state-file]\n"
            "               "
862
            "[-h hello] [-H wired_hello] [-z kind[,factor]]\n"
863
            "               "
864
            "[-g port] [-G port] [-k metric] [-A metric] [-s] [-l] [-w] [-r]\n"
865
            "               "
866
            "[-u] [-t table] [-T table] [-c file] [-C statement]\n"
867
            "               "
868
            "[-d level] [-D] [-L logfile] [-I pidfile]\n"
869
            "               "
870
            "interface...\n",
871
            BABELD_VERSION);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
872 873 874
    exit(1);

 fail:
875 876
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
877
            continue;
878
        interface_up(ifp, 0);
879
    }
880
    kernel_setup_socket(0);
881
    kernel_setup(0);
882 883 884
 fail_pid:
    if(pidfile)
        unlink(pidfile);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
885 886 887
    exit(1);
}

888
static int
889
accept_local_connections()
890
{
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
891
    int rc, s;
892
    struct local_socket *ls;
893

894
    if(local_server_socket < 0)
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
        return 0;

    s = accept(local_server_socket, NULL, NULL);

    if(s < 0) {
        if(errno != EINTR && errno != EAGAIN) {
            perror("accept(local_server_socket)");
            return -1;
        }
        return 0;
    }

    if(num_local_sockets >= MAX_LOCAL_SOCKETS) {
        /* This should never happen, since we don't select for
           the server socket in this case.  But I'm paranoid. */
        fprintf(stderr, "Internal error: too many local sockets.\n");
        close(s);
        return -1;
    }

915 916 917 918 919 920 921 922 923 924 925 926 927 928
    rc = fcntl(s, F_GETFL, 0);
    if(rc < 0) {
        fprintf(stderr, "Unable to get flags of local socket.\n");
        close(s);
        return -1;
    }

    rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
    if(rc < 0) {
        fprintf(stderr, "Unable to set flags of local socket.\n");
        close(s);
        return -1;
    }

929
    ls = local_socket_create(s);
930 931 932 933 934 935
    if(ls == NULL) {
        fprintf(stderr, "Unable create local socket.\n");
        close(s);
        return -1;
    }
    local_header(ls);
936 937 938
    return 1;
}

939 940 941 942 943
void
schedule_neighbours_check(int msecs, int override)
{
    struct timeval timeout;

944
    timeval_add_msec(&timeout, &now, roughly(msecs));
945 946 947 948 949 950
    if(override)
        check_neighbours_timeout = timeout;
    else
        timeval_min(&check_neighbours_timeout, &timeout);
}

951 952 953 954 955 956 957 958 959 960 961 962
void
schedule_interfaces_check(int msecs, int override)
{
    struct timeval timeout;

    timeval_add_msec(&timeout, &now, roughly(msecs));
    if(override)
        check_interfaces_timeout = timeout;
    else
        timeval_min(&check_interfaces_timeout, &timeout);
}

963
int
964 965
resize_receive_buffer(int size)
{
966 967
    unsigned char *new;

968
    if(size <= receive_buffer_size)
969
        return 0;
970

971 972 973 974
    new = realloc(receive_buffer, size);
    if(new == NULL) {
        perror("realloc(receive_buffer)");
        return -1;
975
    }
976 977 978
    receive_buffer = new;
    receive_buffer_size = size;

979
    return 1;
980 981
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
982 983 984 985 986 987
static void
sigexit(int signo)
{
    exiting = 1;
}

988 989 990 991 992 993
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
994
static void
995
sigreopening(int signo)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
996
{
997
    reopening = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
998 999
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
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);
1023

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1024 1025 1026 1027 1028 1029
    sigemptyset(&ss);
    sa.sa_handler = SIG_IGN;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

1030 1031 1032 1033 1034
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
1035

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1036
    sigemptyset(&ss);
1037
    sa.sa_handler = sigreopening;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
1038 1039 1040 1041
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR2, &sa, NULL);

1042 1043 1044 1045 1046 1047 1048
#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
1049 1050
}

1051
static void
1052
dump_route(FILE *out, struct babel_route *route)
1053 1054 1055 1056 1057 1058
{
    const unsigned char *nexthop =
        memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
        NULL : route->nexthop;
    char channels[100];

1059
    if(route->channels_len == 0) {
1060
        channels[0] = '\0';
1061
    } else {
1062 1063 1064
        int k, j = 0;
        snprintf(channels, 100, " chan (");
        j = strlen(channels);
1065
        for(k = 0; k < route->channels_len; k++) {
1066 1067
            if(k > 0)
                channels[j++] = ',';
1068
            snprintf(channels + j, 100 - j, "%u", (unsigned)route->channels[k]);
1069 1070 1071 1072 1073
            j = strlen(channels);
        }
        snprintf(channels + j, 100 - j, ")");
    }

1074
    fprintf(out, "%s%s%s metric %d (%d) refmetric %d id %s "
1075
            "seqno %d%s age %d via %s neigh %s%s%s%s\n",
1076
            format_prefix(route->src->prefix, route->src->plen),
1077 1078 1079
            route->src->src_plen > 0 ? " from " : "",
            route->src->src_plen > 0 ?
            format_prefix(route->src->src_prefix, route->src->src_plen) : "",
1080
            route_metric(route), route_smoothed_metric(route), route->refmetric,
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
            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)" : "");
}

1093
static void
1094
dump_xroute(FILE *out, struct xroute *xroute)
1095
{
1096
    fprintf(out, "%s%s%s metric %d (exported)\n",
1097
            format_prefix(xroute->prefix, xroute->plen),
1098 1099 1100
            xroute->src_plen > 0 ? " from " : "",
            xroute->src_plen > 0 ?
            format_prefix(xroute->src_prefix, xroute->src_plen) : "",
1101 1102 1103
            xroute->metric);
}

1104 1105 1106
static void
dump_tables(FILE *out)
{
1107
    struct neighbour *neigh;
1108
    struct xroute_stream *xroutes;
1109
    struct route_stream *routes;
1110 1111

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

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

1115
    FOR_ALL_NEIGHBOURS(neigh) {
1116 1117
        fprintf(out, "Neighbour %s dev %s reach %04x ureach %04x "
                "rxcost %d txcost %d rtt %s rttcost %d chan %d%s.\n",
1118
                format_address(neigh->address),
1119
                neigh->ifp->name,
1120
                neigh->hello.reach,
1121
                neigh->uhello.reach,
1122 1123
                neighbour_rxcost(neigh),
                neigh->txcost,
1124
                format_thousands(neigh->rtt),
1125
                neighbour_rttcost(neigh),
1126 1127
                neigh->ifp->channel,
                if_up(neigh->ifp) ? "" : " (down)");
1128
    }
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139

    xroutes = xroute_stream();
    if(xroutes) {
        while(1) {
            struct xroute *xroute = xroute_stream_next(xroutes);
            if(xroute == NULL) break;
            dump_xroute(out, xroute);
        }
        xroute_stream_done(xroutes);
    }

1140
    routes = route_stream(ROUTE_ALL);
1141 1142 1143 1144 1145 1146 1147 1148
    if(routes) {
        while(1) {
            struct babel_route *route = route_stream_next(routes);
            if(route == NULL) break;
            dump_route(out, route);
        }
        route_stream_done(routes);
    }
1149

1150 1151 1152
    fflush(out);
}

1153
int
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
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;
}