Statistics
| Branch: | Revision:

iof-bird-daemon / proto / rip / rip.c @ 1a509a63

History | View | Annotate | Download (20.1 KB)

1
/*
2
 *        Rest in pieces - RIP protocol
3
 *
4
 *        Copyright (c) 1998, 1999 Pavel Machek <pavel@ucw.cz>
5
 *
6
 *        Can be freely distributed and used under the terms of the GNU GPL.
7
 *
8
         FIXME: IpV6 support: packet size
9
         FIXME: IpV6 support: use right address for broadcasts
10
        FIXME: IpV6 support: receive "route using" blocks
11

12
        FIXME: fold rip_connection into rip_interface?
13

14
        We are not going to honour requests for sending part of
15
        routing table. That would need to turn split horizont off,
16
        etc.  
17

18
        Triggered updates. When triggered update was sent, don't send
19
        new one for something between 1 and 5 seconds (and send one
20
        after that), says RFC. We do something else: once in 5 second
21
        we look for any changed routes and broadcast them.
22

23
 */
24

    
25
#define LOCAL_DEBUG
26

    
27
#include <string.h>
28
#include <stdlib.h>
29

    
30
#include "nest/bird.h"
31
#include "nest/iface.h"
32
#include "nest/protocol.h"
33
#include "nest/route.h"
34
#include "lib/socket.h"
35
#include "lib/resource.h"
36
#include "lib/lists.h"
37
#include "lib/timer.h"
38

    
39
#include "rip.h"
40

    
41
#define P ((struct rip_proto *) p)
42
#define P_CF ((struct rip_proto_config *)p->cf)
43
#define E ((struct rip_entry *) e)
44

    
45
static struct rip_interface *new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt);
46

    
47
static void
48
rip_reply(struct proto *p)
49
{
50
#if 0
51
  P->listen->tbuf = "ACK!";
52
  sk_send_to( P->listen, 5, P->listen->faddr, P->listen->fport );
53
#endif
54
}
55

    
56
#define P_NAME p->name
57

    
58
/*
59
 * Output processing
60
 */
61

    
62
static void
63
rip_tx_err( sock *s, int err )
64
{
65
  struct rip_connection *c = s->data;
66
  struct proto *p = c->proto;
67
  log( L_ERR "Unexpected error at rip transmit: %m" );
68
}
69

    
70
static void
71
rip_tx_prepare(struct proto *p, ip_addr daddr, struct rip_block *b, struct rip_entry *e )
72
{
73
  DBG( "." );
74
  b->family  = htons( 2 ); /* AF_INET */
75
  b->tag     = htons( e->tag );
76
  b->network = e->n.prefix;
77
#ifndef IPV6
78
  b->netmask = ipa_mkmask( e->n.pxlen );
79
  ipa_hton( b->netmask );
80
  b->nexthop = IPA_NONE;        
81
  {
82
    /*
83
     *  FIXME:  This DOESN'T work. The s->daddr will be typically
84
     *  a broadcast or multicast address which will be of course
85
     *  rejected by the neighbor cache. I've #ifdef'd out the whole
86
     *  test to avoid dereferencing NULL pointer.  --mj
87
     */
88
#if 0
89
    neighbor *n1, *n2;
90
    n1 = neigh_find( p, &daddr, 0 );        /* FIXME, mj: this is neccessary for responses, still it is too complicated for common case */
91
    n2 = neigh_find( p, &e->nexthop, 0 );
92
    if (n1->iface == n2->iface)
93
      b->nexthop = e->nexthop;
94
    else
95
#endif
96
      b->nexthop = IPA_NONE;        
97
  }
98
  ipa_hton( b->nexthop );
99
#else
100
  b->pxlen = e->n.pxlen;
101
#endif
102
  b->metric  = htonl( e->metric );
103
  if (ipa_equal(e->whotoldme, daddr)) {        /* FIXME: ouch, daddr is some kind of broadcast address. How am I expected to do split horizont?!?!? */
104
    DBG( "(split horizont)" );
105
    b->metric = P_CF->infinity;
106
  }
107
  ipa_hton( b->network );
108
}
109

    
110
static void
111
rip_tx( sock *s )
112
{
113
  struct rip_interface *rif = s->data;
114
  struct rip_connection *c = rif->busy;
115
  struct proto *p = c->proto;
116
  struct rip_packet *packet = (void *) s->tbuf;
117
  int i, packetlen;
118

    
119
  DBG( "Sending to %I\n", s->daddr );
120
  do {
121

    
122
    if (c->done) {
123
      DBG( "Looks like I'm" );
124
      c->rif->busy = NULL;
125
      rem_node(NODE c);
126
      mb_free(c);
127
      DBG( " done\n" );
128
      return;
129
    }
130

    
131
    DBG( "Preparing packet to send: " );
132

    
133
    packet->heading.command = RIPCMD_RESPONSE;
134
    packet->heading.version = RIP_V2;
135
    packet->heading.unused  = 0;
136

    
137
    i = !!P_CF->authtype;
138
    FIB_ITERATE_START(&P->rtable, &c->iter, z) {
139
      struct rip_entry *e = (struct rip_entry *) z;
140

    
141
      if (rif->triggered && (!(e->updated < now-5)))
142
        continue;
143

    
144
      rip_tx_prepare( p, s->daddr, packet->block + i, e );
145
      if (i++ == ((P_CF->authtype == AT_MD5) ? PACKET_MD5_MAX : PACKET_MAX)) {
146
        FIB_ITERATE_PUT(&c->iter, z);
147
        goto break_loop;
148
      }
149
    } FIB_ITERATE_END(z);
150
    c->done = 1;
151

    
152
  break_loop:
153

    
154
    packetlen = rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i);
155

    
156
    DBG( ", sending %d blocks, ", i );
157
#if 0        /* FIXME: enable this for production! */
158
    if (i == !!P_CF->authtype)
159
      continue;
160
#endif
161
    if (ipa_nonzero(c->daddr))
162
      i = sk_send_to( s, packetlen, c->daddr, c->dport );
163
    else
164
      i = sk_send( s, packetlen );
165

    
166
    DBG( "it wants more\n" );
167
  
168
  } while (i>0);
