Blame view

babeld.c 32.9 KB
Juliusz Chroboczek committed
1
/*
Juliusz Chroboczek committed
2
Copyright (c) 2007, 2008 by Juliusz Chroboczek
dermiste committed
3
Copyright (c) 2010 by Vincent Gross
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>

Juliusz Chroboczek committed
42
#include "babeld.h"
Juliusz Chroboczek committed
43 44 45
#include "util.h"
#include "net.h"
#include "kernel.h"
Juliusz Chroboczek committed
46
#include "interface.h"
Juliusz Chroboczek committed
47
#include "source.h"
Juliusz Chroboczek committed
48 49 50 51
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
Juliusz Chroboczek committed
52
#include "resend.h"
Juliusz Chroboczek committed
53
#include "configuration.h"
Juliusz Chroboczek committed
54
#include "local.h"
Matthieu Boutier committed
55
#include "rule.h"
Juliusz Chroboczek committed
56
#include "version.h"
Cédric Le Ninivin committed
57
#include "ctl.h"
Juliusz Chroboczek committed
58 59 60

struct timeval now;

Juliusz Chroboczek committed
61
unsigned char myid[8];
Juliusz Chroboczek committed
62
int have_id = 0;
Juliusz Chroboczek committed
63 64
int debug = 0;

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

Juliusz Chroboczek committed
78 79 80
unsigned char *receive_buffer = NULL;
int receive_buffer_size = 0;

Juliusz Chroboczek committed
81
const unsigned char zeroes[16] = {0};
Juliusz Chroboczek committed
82 83 84
const unsigned char ones[16] =
    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Juliusz Chroboczek committed
85 86 87 88

int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
Juliusz Chroboczek committed
89
int kernel_socket = -1;
Juliusz Chroboczek committed
90
static int kernel_routes_changed = 0;
Matthieu Boutier committed
91
static int kernel_rules_changed = 0;
Julien Cristau committed
92 93
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
Juliusz Chroboczek committed
94

Juliusz Chroboczek committed
95
struct timeval check_neighbours_timeout, check_interfaces_timeout;
Juliusz Chroboczek committed
96

Juliusz Chroboczek committed
97
static volatile sig_atomic_t exiting = 0, dumping = 0, reopening = 0;
Juliusz Chroboczek committed
98

Juliusz Chroboczek committed
99
static int accept_local_connections(void);
Juliusz Chroboczek committed
100
static void init_signals(void);
Juliusz Chroboczek committed
101
static void dump_tables(FILE *out);
Juliusz Chroboczek committed
102

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

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

Matthieu Boutier committed
117 118 119 120 121 122 123 124 125 126 127 128 129
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;
}

Matthieu Boutier committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
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 committed
146 147 148 149
int
main(int argc, char **argv)
{
    struct sockaddr_in6 sin6;
Gabriel Kerneis committed
150
    int rc, fd, i, opt;
Juliusz Chroboczek committed
151
    time_t expiry_time, source_expiry_time, kernel_dump_time;
Juliusz Chroboczek committed
152 153
    const char **config_files = NULL;
    int num_config_files = 0;
Juliusz Chroboczek committed
154 155
    void *vrc;
    unsigned int seed;
Matthieu Boutier committed
156
    struct interface *ifp;
Cédric Le Ninivin committed
157
    int ctl_sockfd;
Juliusz Chroboczek committed
158

Juliusz Chroboczek committed
159 160
    gettime(&now);

Juliusz Chroboczek committed
161 162 163 164 165 166
    rc = read_random_bytes(&seed, sizeof(seed));
    if(rc < 0) {
        perror("read(random)");
        seed = 42;
    }

Juliusz Chroboczek committed
167 168 169
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

Juliusz Chroboczek committed
170
    parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
Juliusz Chroboczek committed
171
    protocol_port = 6696;
Juliusz Chroboczek committed
172
    change_smoothing_half_life(4);
Matthieu Boutier committed
173
    has_ipv6_subtrees = kernel_has_ipv6_subtrees();
Juliusz Chroboczek committed
174

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

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

Juliusz Chroboczek committed
330 331 332 333 334 335 336 337 338
    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";
        }
Juliusz Chroboczek committed
339
    }
Juliusz Chroboczek committed
340 341

    for(i = 0; i < num_config_files; i++) {
Juliusz Chroboczek committed
342
        int line;
Juliusz Chroboczek committed
343
        rc = parse_config_from_file(config_files[i], &line);
Juliusz Chroboczek committed
344 345
        if(rc < 0) {
            fprintf(stderr,
Juliusz Chroboczek committed
346 347
                    "Couldn't parse configuration from file %s "
                    "(error at line %d).\n",
Juliusz Chroboczek committed
348
                    config_files[i], line);
Juliusz Chroboczek committed
349 350 351 352
            exit(1);
        }
    }

Juliusz Chroboczek committed
353 354
    free(config_files);

Juliusz Chroboczek committed
355 356 357
    if(default_wireless_hello_interval <= 0)
        default_wireless_hello_interval = 4000;
    default_wireless_hello_interval = MAX(default_wireless_hello_interval, 5);
Juliusz Chroboczek committed
358

Juliusz Chroboczek committed
359 360 361
    if(default_wired_hello_interval <= 0)
        default_wired_hello_interval = 4000;
    default_wired_hello_interval = MAX(default_wired_hello_interval, 5);
Juliusz Chroboczek committed
362

Juliusz Chroboczek committed
363
    resend_delay = 2000;
Juliusz Chroboczek committed
364 365
    resend_delay = MIN(resend_delay, default_wireless_hello_interval / 2);
    resend_delay = MIN(resend_delay, default_wired_hello_interval / 2);
Juliusz Chroboczek committed
366 367
    resend_delay = MAX(resend_delay, 20);

Juliusz Chroboczek committed
368 369
    if(do_daemonise) {
        if(logfile == NULL)
Juliusz Chroboczek committed
370
            logfile = "/var/log/babeld.log";
Juliusz Chroboczek committed
371 372
    }

Juliusz Chroboczek committed
373 374 375
    rc = reopen_logfile();
    if(rc < 0) {
        perror("reopen_logfile()");
Juliusz Chroboczek committed
376
        exit(1);
Juliusz Chroboczek committed
377 378 379 380 381
    }

    fd = open("/dev/null", O_RDONLY);
    if(fd < 0) {
        perror("open(null)");
Juliusz Chroboczek committed
382
        exit(1);
Juliusz Chroboczek committed
383 384 385 386 387
    }

    rc = dup2(fd, 0);
    if(rc < 0) {
        perror("dup2(null, 0)");
Juliusz Chroboczek committed
388
        exit(1);
Juliusz Chroboczek committed
389 390 391 392 393 394 395 396
    }

    close(fd);

    if(do_daemonise) {
        rc = daemonise();
        if(rc < 0) {
            perror("daemonise");
Juliusz Chroboczek committed
397
            exit(1);
Juliusz Chroboczek committed
398 399 400
        }
    }

Juliusz Chroboczek committed
401
    if(pidfile && pidfile[0] != '\0') {
Juliusz Chroboczek committed
402 403 404 405 406 407
        int pfd, len;
        char buf[100];

        len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
        if(len < 0 || len >= 100) {
            perror("snprintf(getpid)");
Juliusz Chroboczek committed
408
            exit(1);
Juliusz Chroboczek committed
409 410 411 412
        }

        pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
        if(pfd < 0) {
Juliusz Chroboczek committed
413 414 415 416
            char buf[40];
            snprintf(buf, 40, "creat(%s)", pidfile);
            buf[39] = '\0';
            perror(buf);
Juliusz Chroboczek committed
417
            exit(1);
Juliusz Chroboczek committed
418 419 420 421 422
        }

        rc = write(pfd, buf, len);
        if(rc < len) {
            perror("write(pidfile)");
Juliusz Chroboczek committed
423
            goto fail_pid;
Juliusz Chroboczek committed
424 425 426 427 428
        }

        close(pfd);
    }

Juliusz Chroboczek committed
429 430 431
    rc = kernel_setup(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
Juliusz Chroboczek committed
432
        goto fail_pid;
Juliusz Chroboczek committed
433 434 435 436 437 438
    }

    rc = kernel_setup_socket(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup_socket failed.\n");
        kernel_setup(0);
Juliusz Chroboczek committed
439
        goto fail_pid;
Juliusz Chroboczek committed
440
    }
Juliusz Chroboczek committed
441

Juliusz Chroboczek committed
442 443 444
    rc = finalise_config();
    if(rc < 0) {
        fprintf(stderr, "Couldn't finalise configuration.\n");
Juliusz Chroboczek committed
445
        goto fail;
Juliusz Chroboczek committed
446 447
    }

Juliusz Chroboczek committed
448
    for(i = optind; i < argc; i++) {
Juliusz Chroboczek committed
449
        vrc = add_interface(argv[i], NULL);
Juliusz Chroboczek committed
450
        if(vrc == NULL)
Juliusz Chroboczek committed
451
            goto fail;
Juliusz Chroboczek committed
452 453
    }

Matthieu Boutier committed
454
    if(interfaces == NULL) {
Juliusz Chroboczek committed
455 456 457 458
        fprintf(stderr, "Eek... asked to run on no interfaces!\n");
        goto fail;
    }

Juliusz Chroboczek committed
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    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;
        }
Juliusz Chroboczek committed
477 478
    }

Juliusz Chroboczek committed
479 480 481 482 483 484 485 486 487 488 489 490
    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;
Juliusz Chroboczek committed
491
    }
Juliusz Chroboczek committed
492

Juliusz Chroboczek committed
493
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek committed
494 495 496

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

Juliusz Chroboczek committed
524
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek committed
525 526 527 528 529
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

Juliusz Chroboczek committed
530 531 532 533 534 535
    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;
        }
Julien Cristau committed
536 537 538 539 540 541
    } 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;
        }
Juliusz Chroboczek committed
542 543
    }

Cédric Le Ninivin committed
544 545 546 547 548 549
    ctl_sockfd = init_control_socket();
    if(ctl_sockfd < 0) {
        perror("Couldn't create control socket");
        goto fail;
    }

Juliusz Chroboczek committed
550
    init_signals();
Juliusz Chroboczek committed
551 552 553
    rc = resize_receive_buffer(1500);
    if(rc < 0)
        goto fail;
Juliusz Chroboczek committed
554 555 556
    if(receive_buffer == NULL)
        goto fail;

Juliusz Chroboczek committed
557 558
    check_interfaces();

Juliusz Chroboczek committed
559 560 561
    rc = check_xroutes(0);
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check exported routes.\n");
Matthieu Boutier committed
562 563 564
    rc = check_rules();
    if(rc < 0)
        fprintf(stderr, "Warning: couldn't check rules.\n");
Juliusz Chroboczek committed
565

Juliusz Chroboczek committed
566
    kernel_routes_changed = 0;
Matthieu Boutier committed
567
    kernel_rules_changed = 0;
Julien Cristau committed
568 569
    kernel_link_changed = 0;
    kernel_addr_changed = 0;
Juliusz Chroboczek committed
570
    kernel_dump_time = now.tv_sec + roughly(30);
Juliusz Chroboczek committed
571
    schedule_neighbours_check(5000, 1);
Juliusz Chroboczek committed
572
    schedule_interfaces_check(30000, 1);
Juliusz Chroboczek committed
573 574
    expiry_time = now.tv_sec + roughly(30);
    source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek committed
575

Juliusz Chroboczek committed
576 577
    /* Make some noise so that others notice us, and send retractions in
       case we were restarted recently */
