message.c 26.5 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
/*
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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#include <arpa/inet.h>

30
#include "babel.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
31 32
#include "util.h"
#include "net.h"
33
#include "network.h"
34
#include "source.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
35 36 37
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
38
#include "request.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
39
#include "message.h"
40
#include "filter.h"
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
41 42 43

struct timeval update_flush_time = {0, 0};

44
const unsigned char packet_header[8] = {42, 1};
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
45 46 47 48 49

int parasitic = 0;
int silent_time = 30;
int split_horizon = 1;

50
unsigned short myseqno = 0;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
51 52 53
int seqno_time = 0;
int seqno_interval = -1;

54 55 56 57 58
struct buffered_update {
    unsigned char prefix[16];
    unsigned char plen;
};
struct buffered_update buffered_updates[MAX_BUFFERED_UPDATES];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
59 60 61
struct network *update_net = NULL;
int updates = 0;

62 63 64 65 66
static void
handle_request(struct neighbour *neigh, const unsigned char *prefix,
               unsigned char plen, unsigned char hop_count,
               unsigned short seqno, unsigned short router_hash);

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
67 68 69 70 71 72 73 74 75 76
unsigned short
hash_id(const unsigned char *id)
{
    int i;
    unsigned short hash = 0;
    for(i = 0; i < 8; i++)
        hash ^= (id[2 * i] << 8) | id[2 * i + 1];
    return hash;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
77 78 79 80 81 82
void
parse_packet(const unsigned char *from, struct network *net,
             const unsigned char *packet, int len)
{
    int i, j;
    const unsigned char *message;
83 84
    unsigned char type, plen, hop_count;
    unsigned short seqno, metric;
85
    const unsigned char *address;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
86
    struct neighbour *neigh;
87 88
    int have_current_source = 0;
    unsigned char current_source[16];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
89

90 91 92 93 94 95
    if(from[0] != 0xFE || (from[1] & 0xC0) != 0x80) {
        fprintf(stderr, "Received packet from non-local address %s.\n",
                format_address(from));
        return;
    }

96
    if(packet[0] != 42) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
97 98 99 100 101
        fprintf(stderr, "Received malformed packet on %s from %s.\n",
                net->ifname, format_address(from));
        return;
    }

102
    if(packet[1] != 1) {
103 104 105 106 107 108
        fprintf(stderr,
                "Received packet with unknown version %d on %s from %s.\n",
                packet[1], net->ifname, format_address(from));
        return;
    }

109 110 111 112 113 114
    if(len % 24 != 8) {
        fprintf(stderr, "Received malformed packet on %s from %s.\n",
                net->ifname, format_address(from));
        return;
    }

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
115
    j = 0;
116 117 118 119
    for(i = 0; i < (len - 8) / 24; i++) {
        message = packet + 8 + 24 * i;
        type = message[0];
        plen = message[1];
120
        hop_count = message[3];
121 122 123 124 125 126
        seqno = ntohs(*(uint16_t*)(message + 4));
        metric = ntohs(*(uint16_t*)(message + 6));
        address = message + 8;
        if(type == 0) {
            int changed;
            if(memcmp(address, myid, 16) == 0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
127
                continue;
128 129
            debugf("Received hello (%d) on %s from %s (%s).\n",
                   metric, net->ifname,
130
                   format_address(address),
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
131
                   format_address(from));
132
            net->activity_time = now.tv_sec;
133
            neigh = add_neighbour(address, from, net);
134 135
            if(neigh == NULL)
                continue;
136 137 138
            changed = update_neighbour(neigh, seqno, metric);
            if(changed)
                update_neighbour_metric(neigh);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
139 140 141 142
        } else {
            neigh = find_neighbour(from, net);
            if(neigh == NULL)
                continue;
143
            net->activity_time = now.tv_sec;
144
            if(type == 1) {
145
                debugf("Received ihu %d for %s from %s (%s) %d.\n",
146
                       metric,
147 148
                       format_address(address),
                       format_address(neigh->id),
149
                       format_address(from), seqno);
150
                if(memcmp(myid, address, 16) == 0) {
151
                    neigh->txcost = metric;
152
                    neigh->ihu_time = now;
153
                    neigh->ihu_interval = seqno;
154 155 156
                    update_neighbour_metric(neigh);
                }
            } else if(type == 2) {
157
                debugf("Received request on %s from %s (%s) for %s "
158
                       "(%d hops).\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
159 160 161
                       net->ifname,
                       format_address(neigh->id),
                       format_address(from),
162 163 164 165 166
                       plen == 0xFF ?
                       "any" :
                       format_prefix(address, plen),
                       metric);
                if(plen == 0xFF) {
167
                    /* If a neighbour is requesting a full route dump from us,
168 169
                       we might as well send it an ihu. */
                    send_ihu(neigh, NULL);