169
  
170
  if (i<0) rip_tx_err( s, i );
171
  DBG( "blocked\n" );
172
  return;
173

    
174
}
175

    
176
static void
177
rip_sendto( struct proto *p, ip_addr daddr, int dport, struct rip_interface *rif )
178
{
179
  struct iface *iface = rif->iface;
180
  struct rip_connection *c = mb_alloc( p->pool, sizeof( struct rip_connection ));
181
  static int num = 0;
182

    
183
  if (rif->busy) {
184
    log (L_WARN "Interface %s is much too slow, dropping request", iface->name);
185
    /* FIXME: memory leak */
186
    return;
187
  }
188
  rif->busy = c;
189
  
190
  c->addr = daddr;
191
  c->proto = p;
192
  c->num = num++;
193
  c->rif = rif;
194

    
195
  c->dport = dport;
196
  c->daddr = daddr;
197
  if (c->rif->sock->data != rif)
198
    bug("not enough send magic");
199
#if 0
200
  if (sk_open(c->send)<0) {
201
    log( L_ERR "Could not open socket for data send to %I:%d on %s", daddr, dport, rif->iface->name );
202
    return;
203
  }
204
#endif
205

    
206
  c->done = 0;
207
  fit_init( &c->iter, &P->rtable );
208
  add_head( &P->connections, NODE c );
209
  debug( "Sending my routing table to %I:%d on %s\n", daddr, dport, rif->iface->name );
210

    
211
  rip_tx(c->rif->sock);
212
}
213

    
214
static struct rip_interface*
215
find_interface(struct proto *p, struct iface *what)
216
{
217
  struct rip_interface *i;
218

    
219
  WALK_LIST (i, P->interfaces)
220
    if (i->iface == what)
221
      return i;
222
  return NULL;
223
}
224

    
225
/*
226
 * Input processing
227
 */