Matthieu Boutier committed
578 579
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
Juliusz Chroboczek committed
580
            continue;
Juliusz Chroboczek committed
581
        /* Apply jitter before we send the first message. */
Juliusz Chroboczek committed
582
        usleep(roughly(10000));
Juliusz Chroboczek committed
583
        gettime(&now);
Matthieu Boutier committed
584 585
        send_hello(ifp);
        send_wildcard_retraction(ifp);
Juliusz Chroboczek committed
586 587
    }

Matthieu Boutier committed
588 589
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
Juliusz Chroboczek committed
590 591 592
            continue;
        usleep(roughly(10000));
        gettime(&now);
Matthieu Boutier committed
593 594 595 596 597
        send_hello(ifp);
        send_wildcard_retraction(ifp);
        send_self_update(ifp);
        flushupdates(ifp);
        flushbuf(ifp);
Juliusz Chroboczek committed
598 599 600 601 602 603
    }

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

    while(1) {
        struct timeval tv;
Cédric Le Ninivin committed
604 605
        fd_set readfds, writefds;
        struct ctl *ctl, *ctl_next;
Juliusz Chroboczek committed
606

Juliusz Chroboczek committed
607
        gettime(&now);
Juliusz Chroboczek committed
608

Juliusz Chroboczek committed
609
        tv = check_neighbours_timeout;
Juliusz Chroboczek committed
610
        timeval_min(&tv, &check_interfaces_timeout);
Juliusz Chroboczek committed
611
        timeval_min_sec(&tv, expiry_time);
Juliusz Chroboczek committed
612
        timeval_min_sec(&tv, source_expiry_time);
Juliusz Chroboczek committed
613
        timeval_min_sec(&tv, kernel_dump_time);
Juliusz Chroboczek committed
614
        timeval_min(&tv, &resend_time);
Matthieu Boutier committed
615 616
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
Juliusz Chroboczek committed
617
                continue;
Matthieu Boutier committed
618 619 620 621
            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 committed
622
        }
