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

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

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

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

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

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

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

struct timeval now;

unsigned char myid[16];
56
int do_ipv4 = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
57 58 59 60 61 62
int debug = 0;

static int maxmtu;

int reboot_time;

63 64
int idle_time = 320;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
65 66
int wireless_hello_interval = -1;
int wired_hello_interval = -1;
67
int idle_hello_interval = -1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
68 69
int update_interval = -1;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
70
const unsigned char zeroes[16] = {0};
71 72 73
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
74

75
char *state_file = "/var/lib/babel-state";
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
76 77 78 79

int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
80
int kernel_socket = -1;
81
static int kernel_routes_changed = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
82

83
static volatile sig_atomic_t exiting = 0, dumping = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
84

85
static int kernel_routes_callback(void *closure);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
86
static void init_signals(void);
87
static void dump_tables(FILE *out);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
88 89 90 91 92 93 94 95

int
main(int argc, char **argv)
{
    struct sockaddr_in6 sin6;
    struct ipv6_mreq mreq;
    int i, rc, fd;
    static unsigned char *buf;
96
    struct timeval check_neighbours_time;
97
    int expiry_time, kernel_dump_time;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
98 99 100 101 102 103 104
    void *vrc;
    unsigned int seed;
    char **arg;

    parse_address("ff02::cca6:c0f9:e182:5373", protocol_group);
    protocol_port = 8475;

105
#define SHIFT() do { arg++; } while(0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
#define SHIFTE() do { arg++; if(*arg == NULL) goto syntax; } while(0)

    arg = argv;

    SHIFTE();

    while((*arg)[0] == '-') {
        if(strcmp(*arg, "--") == 0) {
            SHIFTE();
            break;
        } else if(strcmp(*arg, "-m") == 0) {
            SHIFTE();
            rc = parse_address(*arg, protocol_group);
            if(rc < 0)
                goto syntax;
            if(protocol_group[0] != 0xff) {
                fprintf(stderr,
                        "%s is not a multicast address\n", *arg);
                goto syntax;
            }
            if(protocol_group[1] != 2) {
                fprintf(stderr,
                        "Warning: %s is not a link-local multicast address\n",
                        *arg);
            }
        } else if(strcmp(*arg, "-p") == 0) {
            SHIFTE();
            protocol_port = atoi(*arg);
134 135
        } else if(strcmp(*arg, "-x") == 0 || strcmp(*arg, "-X") == 0) {
            int force = (strcmp(*arg, "-X") == 0);
136 137
            if(numxroutes >= MAXXROUTES) {
                fprintf(stderr, "Too many exported routes.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
138 139 140 141
                exit(1);
            }
            SHIFTE();
            rc = parse_net(*arg,
142 143
                           xroutes[numxroutes].prefix,
                           &xroutes[numxroutes].plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
144 145 146
            if(rc < 0)
                goto syntax;
            SHIFTE();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
147
            if(strcmp(*arg, "infinity") == 0)
148 149 150 151 152 153 154 155 156
                xroutes[numxroutes].metric = INFINITY;
            else {
                int metric = atoi(*arg);
                if(metric < 0 || metric > INFINITY)
                    goto syntax;
                xroutes[numxroutes].metric = metric;
            }
            xroutes[numxroutes].exported = force ? 2 : 0;
            numxroutes++;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
157 158 159 160 161 162
        } else if(strcmp(*arg, "-h") == 0) {
            SHIFTE();
            wireless_hello_interval = atoi(*arg);
        } else if(strcmp(*arg, "-H") == 0) {
            SHIFTE();
            wired_hello_interval = atoi(*arg);
163 164 165
        } else if(strcmp(*arg, "-i") == 0) {
            SHIFTE();
            idle_hello_interval = atoi(*arg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
166 167 168 169 170 171
        } else if(strcmp(*arg, "-u") == 0) {
            SHIFTE();
            update_interval = atoi(*arg);
        } else if(strcmp(*arg, "-k") == 0) {
            SHIFTE();
            kernel_metric = atoi(*arg);
172
            if(kernel_metric < 0 || kernel_metric > 0xFFFF)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
173 174 175
                goto syntax;
        } else if(strcmp(*arg, "-P") == 0) {
            parasitic = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
176 177 178 179 180
        } else if(strcmp(*arg, "-c") == 0) {
            SHIFTE();
            add_cost = atoi(*arg);
            if(add_cost < 0 || add_cost > INFINITY)
                goto syntax;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
181 182 183 184 185 186 187 188
        } else if(strcmp(*arg, "-s") == 0) {
            split_horizon = 0;
        } else if(strcmp(*arg, "-S") == 0) {
            SHIFTE();
            state_file = *arg;
        } else if(strcmp(*arg, "-d") == 0) {
            SHIFTE();
            debug = atoi(*arg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
189
        } else if(strcmp(*arg, "-4") == 0) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
190
            do_ipv4 = 1;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
191 192 193 194 195 196 197
        } else {
            goto syntax;
        }
        SHIFTE();
    }

    if(wireless_hello_interval <= 0)
198
        wireless_hello_interval = 6;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
199 200 201 202 203 204 205

    if(wired_hello_interval <= 0)
        wired_hello_interval = 30;

    if(update_interval <= 0)
        update_interval =
            MIN(MAX(wireless_hello_interval * 5, wired_hello_interval),
206
                70);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
207 208

    if(seqno_interval <= 0)
209
        seqno_interval = MAX(wireless_hello_interval - 1, 2);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
210 211 212 213 214 215 216

    rc = parse_address(*arg, myid);
    if(rc < 0)
        goto syntax;
    SHIFTE();

    gettimeofday(&now, NULL);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

    fd = open("/dev/urandom", O_RDONLY);
    if(fd < 0) {
        perror("open(random)");
        memcpy(&seed, myid + 12, 4);
    } else {
        rc = read(fd, &seed, sizeof(unsigned int));
        if(rc < sizeof(unsigned int)) {
            perror("read(random)");
            exit(1);
        }
        close(fd);
    }
    seed ^= (now.tv_sec ^ now.tv_usec);
    srandom(seed);

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
233
    reboot_time = now.tv_sec;
234
    myseqno = (random() & 0xFFFF);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
235 236 237

    fd = open(state_file, O_RDONLY);
    if(fd < 0 && errno != ENOENT)
238
        perror("open(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
239 240
    rc = unlink(state_file);
    if(fd >= 0 && rc < 0) {
241
        perror("unlink(babel-state)");
242
        /* If we couldn't unlink it, it's probably stale. */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
243 244 245 246 247
        close(fd);
        fd = -1;
    }
    if(fd >= 0) {
        char buf[100];
248 249 250
        char buf2[100];
        int s;
        long t;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
251 252
        rc = read(fd, buf, 99);
        if(rc < 0) {
253
            perror("read(babel-state)");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
254 255
        } else {
            buf[rc] = '\0';
256 257 258 259 260 261 262 263 264
            rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
            if(rc == 3 && s >= 0 && s <= 0xFFFF) {
                unsigned char sid[16];
                rc = parse_address(buf2, sid);
                if(rc < 0) {
                    fprintf(stderr, "Couldn't parse babel-state.\n");
                } else {
                    debugf("Got %s %d %ld from babel-state.\n",
                           format_address(sid), s, t);
265 266 267 268
                    if(memcmp(sid, myid, 16) == 0)
                        myseqno = seqno_plus(s, 1);
                    else
                        fprintf(stderr, "ID mismatch in babel-state.\n");
269 270 271
                    if(t >= 1176800000L && t <= now.tv_sec)
                        reboot_time = t;
                }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
272
            } else {
273
                fprintf(stderr, "Couldn't parse babel-state.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
274 275 276 277 278
            }
        }
        close(fd);
    }

279 280 281 282
    if(reboot_time + silent_time > now.tv_sec)
        fprintf(stderr, "Respecting %ld second silent time.\n",
                (long int)(reboot_time + silent_time - now.tv_sec));

283
    rc = kernel_setup(1, do_ipv4);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
284 285 286 287 288
    if(rc < 0) {
        fprintf(stderr, "kernel_setup failed.\n");
        exit(1);
    }

289 290 291 292 293 294
    rc = kernel_setup_socket(1);
    if(rc < 0) {
        fprintf(stderr, "kernel_setup_socket failed.\n");
        exit(1);
    }

295
    protocol_socket = babel_socket(protocol_port);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
296 297 298 299 300 301 302 303 304 305 306 307 308
    if(protocol_socket < 0) {
        perror("Couldn't create link local socket");
        goto fail;
    }

    /* Just in case. */
    maxmtu = 1500;

    while(*arg) {
        int ifindex;
        int mtu;

        ifindex = if_nametoindex(*arg);
309 310
        if(ifindex <= 0) {
            fprintf(stderr, "Unknown interface %s.\n", *arg);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
            goto fail;
        }

        rc = kernel_setup_interface(1, *arg, ifindex);
        if(rc < 0) {
            fprintf(stderr, "kernel_setup_interface(%s, %d) failed.\n",
                    *arg, ifindex);
            goto fail;
        }

        memset(&mreq, 0, sizeof(mreq));
        memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
        mreq.ipv6mr_interface = ifindex;

        rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                        (char*)&mreq, sizeof(mreq));
        if(rc < 0) {
            perror("setsockopt(IPV6_JOIN_GROUP)");
            goto fail;
        }

        mtu = kernel_interface_mtu(*arg, ifindex);
        if(mtu < 0) {
            fprintf(stderr, "Warning: couldn't get MTU of interface %s (%d).\n",
                    *arg, ifindex);
336 337
            mtu = 1280;
            maxmtu = MAX(maxmtu, 0x10000);
338
        } else if(mtu < 1280) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
339 340 341
            fprintf(stderr,
                    "Warning: suspiciously low MTU %d on interface %s (%d).\n",
                    mtu, *arg, ifindex);
342 343
            mtu = 1280;
            maxmtu = MAX(maxmtu, 0x10000);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
344 345 346 347 348 349 350 351 352 353
        } else {
            if(mtu >= 0x10000) {
                fprintf(stderr,
                        "Warning: "
                        "suspiciously high MTU %d on interface %s (%d).\n",
                        mtu, *arg, ifindex);
                maxmtu = MAX(maxmtu, mtu);
                mtu = 32768;
            }
        }
354 355 356
        maxmtu = MAX(maxmtu, mtu);
        /* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
        mtu -= 60;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
357 358 359 360 361 362 363 364 365 366 367

        rc = kernel_interface_wireless(*arg, ifindex);
        if(rc < 0) {
            fprintf(stderr,
                    "Warning: "
                    "couldn't determine whether %s is a wireless interface.\n",
                    *arg);
            rc = 1;
        }
        debugf("Adding %s network %s (%d).\n",
               rc ? "wireless" : "wired", *arg, ifindex);
368
        vrc = add_network(*arg, ifindex, mtu, !rc, rc ? 256 : 128);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
369 370 371 372 373 374 375 376 377 378 379 380
        if(vrc == NULL)
            goto fail;
        SHIFT();
    }

    buf = malloc(maxmtu);
    if(buf == NULL) {
        perror("malloc");
        goto fail;
    }

    init_signals();
381
    check_xroutes();
382 383
    kernel_routes_changed = 0;
    kernel_dump_time = now.tv_sec + 20 + random() % 20;
384
    timeval_plus_msec(&check_neighbours_time, &now, 5000 + random() % 5000);
385
    expiry_time = now.tv_sec + 20 + random() % 20;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
386

387
    /* Make some noise so that others notice us */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
388
    for(i = 0; i < numnets; i++) {
389 390
        if(!nets[i].up)
            continue;
391
        gettimeofday(&now, NULL);
392
        send_hello(&nets[i]);
393
        send_request(&nets[i], NULL, 0, 0, 0, 0);
394 395
        flushbuf(&nets[i]);
        usleep(50000 + random() % 100000);
396
    }
397

398
    for(i = 0; i < numnets; i++) {
399 400
        if(!nets[i].up)
            continue;
401
        gettimeofday(&now, NULL);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
402
        send_hello(&nets[i]);
403
        send_self_update(&nets[i], 0);
404
        send_request(&nets[i], NULL, 0, 0, 0, 0);
405 406
        flushbuf(&nets[i]);
        usleep(50000 + random() % 100000);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
407 408 409 410 411 412 413 414 415 416
    }

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

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

        gettimeofday(&now, NULL);

417 418
        tv = check_neighbours_time;
        timeval_min_sec(&tv, expiry_time);
419 420
        if(request_resend_time)
            timeval_min_sec(&tv, request_resend_time);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
421
        for(i = 0; i < numnets; i++) {
422 423
            if(!nets[i].up)
                continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
424 425 426
            timeval_min(&tv, &nets[i].flush_time);
            timeval_min_sec(&tv,
                            nets[i].hello_time + nets[i].hello_interval);
427 428 429 430 431
            if(!network_idle(&nets[i])) {
                timeval_min_sec(&tv, nets[i].self_update_time +
                                nets[i].self_update_interval);
                timeval_min_sec(&tv, nets[i].update_time + update_interval);
            }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
432 433
        }
        timeval_min(&tv, &update_flush_time);
434
        FD_ZERO(&readfds);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
435 436 437
        if(timeval_compare(&tv, &now) > 0) {
            timeval_minus(&tv, &tv, &now);
            FD_SET(protocol_socket, &readfds);
438
            if(kernel_socket < 0) kernel_setup_socket(1);
439 440 441 442
            if(kernel_socket >= 0)
                FD_SET(kernel_socket, &readfds);
            rc = select(MAX(protocol_socket, kernel_socket) + 1,
                        &readfds, NULL, NULL, &tv);
443
            if(rc < 0) {
444 445 446 447
                if(errno == EINTR) {
                    rc = 0;
                    FD_ZERO(&readfds);
                } else {
448 449 450 451
                    perror("select");
                    sleep(1);
                    continue;
                }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
452 453 454 455 456 457 458 459
            }
        }

        gettimeofday(&now, NULL);

        if(exiting)
            break;

460
        if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds))
461
            kernel_callback(kernel_routes_callback, NULL);
462

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
463
        if(FD_ISSET(protocol_socket, &readfds)) {
464
            rc = babel_recv(protocol_socket, buf, maxmtu,
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
465 466 467 468 469 470 471 472
                              (struct sockaddr*)&sin6, sizeof(sin6));
            if(rc < 0) {
                if(errno != EAGAIN && errno != EINTR) {
                    perror("recv");
                    sleep(1);
                }
            } else {
                for(i = 0; i < numnets; i++) {
473 474
                    if(!nets[i].up)
                        continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
475 476 477 478 479 480 481 482 483 484
                    if(nets[i].ifindex == sin6.sin6_scope_id) {
                        parse_packet((unsigned char*)&sin6.sin6_addr, &nets[i],
                                     buf, rc);
                        VALGRIND_MAKE_MEM_UNDEFINED(buf, maxmtu);
                        break;
                    }
                }
            }
        }

485
        if(kernel_routes_changed || now.tv_sec >= kernel_dump_time) {
486
            rc = check_xroutes();
487 488 489
            if(rc > 0)
                send_self_update(NULL, 1);
            else if(rc < 0)
490
                fprintf(stderr, "Warning: couldn't check exported routes.\n");
491 492
            kernel_routes_changed = 0;
            if(kernel_socket >= 0)
493
                kernel_dump_time = now.tv_sec + 200 + random() % 200;
494 495 496 497
            else
                kernel_dump_time = now.tv_sec + 20 + random() % 20;
        }

498 499 500 501 502 503 504 505 506 507
        if(timeval_compare(&check_neighbours_time, &now) < 0) {
            int msecs;
            msecs = check_neighbours();
            msecs = MAX(msecs, 500);
            timeval_plus_msec(&check_neighbours_time, &now,
                              msecs / 2 + random() % msecs);
        }

        if(now.tv_sec >= expiry_time) {
            expire_routes();
508
            expire_requests();
509
            check_networks();
510 511 512
            expiry_time = now.tv_sec + 20 + random() % 20;
        }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
513
        for(i = 0; i < numnets; i++) {
514 515
            if(!nets[i].up)
                continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
516 517
            if(now.tv_sec >= nets[i].hello_time + nets[i].hello_interval)
                send_hello(&nets[i]);
518 519
            if(now.tv_sec >= nets[i].ihu_time + nets[i].ihu_interval)
                send_ihu(NULL, &nets[i]);
520 521
            if(!network_idle(&nets[i])) {
                if(now.tv_sec >= nets[i].update_time + update_interval)
522
                    send_update(&nets[i], 0, NULL, 0);
523 524 525 526
                if(now.tv_sec >=
                   nets[i].self_update_time + nets[i].self_update_interval) {
                    send_self_update(&nets[i], 0);
                }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
527 528 529
            }
        }

530 531 532
        if(now.tv_sec >= request_resend_time)
            resend_requests();

533 534 535 536 537 538
        if(update_flush_time.tv_sec != 0) {
            if(now.tv_sec >= update_flush_time.tv_sec)
                flushupdates();
        }

        for(i = 0; i < numnets; i++) {
539 540
            if(!nets[i].up)
                continue;
541 542 543 544 545 546
            if(nets[i].flush_time.tv_sec != 0) {
                if(timeval_compare(&now, &nets[i].flush_time) >= 0)
                    flushbuf(&nets[i]);
            }
        }

547
        if(debug || dumping) {
548
            dump_tables(stdout);
549 550
            dumping = 0;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
551 552 553 554 555 556 557
    }

    debugf("Exiting...\n");
    for(i = 0; i < numroutes; i++) {
        /* Uninstall and retract all routes. */
        if(routes[i].installed) {
            uninstall_route(&routes[i]);
558
            send_update(NULL, 1, routes[i].src->prefix, routes[i].src->plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
559 560 561
        }
    }
    for(i = 0; i < numnets; i++) {
562 563
        if(!nets[i].up)
            continue;
564
        /* Retract exported routes. */
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
565 566 567
        send_self_retract(&nets[i]);
        /* Make sure that we expire quickly from our neighbours'
           association caches. */
568
        send_hello_noupdate(&nets[i], 15 * numnets);
569
        flushupdates();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
570
        flushbuf(&nets[i]);
571
        usleep(50000 + random() % 100000);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
572 573
    }
    for(i = 0; i < numnets; i++) {
574 575
        if(!nets[i].up)
            continue;
576
        /* Make sure they got it. */
577
        send_self_retract(&nets[i]);
578
        send_hello_noupdate(&nets[i], 1);
579
        flushupdates();
580
        flushbuf(&nets[i]);
581
        usleep(50000 + random() % 100000);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
582 583
        kernel_setup_interface(0, nets[i].ifname, nets[i].ifindex);
    }
584
    kernel_setup_socket(0);
585
    kernel_setup(0, do_ipv4);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
586 587 588

    fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    if(fd < 0) {
589
        perror("creat(babel-state)");
590
        unlink(state_file);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
591 592
    } else {
        char buf[100];
593 594
        rc = snprintf(buf, 100, "%s %d %ld\n",
                      format_address(myid), (int)myseqno, (long)now.tv_sec);
595 596
        if(rc < 0 || rc >= 100) {
            fprintf(stderr, "write(babel-state): overflow.\n");
597
            unlink(state_file);
598 599 600 601 602 603 604
        } else {
            rc = write(fd, buf, rc);
            if(rc < 0) {
                perror("write(babel-state)");
                unlink(state_file);
            }
            fsync(fd);
605
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
606 607
        close(fd);
    }
608
    debugf("Done.\n");
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
609 610 611 612 613 614 615
    return 0;

 syntax:
    fprintf(stderr,
            "Syntax: %s "
            "[-m multicast_address] [-p port] [-S state-file]\n"
            "                "
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
616
            "[-h hello] [-H wired_hello] [-i idle_hello]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
617
            "                "
618
            "[-u update] [-k metric] [-4] [-s] [-P] [-c cost]\n"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
619
            "                "
620
            "[-d level] [-x net cost] [-X net cost]... id interface...\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
621 622 623 624
            argv[0]);
    exit(1);

 fail:
625 626 627
    for(i = 0; i < numnets; i++) {
        if(!nets[i].up)
            continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
628
        kernel_setup_interface(0, nets[i].ifname, nets[i].ifindex);
629
    }
630
    kernel_setup_socket(0);
631
    kernel_setup(0, do_ipv4);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
632 633 634 635 636 637 638 639 640
    exit(1);
}

static void
sigexit(int signo)
{
    exiting = 1;
}

641 642 643 644 645 646
static void
sigdump(int signo)
{
    dumping = 1;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
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);
670 671 672 673 674 675

    sigemptyset(&ss);
    sa.sa_handler = sigdump;
    sa.sa_mask = ss;
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);
676 677 678 679 680 681 682 683

#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
684 685
}

686 687 688 689 690 691
static void
dump_tables(FILE *out)
{
    int i;

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

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

695
    for(i = 0; i < numneighs; i++) {
696
        if(neighs[i].id[0] == 0xFF)
697 698 699 700 701 702 703 704 705 706
            continue;
        fprintf(out, "Neighbour %s ", format_address(neighs[i].id));
        fprintf(out, "at %s dev %s reach %04x rxcost %d txcost %d.\n",
               format_address(neighs[i].address),
               neighs[i].network->ifname,
               neighs[i].reach,
               neighbour_rxcost(&neighs[i]),
               neighs[i].txcost);
    }
    for(i = 0; i < numxroutes; i++) {
707 708
        fprintf(out, "%s metric %d (%s)\n",
                format_prefix(xroutes[i].prefix, xroutes[i].plen),
709
                xroutes[i].metric,
710 711 712 713 714
                xroutes[i].exported ?
                xroutes[i].exported > 1 ? "forced" : "exported" :
                "not exported");
    }
    for(i = 0; i < numroutes; i++) {
715 716 717
        int id =
            routes[i].src->plen != 128 ||
            memcmp(routes[i].src->prefix, routes[i].src->address, 16) != 0;
718 719 720
        const unsigned char *nexthop =
            memcmp(routes[i].nexthop, routes[i].neigh->address, 16) == 0 ?
            NULL : routes[i].nexthop;
721
        fprintf(out, "%s metric %d refmetric %d %s%s seqno %d age %d "
722
                "via %s neigh %s%s%s%s\n",
723
                format_prefix(routes[i].src->prefix, routes[i].src->plen),
724 725 726 727
                routes[i].metric, routes[i].refmetric,
                id ? "id " : "",
                id ? format_address(routes[i].src->address) : "",
                (int)routes[i].seqno,
728
                (int)(now.tv_sec - routes[i].time),
729 730
                routes[i].neigh->network->ifname,
                format_address(routes[i].neigh->address),
731 732
                nexthop ? " nexthop " : "",
                nexthop ? format_address(nexthop) : "",
733 734
                routes[i].installed ? " (installed)" :
                route_feasible(&routes[i]) ? " (feasible)" : "");
735 736 737 738
    }
    fflush(out);
}

739
static int
740
kernel_routes_callback(void *closure)
741
{
742
    kernel_routes_changed = 1;
743
    return 1;
744
}