170
                    send_update(neigh->network, 0, NULL, 0);
171
                } else {
172 173
                    handle_request(neigh, address, plen,
                                   hop_count, seqno, metric);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
174
                }
175 176 177
            } else if(type == 3) {
                if(plen == 0xFF)
                    debugf("Received update for %s/none on %s from %s (%s).\n",
178
                           format_address(address),
179 180 181 182 183
                           net->ifname,
                           format_address(neigh->id),
                           format_address(from));
                else
                    debugf("Received update for %s on %s from %s (%s).\n",
184
                           format_prefix(address, plen),
185 186 187 188 189 190 191
                           net->ifname,
                           format_address(neigh->id),
                           format_address(from));
                memcpy(current_source, address, 16);
                have_current_source = 1;
                if(memcmp(address, myid, 16) == 0)
                    continue;
192 193 194
                if(plen <= 128) {
                    unsigned char prefix[16];
                    mask_prefix(prefix, address, plen);
195 196
                    update_route(address, prefix, plen, seqno, metric, neigh,
                                 neigh->address);
197
                }
198
            } else if(type == 4) {
199
                unsigned char prefix[16];
200 201
                debugf("Received prefix %s on %s from %s (%s).\n",
                       format_prefix(address, plen),
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
202 203 204
                       net->ifname,
                       format_address(neigh->id),
                       format_address(from));
205 206 207 208 209 210
                if(!have_current_source) {
                    fprintf(stderr, "Received prefix with no source "
                            "on %s from %s (%s).\n",
                            net->ifname,
                            format_address(neigh->id),
                            format_address(from));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
211 212
                    continue;
                }
213 214
                if(memcmp(current_source, myid, 16) == 0)
                    continue;
215
                mask_prefix(prefix, address, plen);
216 217
                update_route(current_source, prefix, plen, seqno, metric,
                             neigh, neigh->address);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
218 219
            } else if(type == 5) {
                unsigned char p4[16], prefix[16], nh[16];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
220
                if(!net->ipv4)
221
                    continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
222 223
                v4tov6(p4, message + 20);
                v4tov6(nh, message + 16);
224
                debugf("Received update for %s nh %s on %s from %s (%s).\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238
                       format_prefix(p4, plen),
                       format_address(nh),
                       net->ifname,
                       format_address(neigh->id),
                       format_address(from));
                if(plen > 32)
                    continue;
                if(!have_current_source)
                    continue;
                if(memcmp(current_source, myid, 16) == 0)
                    continue;
                mask_prefix(prefix, p4, plen + 96);
                update_route(current_source, prefix, plen + 96, seqno, metric,
                             neigh, nh);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
239
            } else {
240 241
                debugf("Received unknown packet type %d from %s (%s).\n",
                       type, format_address(neigh->id), format_address(from));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
242 243 244 245 246 247
            }
        }
    }
    return;
}