Juliusz Chroboczek committed
623
        timeval_min(&tv, &unicast_flush_timeout);
Juliusz Chroboczek committed
624
        FD_ZERO(&readfds);
Cédric Le Ninivin committed
625
        FD_ZERO(&writefds);
Juliusz Chroboczek committed
626
        if(timeval_compare(&tv, &now) > 0) {
Juliusz Chroboczek committed
627
            int maxfd = 0;
Juliusz Chroboczek committed
628 629
            timeval_minus(&tv, &tv, &now);
            FD_SET(protocol_socket, &readfds);
Juliusz Chroboczek committed
630
            maxfd = MAX(maxfd, protocol_socket);
Grégoire Henry committed
631
            if(kernel_socket < 0) kernel_setup_socket(1);
Juliusz Chroboczek committed
632
            if(kernel_socket >= 0) {
Juliusz Chroboczek committed
633
                FD_SET(kernel_socket, &readfds);
Juliusz Chroboczek committed
634 635
                maxfd = MAX(maxfd, kernel_socket);
            }
Juliusz Chroboczek committed
636 637
            if(local_server_socket >= 0 &&
               num_local_sockets < MAX_LOCAL_SOCKETS) {
Juliusz Chroboczek committed
638 639 640
                FD_SET(local_server_socket, &readfds);
                maxfd = MAX(maxfd, local_server_socket);
            }
Juliusz Chroboczek committed
641
            for(i = 0; i < num_local_sockets; i++) {
Juliusz Chroboczek committed
642 643
                FD_SET(local_sockets[i].fd, &readfds);
                maxfd = MAX(maxfd, local_sockets[i].fd);
Juliusz Chroboczek committed
644
            }
Cédric Le Ninivin committed
645 646 647 648 649 650 651 652 653 654
            FD_SET(ctl_sockfd, &readfds);
            maxfd = MAX(maxfd, ctl_sockfd);
            for(ctl = ctl_connection; ctl; ctl = ctl->next) {
                if(ctl->write < ctl->buffer_out.end)
                    FD_SET(ctl->fd, &writefds);
                else
                    FD_SET(ctl->fd, &readfds);
                maxfd = MAX(maxfd, ctl->fd);
            }
            rc = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
Juliusz Chroboczek committed
655
            if(rc < 0) {
Juliusz Chroboczek committed
656
                if(errno != EINTR) {
Juliusz Chroboczek committed
657
                    perror("select");
Juliusz Chroboczek committed
658
                    sleep(1);
Juliusz Chroboczek committed
659
                }
Juliusz Chroboczek committed
660 661
                rc = 0;
                FD_ZERO(&readfds);
Juliusz Chroboczek committed
662 663 664
            }
        }