228

    
229
/* Let main routing table know about our new entry */
230
static void
231
advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme )
232
{
233
  rta *a, A;
234
  rte *r;
235
  net *n;
236
  neighbor *neighbor;
237
  struct rip_interface *rif;
238
  int pxlen;
239

    
240
  bzero(&A, sizeof(A));
241
  A.proto = p;
242
  A.source = RTS_RIP;
243
  A.scope = SCOPE_UNIVERSE;
244
  A.cast = RTC_UNICAST;
245
  A.dest = RTD_ROUTER;
246
  A.flags = 0;
247
#ifndef IPV6
248
  A.gw = ipa_nonzero(b->nexthop) ? b->nexthop : whotoldme;
249
  pxlen = ipa_mklen(b->netmask);
250
#else
251
  A.gw = whotoldme; /* FIXME: next hop is in other packet for v6 */
252
  pxlen = b->pxlen;
253
#endif
254
  A.from = whotoldme;
255

    
256
  /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */
257

    
258
  neighbor = neigh_find( p, &A.gw, 0 );
259
  if (!neighbor) {
260
    log( L_ERR "%I asked me to route %I/%d using not-neighbor %I.", A.from, b->network, pxlen, A.gw );
261
    return;
262
  }
263

    
264
  A.iface = neighbor->iface;
265
  if (!(rif = neighbor->data)) {
266
    rif = neighbor->data = find_interface(p, A.iface);
267
  }
268
  if (!rif) {
269
    bug("Route packet using unknown interface? No.");
270
    return;
271
  }
272
    
273
  /* set to: interface of nexthop */
274
  a = rta_lookup(&A);
275
  if (pxlen==-1)  {
276
    log( L_ERR "%I gave me invalid pxlen/netmask for %I.", A.from, b->network );
277
    return;
278
  }
279
  n = net_get( p->table, b->network, pxlen );
280
  r = rte_get_temp(a);
281
  r->u.rip.metric = ntohl(b->metric) + rif->patt->metric;
282
  if (r->u.rip.metric > P_CF->infinity) r->u.rip.metric = P_CF->infinity;
283
  r->u.rip.tag = ntohl(b->tag);
284
  r->net = n;
285
  r->pflags = 0; /* Here go my flags */
286
  rte_update( p->table, n, p, r );
287
  DBG( "done\n" );
288
}
289

    
290
static void
291
process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme )
292
{
293
  int metric = ntohl( block->metric );
294
  ip_addr network = block->network;
295

    
296
  CHK_MAGIC;
297
  debug( "block: %I tells me: %I/??? available, metric %d... ", whotoldme, network, metric );
298
  if ((!metric) || (metric > P_CF->infinity)) {
299
    log( L_WARN "Got metric %d from %I", metric, whotoldme );
300
    return;
301
  }
302

    
303
  advertise_entry( p, block, whotoldme );
304
}
305

    
306
#define BAD( x ) { log( L_WARN "RIP/%s: " x, P_NAME ); return 1; }
307

    
308
static int
309
rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port )
310
{
311
  int i;
312
  int native_class = 0, authenticated = 0;
313

    
314
  switch( packet->heading.version ) {
315
  case RIP_V1: DBG( "Rip1: " ); break;
316
  case RIP_V2: DBG( "Rip2: " ); break;
317
  default: BAD( "Unknown version" );
318
  }
319

    
320
  switch( packet->heading.command ) {
321
  case RIPCMD_REQUEST: DBG( "Asked to send my routing table\n" ); 
322
          if (P_CF->honour == HO_NEVER) {
323
            log( L_WARN "They asked me to send routing table, but I was told not to do it\n" );
324
            return 0;
325
          }
326
          if ((P_CF->honour == HO_NEIGHBOUR) && (!neigh_find( p, &whotoldme, 0 ))) {
327
            log( L_WARN "They asked me to send routing table, but he is not my neighbour\n" );
328
            return 0;
329
          }
330
              rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */
331
          break;
332
  case RIPCMD_RESPONSE: DBG( "*** Rtable from %I\n", whotoldme ); 
333
          if (port != P_CF->port) {
334
            log( L_ERR "%I send me routing info from port %d", whotoldme, port );
335
#if 0
336
            return 0;
337
#else
338
            log( L_ERR "...ignoring" );
339
#endif
340
          }
341

    
342
          if (!neigh_find( p, &whotoldme, 0 )) {
343
            log( L_ERR "%I send me routing info but he is not my neighbour", whotoldme );
344
#if 0
345
            return 0;
346
#else
347
            log( L_ERR "...ignoring" );
348
#endif
349
          }
350

    
351
          for (i=0; i<num; i++) {
352
            struct rip_block *block = &packet->block[i];
353
            if (block->family == 0xffff) {
354
              if (i)
355
                continue;        /* md5 tail has this family */
356
              if (rip_incoming_authentication(p, (void *) block, packet, num))
357
                BAD( "Authentication failed" );
358
              authenticated = 1;
359
              continue;
360
            }
361
            if ((!authenticated) && (P_CF->authtype != AT_NONE))
362
              BAD( "Packet is not authenticated and it should be" );
363
            ipa_ntoh( block->network );
364
#ifndef IPV6
365
            ipa_ntoh( block->netmask );
366
            ipa_ntoh( block->nexthop );
367
            if (packet->heading.version == RIP_V1)
368
              block->netmask = ipa_class_mask(block->network);
369
#endif
370
            process_block( p, block, whotoldme );
371
          }
372
          break;
373
  case RIPCMD_TRACEON:
374
  case RIPCMD_TRACEOFF: BAD( "I was asked for traceon/traceoff" );
375
  case 5: BAD( "Some Sun extension around here" );
376
  default: BAD( "Unknown command" );
377
  }
378

    
379
  rip_reply(p);
380
  return 0;
381
}
382

    
383
static int
384
rip_rx(sock *s, int size)
385
{
386
  struct rip_interface *i = s->data;
387
  struct proto *p = i->proto;
388
  int num;
389

    
390
  CHK_MAGIC;
391
  DBG( "RIP: message came: %d bytes\n", size );
392
  size -= sizeof( struct rip_packet_heading );
393
  if (size < 0) BAD( "Too small packet" );
394
  if (size % sizeof( struct rip_block )) BAD( "Odd sized packet" );
395
  num = size / sizeof( struct rip_block );
396
  if (num>25) BAD( "Too many blocks" );
397

    
398
  rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport );
399
  return 1;
400
}
401

    
402
/*
403
 * Interface to rest of bird
404
 */
405

    
406
static void
407
rip_dump_entry( struct rip_entry *e )
408
{
409
  debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ", 
410
  e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric );
411
  if (e->flags & RIP_F_EXTERNAL) debug( "[external]" );
412
  debug( "\n" );
413
}
414

    
415
static void
416
rip_timer(timer *t)
417
{
418
  struct proto *p = t->data;
419
  struct rip_entry *e, *et;
420

    
421
  CHK_MAGIC;
422
  DBG( "RIP: tick tock\n" );
423
  
424
  WALK_LIST_DELSAFE( e, et, P->garbage ) {
425
    rte *rte;
426
    rte = SKIP_BACK( struct rte, u.rip.garbage, e );
427
    DBG( "Garbage: " ); rte_dump( rte );
428

    
429
    if (now - rte->u.rip.lastmodX > P_CF->timeout_time) {
430
      debug( "RIP: entry is too old: " ); rte_dump( rte );
431
      e->metric = P_CF->infinity;
432
    }
433

    
434
    if (now - rte->u.rip.lastmodX > P_CF->garbage_time) {
435
      debug( "RIP: entry is much too old: " ); rte_dump( rte );
436
      rte_discard(p->table, rte);
437
    }
438
  }
439

    
440
  /* FIXME: we need to do triggered updates */
441

    
442
  DBG( "RIP: Broadcasting routing tables\n" );
443
  {
444
    struct rip_interface *rif;
445
    P->tx_count ++;
446

    
447
    WALK_LIST( rif, P->interfaces ) {
448
      struct iface *iface = rif->iface;
449

    
450
      if (!iface) continue;
451
      if (rif->patt->mode & IM_QUIET) continue;
452
      if (!(iface->flags & IF_UP)) continue;
453

    
454
      rif->triggered = (P->tx_count % 6);
455
      rip_sendto( p, IPA_NONE, 0, rif );
456
    }
457
  }
458

    
459
  DBG( "RIP: tick tock done\n" );
460
}
461

    
462
static int
463
rip_start(struct proto *p)
464
{
465
  struct rip_interface *rif;
466
  DBG( "RIP: starting instance...\n" );
467

    
468
  P->magic = RIP_MAGIC;
469
  fib_init( &P->rtable, p->pool, sizeof( struct rip_entry ), 0, NULL );
470
  init_list( &P->connections );
471
  init_list( &P->garbage );
472
  init_list( &P->interfaces );
473
  P->timer = tm_new( p->pool );
474
  P->timer->data = p;
475
  P->timer->randomize = 5;
476
  P->timer->recurrent = P_CF->period; 
477
  P->timer->hook = rip_timer;
478
  tm_start( P->timer, 5 );
479
  rif = new_iface(p, NULL, 0, NULL);        /* Initialize dummy interface */
480
  add_head( &P->interfaces, NODE rif );
481
  CHK_MAGIC;
482

    
483
  rip_init_instance(p);
484

    
485
  DBG( "RIP: ...done\n");
486
  return PS_UP;
487
}
488

    
489
static struct proto *
490
rip_init(struct proto_config *cfg)
491
{
492
  struct proto *p = proto_new(cfg, sizeof(struct rip_proto));
493

    
494
  return p;
495
}
496

    
497
static void
498
rip_dump(struct proto *p)
499
{
500
  int i;
501
  node *w, *e;
502
  struct rip_interface *rif;
503
  i = 0;
504

    
505
  CHK_MAGIC;
506
  WALK_LIST( w, P->connections ) {
507
    struct rip_connection *n = (void *) w;
508
    debug( "RIP: connection #%d: %I\n", n->num, n->addr );
509
  }
510
  i = 0;
511
  FIB_WALK( &P->rtable, e ) {
512
    debug( "RIP: entry #%d: ", i++ );
513
    rip_dump_entry( E );
514
  } FIB_WALK_END;
515
  i = 0;
516
  WALK_LIST( rif, P->interfaces ) {
517
    debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy );
518
  }