248 249 250 251 252
static void
handle_request(struct neighbour *neigh, const unsigned char *prefix,
               unsigned char plen, unsigned char hop_count,
               unsigned short seqno, unsigned short router_hash)
{
253 254 255 256 257 258 259 260
    struct xroute *xroute;
    struct route *route;

    if(hop_count == 0) {
        send_update(neigh->network, 1, prefix, plen);
        return;
    }

261
    xroute = find_xroute(prefix, plen);
262 263 264 265 266 267 268 269 270 271 272 273
    if(xroute) {
        if(router_hash == hash_id(myid) && seqno_compare(seqno, myseqno) > 0)
            update_myseqno(1);
        send_update(neigh->network, 1, prefix, plen);
        return;
    }

    route = find_installed_route(prefix, plen);
    if(route && route->metric < INFINITY) {
        if(router_hash == hash_id(route->src->address) &&
           seqno_compare(seqno, route->seqno) > 0) {
            if(hop_count > 1) {
274
                send_unicast_request(route->neigh, prefix, plen,
275
                                     hop_count - 1, seqno, router_hash);
276
                record_request(prefix, plen, seqno, router_hash,
277
                               neigh->network, 0);
278 279 280 281 282 283
            }
        } else {
            send_update(neigh->network, 1, prefix, plen);
        }
        return;
    }
284 285 286
}


287 288
/* Under normal circumstances, there are enough moderation mechanisms
   elsewhere in the protocol to make sure that this last-ditch check
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
289
   should never trigger.  But I'm supersticious. */
290 291 292 293 294 295