Juliusz Chroboczek committed
665
        gettime(&now);
Juliusz Chroboczek committed
666 667 668 669

        if(exiting)
            break;

Matthieu Boutier committed
670 671 672
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds)) {
            struct kernel_filter filter = {0};
            filter.route = kernel_route_notify;
Matthieu Boutier committed
673
            filter.addr = kernel_addr_notify;
Matthieu Boutier committed
674
            filter.link = kernel_link_notify;
Matthieu Boutier committed
675
            filter.rule = kernel_rule_notify;
Matthieu Boutier committed
676 677
            kernel_callback(&filter);
        }
Juliusz Chroboczek committed
678

Juliusz Chroboczek committed
679
        if(FD_ISSET(protocol_socket, &readfds)) {
Juliusz Chroboczek committed
680 681 682
            rc = babel_recv(protocol_socket,
                            receive_buffer, receive_buffer_size,
                            (struct sockaddr*)&sin6, sizeof(sin6));
Juliusz Chroboczek committed
683 684 685
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
Juliusz Chroboczek committed
686
                    sleep(1);
Juliusz Chroboczek committed
687 688
                }
            } else {
Matthieu Boutier committed
689 690
                FOR_ALL_INTERFACES(ifp) {
                    if(!if_up(ifp))
Juliusz Chroboczek committed
691
                        continue;
Matthieu Boutier committed
692 693
                    if(ifp->ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
Juliusz Chroboczek committed
694 695 696
                                     receive_buffer, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
                                                    receive_buffer_size);
Juliusz Chroboczek committed
697 698 699 700 701 702
                        break;
                    }
                }
            }
        }

Juliusz Chroboczek committed
703 704
        if(local_server_socket >= 0 && FD_ISSET(local_server_socket, &readfds))
           accept_local_connections();
Juliusz Chroboczek committed
705

Juliusz Chroboczek committed
706 707
        i = 0;
        while(i < num_local_sockets) {
Juliusz Chroboczek committed
708 709
            if(FD_ISSET(local_sockets[i].fd, &readfds)) {
                rc = local_read(&local_sockets[i]);
Juliusz Chroboczek committed
710
                if(rc <= 0) {
Juliusz Chroboczek committed
711
                    if(rc < 0) {
Juliusz Chroboczek committed
712
                        if(errno == EINTR || errno == EAGAIN)
Juliusz Chroboczek committed
713
                            continue;
Juliusz Chroboczek committed
714
                        perror("read(local_socket)");
Juliusz Chroboczek committed
715
                    }
Juliusz Chroboczek committed
716
                    local_socket_destroy(i);
Juliusz Chroboczek committed
717
                }
Juliusz Chroboczek committed
718
            }
Juliusz Chroboczek committed
719
            i++;
Juliusz Chroboczek committed
720 721
        }