519
}
520

    
521
static int
522
rip_want_this_if(struct rip_interface *iface)
523
{
524
  return 1;
525
}
526

    
527
static void
528
kill_iface(struct proto *p, struct rip_interface *i)
529
{
530
  DBG( "RIP: Interface %s disappeared\n", i->iface->name);
531
  rfree(i->sock);
532
  mb_free(i);
533
}
534

    
535
/*
536
 * new maybe null if we are creating initial send socket 
537
 */
538
static struct rip_interface *
539
new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt )
540
{
541
  struct rip_interface *rif;
542
  int want_multicast = 0;
543

    
544
  rif = mb_allocz(p->pool, sizeof( struct rip_interface ));
545
  rif->iface = new;
546
  rif->proto = p;
547
  rif->busy = NULL;
548
  rif->patt = (struct rip_patt *) patt;
549

    
550
  if (rif->patt)
551
    want_multicast = (!(rif->patt->mode & IM_BROADCAST)) && (flags & IF_MULTICAST);
552
  /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */
553

    
554
  if (want_multicast)
555
    DBG( "Doing multicasts!\n" );
556

    
557
  rif->sock = sk_new( p->pool );
558
  rif->sock->type = want_multicast?SK_UDP_MC:SK_UDP;
559
  rif->sock->sport = P_CF->port;
560
  rif->sock->rx_hook = rip_rx;
561
  rif->sock->data = rif;
562
  rif->sock->rbsize = 10240;
563
  rif->sock->iface = new;                /* Automagically works for dummy interface */
564
  rif->sock->tbuf = mb_alloc( p->pool, sizeof( struct rip_packet ));
565
  rif->sock->tx_hook = rip_tx;
566
  rif->sock->err_hook = rip_tx_err;
567
  rif->sock->daddr = IPA_NONE;
568
  rif->sock->dport = P_CF->port;
569
  if (new)
570
    rif->sock->ttl = 1;
571
  else
572
    rif->sock->ttl = 30;
573
  rif->sock->tos = IP_PREC_INTERNET_CONTROL;
574

    
575
  if (flags & IF_BROADCAST)
576
    rif->sock->daddr = new->addr->brd;
577
  if (flags & IF_UNNUMBERED) {
578
    rif->sock->daddr = new->addr->opposite;
579
    log( L_WARN "RIP/%s: rip is not defined over unnumbered links\n", P_NAME );
580
  }
581
  if (want_multicast) {
582
    rif->sock->daddr = ipa_from_u32(0xe0000009);
583
    rif->sock->saddr = ipa_from_u32(0xe0000009);
584
  }
585

    
586
  if (!ipa_nonzero(rif->sock->daddr)) {
587
    log( L_WARN "RIP/%s: interface %s is too strange for me", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
588
  } else
589
    if (!(rif->patt->mode & IM_NOLISTEN))
590
      if (sk_open(rif->sock)<0) {
591
        log( L_ERR "RIP/%s: could not listen on %s", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
592
        /* Don't try to transmit into this one? Well, why not? This should not happen, anyway :-) */
593
      }
594

    
595
  log( L_DEBUG "RIP/%s: listening on %s, port %d, mode %s (%I)", P_NAME, rif->iface ? rif->iface->name : "(dummy)", P_CF->port, want_multicast ? "multicast" : "broadcast", rif->sock->daddr );
596
  
597
  return rif;
598
}
599

    
600
static void
601
rip_if_notify(struct proto *p, unsigned c, struct iface *iface)
602
{
603
  DBG( "RIP: if notify\n" );
604
  if (iface->flags & IF_IGNORE)
605
    return;
606
  if (c & IF_CHANGE_DOWN) {
607
    struct rip_interface *i;
608
    i = find_interface(p, iface);
609
    if (i) {
610
      rem_node(NODE i);
611
      kill_iface(p, i);
612
    }
613
  }
614
  if (c & IF_CHANGE_UP) {
615
    struct rip_interface *rif;
616
    struct iface_patt *k = iface_patt_match(&P_CF->iface_list, iface);
617

    
618
    if (!k) return; /* We are not interested in this interface */
619
    DBG("adding interface %s\n", iface->name );
620
    rif = new_iface(p, iface, iface->flags, k);
621
    add_head( &P->interfaces, NODE rif );
622
  }
623
}
624

    
625
static struct ea_list *
626
rip_gen_attrs(struct proto *p, struct linpool *pool, int metric, u16 tag)
627
{
628
  struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr));
629

    
630
  l->next = NULL;
631
  l->flags = EALF_SORTED;
632
  l->count = 2;
633
  l->attrs[0].id = EA_RIP_TAG;
634
  l->attrs[0].flags = 0;
635
  l->attrs[0].type = EAF_TYPE_INT | EAF_INLINE;
636
  l->attrs[0].u.data = tag;
637
  l->attrs[1].id = EA_RIP_TAG;
638
  l->attrs[1].flags = 0;
639
  l->attrs[1].type = EAF_TYPE_INT | EAF_INLINE;
640
  l->attrs[1].u.data = metric;
641
  return l;
642
}
643

    
644
static int
645
rip_import_control(struct proto *p, struct rte **rt, struct ea_list **attrs, struct linpool *pool)
646
{
647
  if ((*rt)->attrs->proto == p)        /* My own must not be touched */
648
    return 1;
649

    
650
  if ((*rt)->attrs->source != RTS_RIP) {
651
    struct ea_list *new = rip_gen_attrs(p, pool, 1, 0);
652
    new->next = *attrs;
653
    *attrs = new;
654
  }
655
  return 0;
656
}
657

    
658
static struct ea_list *
659
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
660
{
661
  struct proto *p = rt->attrs->proto;
662
  return rip_gen_attrs(p, pool, rt->u.rip.metric, rt->u.rip.tag);
663
}
664

    
665
static void 
666
rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
667
{
668
  struct proto *p = rt->attrs->proto;
669

    
670
  rt->u.rip.tag = ea_find(attrs, EA_RIP_TAG)->u.data;
671
  rt->u.rip.metric = ea_find(attrs, EA_RIP_TAG)->u.data;
672
}
673

    
674
static void
675
rip_rt_notify(struct proto *p, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs)
676
{
677
  CHK_MAGIC;
678

    
679
  if (old) {
680
    struct rip_entry *e = fib_find( &P->rtable, &net->n.prefix, net->n.pxlen );
681
    if (!e)
682
      log( L_BUG "Deleting nonexistent entry?!" );
683
    fib_delete( &P->rtable, e );
684
  }
685

    
686
  if (new) {
687
    struct rip_entry *e;
688
    if (fib_find( &P->rtable, &net->n.prefix, net->n.pxlen ))
689
      log( L_BUG "Inserting entry which is already there?" );
690
    e = fib_get( &P->rtable, &net->n.prefix, net->n.pxlen );
691

    
692
    e->nexthop = new->attrs->gw;
693
    e->tag = ea_find(attrs, EA_RIP_TAG)->u.data;
694
    e->metric = ea_find(attrs, EA_RIP_TAG)->u.data;
695
    if (e->metric > P_CF->infinity)
696
      e->metric = P_CF->infinity;
697
    if (!e->metric)
698
      e->metric = 1;
699
    e->whotoldme = new->attrs->from;
700
    e->updated = e->changed = now;
701
    e->flags = 0;
702
  }
703
}
704

    
705
static int
706
rip_rte_better(struct rte *new, struct rte *old)
707
{
708
  if (ipa_equal(old->attrs->from, new->attrs->from))
709
    return 1;
710

    
711
  if (old->u.rip.metric < new->u.rip.metric)
712
    return 0;
713

    
714
  if (old->u.rip.metric > new->u.rip.metric)
715
    return 1;
716

    
717
  if ((old->u.rip.metric != 16) && (new->u.rip.metric == 16)) {
718
    struct proto *p = new->attrs->proto;
719
    new->u.rip.lastmodX = now - P_CF->timeout_time;        /* Check this: if new metric is 16, act as it was timed out */
720
  }
721

    
722
  if ((old->u.rip.metric == new->u.rip.metric) &&
723
      ((now - old->u.rip.lastmodX) > 60))        /* FIXME (nonurgent): this probably should be P_CF->timeout_time / 2 if old->attrs->proto == new->attrs->proto, else don't do this check */
724
    return 1;
725

    
726
  return 0;
727
}
728

    
729
static void
730
rip_rte_insert(net *net, rte *rte)
731
{
732
  struct proto *p = rte->attrs->proto;
733
  rte->u.rip.lastmodX = now;
734
  add_head( &P->garbage, &rte->u.rip.garbage );
735
}
736

    
737
static void
738
rip_rte_remove(net *net, rte *rte)
739
{
740
  struct proto *p = rte->attrs->proto;
741
  rem_node( &rte->u.rip.garbage );
742
}
743

    
744
void
745
rip_init_instance(struct proto *p)
746
{
747
  p->preference = DEF_PREF_RIP;
748
  p->if_notify = rip_if_notify;
749
  p->rt_notify = rip_rt_notify;
750
  p->import_control = rip_import_control;
751
  p->make_tmp_attrs = rip_make_tmp_attrs;
752
  p->store_tmp_attrs = rip_store_tmp_attrs;
753
  p->rte_better = rip_rte_better;
754
  p->rte_insert = rip_rte_insert;
755
  p->rte_remove = rip_rte_remove;
756
}
757

    
758
void
759
rip_init_config(struct rip_proto_config *c)
760
{
761
  init_list(&c->iface_list);
762
  c->infinity        = 16;
763
  c->port        = 520;
764
  c->period        = 30;
765
  c->garbage_time = 120+180;
766
  c->timeout_time = 120;
767
  c->passwords        = NULL;
768
  c->authtype        = AT_NONE;
769
}
770

    
771
static void
772
rip_preconfig(struct protocol *x, struct config *c)
773
{
774
  DBG( "RIP: preconfig\n" );
775
}
776

    
777
static void
778
rip_postconfig(struct proto_config *c)
779
{
780
}
781

    
782
struct protocol proto_rip = {
783
  name: "RIP",
784
  preconfig: rip_preconfig,
785
  postconfig: rip_postconfig,
786

    
787
  init: rip_init,
788
  dump: rip_dump,
789
  start: rip_start,
790
};