static int
check_bucket(struct network *net)
{
    if(net->bucket > 0 && now.tv_sec > net->bucket_time) {
        net->bucket =
296
            MAX(0, (int)net->bucket - 40 * (now.tv_sec - net->bucket_time));
297 298 299 300 301 302 303 304 305 306 307 308
    }

    net->bucket_time = now.tv_sec;

    if(net->bucket < 400) {
        net->bucket++;
        return 1;
    } else {
        return 0;
    }
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
309 310 311 312 313 314
void
flushbuf(struct network *net)
{
    int rc;
    struct sockaddr_in6 sin6;

315 316 317
    assert(net->buffered <= net->bufsize);

    if(update_net == net)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
318 319 320 321 322
        flushupdates();

    if(net->buffered > 0) {
        debugf("  (flushing %d buffered bytes on %s)\n",
               net->buffered, net->ifname);
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
        if(check_bucket(net)) {
            memset(&sin6, 0, sizeof(sin6));
            sin6.sin6_family = AF_INET6;
            memcpy(&sin6.sin6_addr, protocol_group, 16);
            sin6.sin6_port = htons(protocol_port);
            sin6.sin6_scope_id = net->ifindex;
            rc = babel_send(protocol_socket,
                            packet_header, sizeof(packet_header),
                            net->sendbuf, net->buffered,
                            (struct sockaddr*)&sin6, sizeof(sin6));
            if(rc < 0)
                perror("send");
        } else {
            fprintf(stderr, "Warning: bucket full, dropping packet to %s.\n",
                    net->ifname);
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
339 340 341 342 343 344 345 346 347 348
    }
    VALGRIND_MAKE_MEM_UNDEFINED(net->sendbuf, net->bufsize);
    net->buffered = 0;
    net->flush_time.tv_sec = 0;
    net->flush_time.tv_usec = 0;
}

static void
schedule_flush(struct network *net)
{
349
    int msecs = jitter(net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
350 351 352 353 354 355 356
    if(net->flush_time.tv_sec != 0 &&
       timeval_minus_msec(&net->flush_time, &now) < msecs)
        return;
    net->flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
    net->flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}

357 358 359
void
schedule_flush_now(struct network *net)
{
360 361
    /* Almost now */
    int msecs = 5 + random() % 5;
362 363 364 365 366 367 368
    if(net->flush_time.tv_sec != 0 &&
       timeval_minus_msec(&net->flush_time, &now) < msecs)
        return;
    net->flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
    net->flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
369 370 371
static void
start_message(struct network *net, int bytes)
{
372
    assert(net->buffered % 8 == 0);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386
    if(net->bufsize - net->buffered < bytes)
        flushbuf(net);
}

static void
accumulate_byte(struct network *net, unsigned char byte)
{
    net->sendbuf[net->buffered] = byte;
    net->buffered++;
}

static void
accumulate_short(struct network *net, unsigned short s)
{
387
    *(uint16_t *)(net->sendbuf + net->buffered) = htons(s);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
388 389 390 391 392 393 394 395 396 397 398
    net->buffered += 2;
}

static void
accumulate_data(struct network *net,
                const unsigned char *data, unsigned int len)
{
    memcpy(net->sendbuf + net->buffered, data, len);
    net->buffered += len;
}

399 400
static void
send_message(struct network *net,
401
             unsigned char type,  unsigned char plen, unsigned char hop_count,
402 403 404
             unsigned short seqno, unsigned short metric,
             const unsigned char *address)
{
405
    if(!net->up)
406 407
        return;

408 409 410
    start_message(net, 24);
    accumulate_byte(net, type);
    accumulate_byte(net, plen);
411 412
    accumulate_byte(net, 0);
    accumulate_byte(net, hop_count);
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    accumulate_short(net, seqno);
    accumulate_short(net, metric);
    accumulate_data(net, address, 16);
    schedule_flush(net);
}

static const unsigned char *
message_source_id(struct network *net)
{
    int i;
    assert(net->buffered % 24 == 0);

    i = net->buffered / 24 - 1;
    while(i >= 0) {
        const unsigned char *message;
        message = (const unsigned char*)(net->sendbuf + i * 24);
        if(message[0] == 3)
            return message + 8;
        else if(message[0] == 4)
            i--;
        else
            break;
    }

    return NULL;
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
440
void
441
send_hello_noupdate(struct network *net, unsigned interval)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
442
{
443
    debugf("Sending hello (%d) to %s.\n", interval, net->ifname);
444
    net->hello_seqno = seqno_plus(net->hello_seqno, 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
445
    net->hello_time = now.tv_sec;
446
    send_message(net, 0, 0, 0, net->hello_seqno,
447
                 interval > 0xFFFF ? 0 : interval,
448
                 myid);
449 450 451 452 453 454 455 456
}

void
send_hello(struct network *net)
{
    int changed;
    changed = update_hello_interval(net);
    send_hello_noupdate(net, net->hello_interval * 100);
457 458
    if(changed)
        send_ihu(NULL, net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
459 460 461
}

void
462
send_request(struct network *net,
463 464 465
             const unsigned char *prefix, unsigned char plen,
             unsigned char hop_count, unsigned short seqno,
             unsigned short router_hash)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
466 467 468 469
{
    int i;

    if(net == NULL) {
470 471 472
        for(i = 0; i < numnets; i++) {
            if(!nets[i].up)
                continue;
473
            send_request(&nets[i], prefix, plen, hop_count, seqno, router_hash);
474
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
475 476 477
        return;
    }

478 479 480
    debugf("Sending request to %s for %s (%d hops).\n",
           net->ifname, prefix ? format_prefix(prefix, plen) : "any",
           hop_count);
481
    if(prefix)
482
        send_message(net, 2, plen, hop_count, seqno, router_hash, prefix);
483
    else
484
        send_message(net, 2, 0xFF, 0, 0, 0, ones);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
485 486
}

487 488 489 490 491 492 493 494
void
send_request_resend(const unsigned char *prefix, unsigned char plen,
                    unsigned short seqno, unsigned short router_hash)
{
    send_request(NULL, prefix, plen, 127, seqno, router_hash);
    record_request(prefix, plen, seqno, router_hash, NULL, 2);
}

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
495 496 497 498 499 500
static void
send_unicast_packet(struct neighbour *neigh, unsigned char *buf, int buflen)
{
    struct sockaddr_in6 sin6;
    int rc;

501 502 503
    if(!neigh->network->up)
        return;

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
    if(check_bucket(neigh->network)) {
        memset(&sin6, 0, sizeof(sin6));
        sin6.sin6_family = AF_INET6;
        memcpy(&sin6.sin6_addr, neigh->address, 16);
        sin6.sin6_port = htons(protocol_port);
        sin6.sin6_scope_id = neigh->network->ifindex;
        rc = babel_send(protocol_socket,
                        packet_header, sizeof(packet_header),
                        buf, buflen,
                        (struct sockaddr*)&sin6, sizeof(sin6));
        if(rc < 0)
            perror("send(unicast)");
    } else {
        fprintf(stderr, "Warning: bucket full, dropping packet to %s.\n",
                neigh->network->ifname);
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
520 521 522
}

void
523
send_unicast_request(struct neighbour *neigh,
524 525 526
                     const unsigned char *prefix, unsigned char plen,
                     unsigned char hop_count, unsigned short seqno,
                     unsigned short router_hash)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
527
{
528
    unsigned char buf[24];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
529

530
    debugf("Sending unicast request to %s (%s) for %s (%d hops).\n",
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
531 532
           format_address(neigh->id),
           format_address(neigh->address),
533 534
           prefix ? format_prefix(prefix, plen) : "any",
           hop_count);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
535

536
    buf[0] = 2;
537
    if(prefix) {
538 539 540 541 542
        buf[1] = plen;
        buf[2] = 0;
        buf[3] = hop_count;
        *(uint16_t*)(buf + 4) = seqno;
        *(uint16_t*)(buf + 6) = router_hash;
543
        memcpy(buf + 8, prefix, 16);
544
    } else {
545 546
        buf[1] = 0xFF;
        memset(buf + 2, 0, 6);
547
        memcpy(buf + 8, ones, 16);
548
    }
549 550
    send_unicast_packet(neigh, buf, 24);
}
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
551

552 553 554 555
static void
really_send_update(struct network *net,
                   const unsigned char *address,
                   const unsigned char *prefix, unsigned char plen,
556
                   unsigned short seqno, unsigned short metric)
557
{
558 559
    int add_metric;

560 561 562
    if(!net->up)
        return;

563 564 565
    add_metric = output_filter(address, prefix, plen, net->ifindex);

    if(add_metric < INFINITY) {
566
        if(plen >= 96 && v4mapped(prefix)) {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
567 568
            const unsigned char *sid;
            unsigned char v4route[16];
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
569
            if(!net->ipv4)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
570 571
                return;
            memset(v4route, 0, 8);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
572
            memcpy(v4route + 8, net->ipv4, 4);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
573 574 575 576 577
            memcpy(v4route + 12, prefix + 12, 4);
            start_message(net, 48);
            sid = message_source_id(net);
            if(sid == NULL || memcmp(address, sid, 16) != 0)
                send_message(net, 3, 0xFF, 0, 0, 0xFFFF, address);
578 579
            send_message(net, 5, plen - 96, 0, seqno, metric + add_metric,
                         v4route);
580
        } else {
581 582 583
            if(in_prefix(address, prefix, plen)) {
                send_message(net, 3, plen, 0, seqno, metric, address);
            } else {
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
584
                const unsigned char *sid;
585 586 587 588
                start_message(net, 48);
                sid = message_source_id(net);
                if(sid == NULL || memcmp(address, sid, 16) != 0)
                    send_message(net, 3, 0xFF, 0, 0, 0xFFFF, address);
589 590
                send_message(net, 4, plen, 0, seqno, metric + add_metric,
                             prefix);
591
            }
592
        }
593
    }
594
    satisfy_request(prefix, plen, seqno, hash_id(address), net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
595 596 597 598 599
}

void
flushupdates(void)
{
600
    int i;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
601 602 603 604 605 606 607 608 609 610

    if(updates > 0) {
        /* Ensure that we won't be recursively called by flushbuf. */
        int n = updates;
        struct network *net = update_net;
        updates = 0;
        update_net = NULL;

        debugf("  (flushing %d buffered updates)\n", n);

611 612 613 614 615 616
        for(i = 0; i < n; i++) {
            struct xroute *xroute;
            struct route *route;
            struct source *src;
            unsigned short seqno;
            unsigned short metric;
617 618
            xroute = find_xroute(buffered_updates[i].prefix,
                                 buffered_updates[i].plen);
619 620 621
            if(xroute) {
                really_send_update(net, myid,
                                   xroute->prefix, xroute->plen,
622
                                   myseqno, xroute->metric);
623 624 625 626 627 628
                continue;
            }
            route = find_installed_route(buffered_updates[i].prefix,
                                         buffered_updates[i].plen);
            if(route) {
                if(split_horizon &&
629
                   net->wired && route->neigh->network == net)
630 631
                    continue;
                seqno = route->seqno;
632
                metric = route->metric;
633 634 635
                really_send_update(net, route->src->address,
                                   route->src->prefix,
                                   route->src->plen,
636
                                   seqno, metric);
637 638 639 640 641 642 643 644
                update_source(route->src, seqno, metric);
                continue;
            }
            src = find_recent_source(buffered_updates[i].prefix,
                                     buffered_updates[i].plen);
            if(src) {
                really_send_update(net, src->address, src->prefix, src->plen,
                                   src->metric >= INFINITY ?
645
                                   src->seqno : seqno_plus(src->seqno, 1),
646
                                   INFINITY);
647
                continue;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
648 649 650
            }
        }
        schedule_flush_now(net);
651 652
        VALGRIND_MAKE_MEM_UNDEFINED(&buffered_updates,
                                    sizeof(buffered_updates));
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
653 654 655 656 657 658
    }
    update_flush_time.tv_sec = 0;
    update_flush_time.tv_usec = 0;
}

static void
659
schedule_update_flush(struct network *net, int urgent)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
660
{
661
    int msecs;
662
    msecs = update_jitter(net, urgent);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
663 664 665 666 667 668 669 670
    if(update_flush_time.tv_sec != 0 &&
       timeval_minus_msec(&update_flush_time, &now) < msecs)
        return;
    update_flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
    update_flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}

static void
671 672
buffer_update(struct network *net,
              const unsigned char *prefix, unsigned char plen)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
673 674 675 676 677 678 679 680
{
    int i;

    if(update_net && update_net != net)
        flushupdates();

    update_net = net;

681 682 683
    for(i = 0; i < updates; i++) {
        if(buffered_updates[i].plen == plen &&
           memcmp(buffered_updates[i].prefix, prefix, 16) == 0)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
684
            return;
685
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
686 687 688

    if(updates >= MAX_BUFFERED_UPDATES)
        flushupdates();
689 690 691
    memcpy(buffered_updates[updates].prefix, prefix, 16);
    buffered_updates[updates].plen = plen;
    updates++;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
692 693 694
}

void
695
send_update(struct network *net, int urgent,
696
            const unsigned char *prefix, unsigned char plen)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
697 698
{
    int i;
699 700
    struct request *request;

701 702 703 704 705 706 707 708 709 710 711 712
    if(prefix) {
        /* This is needed here, since really_send_update only handles the
           case where network is not null. */
        request = find_request(prefix, plen, NULL);
        if(request) {
            struct route *route;
            route = find_installed_route(prefix, plen);
            if(route) {
                urgent = 1;
                satisfy_request(prefix, plen, route->seqno,
                                hash_id(route->src->address), net);
            }
713 714
        }
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
715 716

    if(net == NULL) {
717
        for(i = 0; i < numnets; i++) {
718
            send_update(&nets[i], urgent, prefix, plen);
719 720 721
            if(!nets[i].up)
                continue;
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
722 723 724
        return;
    }

725 726 727
    if(!net->up)
        return;

728 729
    if(parasitic || (silent_time && now.tv_sec < reboot_time + silent_time)) {
        if(prefix == NULL) {
730
            send_self_update(net, 0);
731
            net->update_time = now.tv_sec;
732
        } else if(find_xroute(prefix, plen)) {
733 734
            buffer_update(net, prefix, plen);
        }
735 736 737 738 739
        return;
    }

    silent_time = 0;

740 741
    if(prefix) {
        if(updates > net->bufsize / 24 - 2) {
742 743
            /* Update won't fit in current packet */
            flushupdates();
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
744 745
        }
        debugf("Sending update to %s for %s.\n",
746 747
               net->ifname, format_prefix(prefix, plen));
        buffer_update(net, prefix, plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
748
    } else {
749 750
        send_self_update(net, 0);
        if(now.tv_sec - net->update_time < 1)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
751
            return;
752
        debugf("Sending update to %s for any.\n", net->ifname);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
753 754
        for(i = 0; i < numroutes; i++)
            if(routes[i].installed)
755
                buffer_update(net, routes[i].src->prefix, routes[i].src->plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
756 757
        net->update_time = now.tv_sec;
    }
758
    schedule_update_flush(net, urgent);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
759 760 761
}

void
762
update_myseqno(int force)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
763
{
764
    if(force || seqno_time + seqno_interval < now.tv_sec) {
765
        myseqno = seqno_plus(myseqno, 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
766 767
        seqno_time = now.tv_sec;
    }
768 769 770 771 772 773 774 775
}

void
send_self_update(struct network *net, int force_seqno)
{
    int i;

    update_myseqno(force_seqno);
776 777

    if(net == NULL) {
778 779 780
        for(i = 0; i < numnets; i++) {
            if(!nets[i].up)
                continue;
781
            send_self_update(&nets[i], 0);
782
        }
783 784 785 786 787
        return;
    }

    debugf("Sending self update to %s.\n", net->ifname);

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
788
    net->self_update_time = now.tv_sec;
789 790

    for(i = 0; i < numxroutes; i++) {
791
        send_update(net, 0, xroutes[i].prefix, xroutes[i].plen);
792
    }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
793 794 795 796 797
}

void
send_self_retract(struct network *net)
{
798 799
    int i;

800
    if(net == NULL) {
801 802 803
        for(i = 0; i < numnets; i++) {
            if(!nets[i].up)
                continue;
804
            send_self_retract(&nets[i]);
805
        }
806 807 808
        return;
    }

809 810
    flushupdates();

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
811
    debugf("Retracting self on %s.\n", net->ifname);
812

813
    myseqno = seqno_plus(myseqno, 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
814 815
    seqno_time = now.tv_sec;
    net->self_update_time = now.tv_sec;
816
    for(i = 0; i < numxroutes; i++) {
817 818
        really_send_update(net, myid, xroutes[i].prefix, xroutes[i].plen,
                           myseqno, 0xFFFF);
819
    }
820
    schedule_update_flush(net, 1);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
821 822 823 824 825 826 827
}

void
send_neighbour_update(struct neighbour *neigh, struct network *net)
{
    int i;
    for(i = 0; i < numroutes; i++) {
828
        if(routes[i].installed && routes[i].neigh == neigh)
829
            send_update(net, 0, routes[i].src->prefix, routes[i].src->plen);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
830 831 832 833
    }
}

void
834
send_ihu(struct neighbour *neigh, struct network *net)
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
835 836
{
    int i;
837
    unsigned short interval;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
838 839

    if(neigh == NULL && net == NULL) {
840 841 842
        for(i = 0; i < numnets; i++) {
            if(!nets[i].up)
                continue;
843
            send_ihu(NULL, &nets[i]);
844
        }
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
845 846 847 848
        return;
    }

    if(neigh == NULL) {
849 850 851 852
        for(i = 0; i < numneighs; i++) {
            if(neighs[i].id[0] != 0xFF) {
                if(neighs[i].network == net)
                    send_ihu(&neighs[i], net);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
853 854
            }
        }
855
        net->ihu_time = now.tv_sec;
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
856
    } else {
857 858
        int rxcost;

Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
859 860 861 862 863
        if(net && neigh->network != net)
            return;

        net = neigh->network;

864 865
        rxcost = neighbour_rxcost(neigh);

866 867 868 869 870
        if(net->ihu_interval * 100 <= 0xFFFF)
            interval = net->ihu_interval * 100;
        else
            interval = 0;

871 872
        debugf("Sending ihu %d on %s to %s (%s).\n",
               rxcost,
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
873 874 875 876
               neigh->network->ifname,
               format_address(neigh->id),
               format_address(neigh->address));

877
        send_message(net, 1, 128, 0, interval, rxcost, neigh->id);
Juliusz Chroboczek's avatar
Juliusz Chroboczek committed
878 879
    }
}