Cédric Le Ninivin committed
722 723 724 725 726 727 728 729 730 731
        if(FD_ISSET(ctl_sockfd, &readfds)) {
            accept_ctl_connection(ctl_sockfd);
        }
        for(ctl = ctl_connection; ctl; ctl = ctl_next) {
            ctl_next = ctl->next;
            if (FD_ISSET(ctl->fd, &writefds))
                ctl_write(ctl);
            if (FD_ISSET(ctl->fd, &readfds))
                ctl_read(ctl);
        }
Juliusz Chroboczek committed
732
        if(reopening) {
Juliusz Chroboczek committed
733
            kernel_dump_time = now.tv_sec;
Juliusz Chroboczek committed
734
            check_neighbours_timeout = now;
Juliusz Chroboczek committed
735
            expiry_time = now.tv_sec;
Juliusz Chroboczek committed
736 737 738 739 740
            rc = reopen_logfile();
            if(rc < 0) {
                perror("reopen_logfile");
                break;
            }
Juliusz Chroboczek committed
741
            reopening = 0;
Juliusz Chroboczek committed
742 743
        }

Juliusz Chroboczek committed
744
        if(kernel_link_changed || kernel_addr_changed) {
Matthieu Boutier committed
745
            check_interfaces();
Julien Cristau committed
746 747 748
            kernel_link_changed = 0;
        }

Juliusz Chroboczek committed
749
        if(kernel_routes_changed || kernel_addr_changed ||
Matthieu Boutier committed
750
           kernel_rules_changed || now.tv_sec >= kernel_dump_time) {
Juliusz Chroboczek committed
751 752
            rc = check_xroutes(1);
            if(rc < 0)
Juliusz Chroboczek committed
753
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
Matthieu Boutier committed
754 755 756
            rc = check_rules();
            if(rc < 0)
                fprintf(stderr, "Warning: couldn't check rules.\n");
Matthieu Boutier committed
757 758
            kernel_routes_changed = kernel_rules_changed =
                kernel_addr_changed = 0;
Juliusz Chroboczek committed
759
            if(kernel_socket >= 0)
Juliusz Chroboczek committed
760
                kernel_dump_time = now.tv_sec + roughly(300);
Juliusz Chroboczek committed
761
            else
Juliusz Chroboczek committed
762
                kernel_dump_time = now.tv_sec + roughly(30);
Juliusz Chroboczek committed
763 764
        }

Juliusz Chroboczek committed
765
        if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
Juliusz Chroboczek committed
766 767
            int msecs;
            msecs = check_neighbours();
Juliusz Chroboczek committed
768 769
            /* Multiply by 3/2 to allow neighbours to expire. */
            msecs = MAX(3 * msecs / 2, 10);
Juliusz Chroboczek committed
770
            schedule_neighbours_check(msecs, 1);
Juliusz Chroboczek committed
771 772
        }

Juliusz Chroboczek committed
773
        if(timeval_compare(&check_interfaces_timeout, &now) < 0) {
Matthieu Boutier committed
774
            check_interfaces();
Juliusz Chroboczek committed
775 776 777 778
            schedule_interfaces_check(30000, 1);
        }

        if(now.tv_sec >= expiry_time) {
Juliusz Chroboczek committed
779
            expire_routes();
Juliusz Chroboczek committed
780
            expire_resend();
Juliusz Chroboczek committed
781
            expiry_time = now.tv_sec + roughly(30);
Juliusz Chroboczek committed
782 783
        }

Juliusz Chroboczek committed
784 785
        if(now.tv_sec >= source_expiry_time) {
            expire_sources();
Juliusz Chroboczek committed
786
            source_expiry_time = now.tv_sec + roughly(300);
Juliusz Chroboczek committed
787 788
        }

Matthieu Boutier committed
789 790
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
Juliusz Chroboczek committed
791
                continue;
Matthieu Boutier committed
792 793 794
            if(timeval_compare(&now, &ifp->hello_timeout) >= 0)
                send_hello(ifp);
            if(timeval_compare(&now, &ifp->update_timeout) >= 0)
Matthieu Boutier committed
795
                send_update(ifp, 0, NULL, 0, NULL, 0);
Matthieu Boutier committed
796 797
            if(timeval_compare(&now, &ifp->update_flush_timeout) >= 0)
                flushupdates(ifp);
Juliusz Chroboczek committed
798 799
        }

Juliusz Chroboczek committed
800 801 802 803
        if(resend_time.tv_sec != 0) {
            if(timeval_compare(&now, &resend_time) >= 0)
                do_resend();
        }
Juliusz Chroboczek committed
804

Juliusz Chroboczek committed
805 806 807 808 809
        if(unicast_flush_timeout.tv_sec != 0) {
            if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
                flush_unicast(1);
        }

Matthieu Boutier committed
810 811
        FOR_ALL_INTERFACES(ifp) {
            if(!if_up(ifp))
Juliusz Chroboczek committed
812
                continue;
Matthieu Boutier committed
813 814 815
            if(ifp->flush_timeout.tv_sec != 0) {
                if(timeval_compare(&now, &ifp->flush_timeout) >= 0)
                    flushbuf(ifp);
Juliusz Chroboczek committed
816 817 818
            }
        }

Juliusz Chroboczek committed
819
        if(UNLIKELY(debug || dumping)) {
Juliusz Chroboczek committed
820
            dump_tables(stdout);
Juliusz Chroboczek committed
821 822
            dumping = 0;
        }
Juliusz Chroboczek committed
823 824 825
    }

    debugf("Exiting...\n");
Juliusz Chroboczek committed
826
    usleep(roughly(10000));
Juliusz Chroboczek committed
827
    gettime(&now);
Juliusz Chroboczek committed
828

Juliusz Chroboczek committed
829 830
    /* We need to flush so interface_up won't try to reinstall. */
    flush_all_routes();
Juliusz Chroboczek committed
831

Matthieu Boutier committed
832 833
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
Juliusz Chroboczek committed
834
            continue;
Matthieu Boutier committed
835
        send_wildcard_retraction(ifp);
Juliusz Chroboczek committed
836
        /* Make sure that we expire quickly from our neighbours'
Juliusz Chroboczek committed
837
           association caches. */
Matthieu Boutier committed
838 839
        send_hello_noupdate(ifp, 10);
        flushbuf(ifp);
Juliusz Chroboczek committed
840
        usleep(roughly(1000));
Juliusz Chroboczek committed
841
        gettime(&now);
Juliusz Chroboczek committed
842
    }
Matthieu Boutier committed
843 844
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
Juliusz Chroboczek committed
845
            continue;
Juliusz Chroboczek committed
846
        /* Make sure they got it. */
Matthieu Boutier committed
847 848 849
        send_wildcard_retraction(ifp);
        send_hello_noupdate(ifp, 1);
        flushbuf(ifp);
Juliusz Chroboczek committed
850
        usleep(roughly(10000));
Juliusz Chroboczek committed
851
        gettime(&now);
Matthieu Boutier committed
852
        interface_up(ifp, 0);
Juliusz Chroboczek committed
853
    }
Matthieu Boutier committed
854
    release_tables();
Grégoire Henry committed
855
    kernel_setup_socket(0);
Juliusz Chroboczek committed
856
    kernel_setup(0);
Juliusz Chroboczek committed
857 858 859

    fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if(fd < 0) {
Juliusz Chroboczek committed
860
        perror("creat(babel-state)");
Juliusz Chroboczek committed
861
        unlink(state_file);
Juliusz Chroboczek committed
862
    } else {
Juliusz Chroboczek committed
863 864 865
        char buf[10];
        rc = snprintf(buf, 10, "%d\n", (int)myseqno);
        if(rc < 0 || rc >= 10) {
Juliusz Chroboczek committed
866
            fprintf(stderr, "write(babel-state): overflow.\n");
Juliusz Chroboczek committed
867
            unlink(state_file);
Juliusz Chroboczek committed
868 869 870 871 872 873 874
        } else {
            rc = write(fd, buf, rc);
            if(rc < 0) {
                perror("write(babel-state)");
                unlink(state_file);
            }
            fsync(fd);
Juliusz Chroboczek committed
875
        }
Juliusz Chroboczek committed
876 877
        close(fd);
    }
Julien Cristau committed
878 879 880 881
    if(local_server_socket >= 0 && local_server_path) {
        unlink(local_server_path);
        free(local_server_path);
    }
Juliusz Chroboczek committed
882 883
    if(pidfile)
        unlink(pidfile);
Juliusz Chroboczek committed
884
    debugf("Done.\n");
Juliusz Chroboczek committed
885 886
    return 0;

Juliusz Chroboczek committed
887
 usage:
Juliusz Chroboczek committed
888
    fprintf(stderr,
Juliusz Chroboczek committed
889 890 891 892
            "%s\n"
            "Syntax: babeld "
            "[-V] [-m multicast_address] [-p port] [-S state-file]\n"
            "               "
Juliusz Chroboczek committed
893
            "[-h hello] [-H wired_hello] [-z kind[,factor]]\n"
Juliusz Chroboczek committed
894
            "               "
Juliusz Chroboczek committed
895
            "[-g port] [-G port] [-k metric] [-A metric] [-s] [-l] [-w] [-r]\n"
Juliusz Chroboczek committed
896
            "               "
Juliusz Chroboczek committed
897
            "[-u] [-t table] [-T table] [-c file] [-C statement]\n"
Juliusz Chroboczek committed
898
            "               "
Cédric Le Ninivin committed
899
            "[-d level] [-D] [-L logfile] [-I pidfile] [-X control-socket]\n"
Juliusz Chroboczek committed
900
            "               "
Juliusz Chroboczek committed
901
            "interface...\n",
Juliusz Chroboczek committed
902
            BABELD_VERSION);
Juliusz Chroboczek committed
903 904 905
    exit(1);

 fail:
Matthieu Boutier committed
906 907
    FOR_ALL_INTERFACES(ifp) {
        if(!if_up(ifp))
Juliusz Chroboczek committed
908
            continue;
Matthieu Boutier committed
909
        interface_up(ifp, 0);
Juliusz Chroboczek committed
910
    }
Grégoire Henry committed
911
    kernel_setup_socket(0);
Juliusz Chroboczek committed
912
    kernel_setup(0);
Juliusz Chroboczek committed
913 914 915
 fail_pid:
    if(pidfile)
        unlink(pidfile);
Juliusz Chroboczek committed
916 917 918
    exit(1);
}

Baptiste Jonglez committed
919
static int
Juliusz Chroboczek committed
920
accept_local_connections()
Baptiste Jonglez committed
921
{
Juliusz Chroboczek committed
922
    int rc, s;
Juliusz Chroboczek committed
923
    struct local_socket *ls;
Baptiste Jonglez committed
924

Juliusz Chroboczek committed
925
    if(local_server_socket < 0)
Baptiste Jonglez committed
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
        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;
    }

Baptiste Jonglez committed
946 947 948 949 950 951 952 953 954 955 956 957 958 959
    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;
    }

Juliusz Chroboczek committed
960
    ls = local_socket_create(s);
Juliusz Chroboczek committed
961 962 963 964 965 966
    if(ls == NULL) {
        fprintf(stderr, "Unable create local socket.\n");
        close(s);
        return -1;
    }
    local_header(ls);
Baptiste Jonglez committed
967 968 969
    return 1;
}

Juliusz Chroboczek committed
970 971 972 973 974
void
schedule_neighbours_check(int msecs, int override)
{
    struct timeval timeout;

Juliusz Chroboczek committed
975
    timeval_add_msec(&timeout, &now, roughly(msecs));
Juliusz Chroboczek committed
976 977 978 979 980 981
    if(override)
        check_neighbours_timeout = timeout;
    else
        timeval_min(&check_neighbours_timeout, &timeout);
}

Juliusz Chroboczek committed
982 983 984 985 986 987 988 989 990 991 992 993
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);
}

Juliusz Chroboczek committed
994
int
Juliusz Chroboczek committed
995 996
resize_receive_buffer(int size)
{
Juliusz Chroboczek committed
997 998
    unsigned char *new;

Juliusz Chroboczek committed
999
    if(size <= receive_buffer_size)
Juliusz Chroboczek committed
1000
        return 0;
Juliusz Chroboczek committed
1001

Juliusz Chroboczek committed
1002 1003 1004 1005
    new = realloc(receive_buffer, size);
    if(new == NULL) {
        perror("realloc(receive_buffer)");
        return -1;
Juliusz Chroboczek committed
1006
    }
Juliusz Chroboczek committed
1007 1008 1009
    receive_buffer = new;
    receive_buffer_size = size;

Juliusz Chroboczek committed
1010
    return 1;
Juliusz Chroboczek committed
1011 1012
}

Juliusz Chroboczek committed
1013 1014 1015 1016 1017 1018
static void
sigexit(int signo)
{
    exiting = 1;
}

Juliusz Chroboczek committed
1019 1020 1021 1022 1023 1024
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek committed
1025
static void
Juliusz Chroboczek committed
1026
sigreopening(int signo)
Juliusz Chroboczek committed
1027
{
Juliusz Chroboczek committed
1028
    reopening = 1;
Juliusz Chroboczek committed
1029 1030
}

Juliusz Chroboczek committed
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
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);
Juliusz Chroboczek committed
1054

Juliusz Chroboczek committed
1055 1056 1057 1058 1059 1060
    sigemptyset(&ss);
    sa.sa_handler = SIG_IGN;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

Juliusz Chroboczek committed
1061 1062 1063 1064 1065
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
Juliusz Chroboczek committed
1066

Juliusz Chroboczek committed
1067
    sigemptyset(&ss);
Juliusz Chroboczek committed
1068
    sa.sa_handler = sigreopening;
Juliusz Chroboczek committed
1069 1070 1071 1072
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR2, &sa, NULL);

Juliusz Chroboczek committed
1073 1074 1075 1076 1077 1078 1079
#ifdef SIGINFO
    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGINFO, &sa, NULL);
#endif
Juliusz Chroboczek committed
1080 1081
}

Juliusz Chroboczek committed
1082
static void
Juliusz Chroboczek committed
1083
dump_route(FILE *out, struct babel_route *route)
Juliusz Chroboczek committed
1084 1085 1086 1087 1088 1089
{
    const unsigned char *nexthop =
        memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
        NULL : route->nexthop;
    char channels[100];

Juliusz Chroboczek committed
1090
    if(route->channels_len == 0) {
Juliusz Chroboczek committed
1091
        channels[0] = '\0';
Juliusz Chroboczek committed
1092
    } else {
Juliusz Chroboczek committed
1093 1094 1095
        int k, j = 0;
        snprintf(channels, 100, " chan (");
        j = strlen(channels);
Juliusz Chroboczek committed
1096
        for(k = 0; k < route->channels_len; k++) {
Juliusz Chroboczek committed
1097 1098
            if(k > 0)
                channels[j++] = ',';
Juliusz Chroboczek committed
1099
            snprintf(channels + j, 100 - j, "%u", (unsigned)route->channels[k]);
Juliusz Chroboczek committed
1100 1101 1102 1103 1104
            j = strlen(channels);
        }
        snprintf(channels + j, 100 - j, ")");
    }

Juliusz Chroboczek committed
1105
    fprintf(out, "%s%s%s metric %d (%d) refmetric %d id %s "
Matthieu Boutier committed
1106
            "seqno %d%s age %d via %s neigh %s%s%s%s\n",
Juliusz Chroboczek committed
1107
            format_prefix(route->src->prefix, route->src->plen),
Juliusz Chroboczek committed
1108 1109 1110
            route->src->src_plen > 0 ? " from " : "",
            route->src->src_plen > 0 ?
            format_prefix(route->src->src_prefix, route->src->src_plen) : "",
Juliusz Chroboczek committed
1111
            route_metric(route), route_smoothed_metric(route), route->refmetric,
Juliusz Chroboczek committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
            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)" : "");
}

Juliusz Chroboczek committed
1124
static void
Juliusz Chroboczek committed
1125
dump_xroute(FILE *out, struct xroute *xroute)
Juliusz Chroboczek committed
1126
{
Juliusz Chroboczek committed
1127
    fprintf(out, "%s%s%s metric %d (exported)\n",
Juliusz Chroboczek committed
1128
            format_prefix(xroute->prefix, xroute->plen),
Juliusz Chroboczek committed
1129 1130 1131
            xroute->src_plen > 0 ? " from " : "",
            xroute->src_plen > 0 ?
            format_prefix(xroute->src_prefix, xroute->src_plen) : "",
Juliusz Chroboczek committed
1132 1133 1134
            xroute->metric);
}

Juliusz Chroboczek committed
1135 1136 1137
static void
dump_tables(FILE *out)
{
Juliusz Chroboczek committed
1138
    struct neighbour *neigh;
Juliusz Chroboczek committed
1139
    struct xroute_stream *xroutes;
Juliusz Chroboczek committed
1140
    struct route_stream *routes;
Juliusz Chroboczek committed
1141 1142

    fprintf(out, "\n");
Juliusz Chroboczek committed
1143

Juliusz Chroboczek committed
1144
    fprintf(out, "My id %s seqno %d\n", format_eui64(myid), myseqno);
Juliusz Chroboczek committed
1145

Juliusz Chroboczek committed
1146
    FOR_ALL_NEIGHBOURS(neigh) {
Juliusz Chroboczek committed
1147 1148
        fprintf(out, "Neighbour %s dev %s reach %04x ureach %04x "
                "rxcost %d txcost %d rtt %s rttcost %d chan %d%s.\n",
Juliusz Chroboczek committed
1149
                format_address(neigh->address),
Matthieu Boutier committed
1150
                neigh->ifp->name,
Juliusz Chroboczek committed
1151
                neigh->hello.reach,
Juliusz Chroboczek committed
1152
                neigh->uhello.reach,
Juliusz Chroboczek committed
1153 1154
                neighbour_rxcost(neigh),
                neigh->txcost,
Baptiste Jonglez committed
1155
                format_thousands(neigh->rtt),
Baptiste Jonglez committed
1156
                neighbour_rttcost(neigh),
Matthieu Boutier committed
1157 1158
                neigh->ifp->channel,
                if_up(neigh->ifp) ? "" : " (down)");
Juliusz Chroboczek committed
1159
    }
Juliusz Chroboczek committed
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170

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

Matthieu Boutier committed
1171
    routes = route_stream(ROUTE_ALL);
Juliusz Chroboczek committed
1172 1173 1174 1175 1176 1177 1178 1179
    if(routes) {
        while(1) {
            struct babel_route *route = route_stream_next(routes);
            if(route == NULL) break;
            dump_route(out, route);
        }
        route_stream_done(routes);
    }
Juliusz Chroboczek committed
1180

Juliusz Chroboczek committed
1181 1182 1183
    fflush(out);
}

Juliusz Chroboczek committed
1184
int
Juliusz Chroboczek committed
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
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;
}