]> git.ipfire.org Git - thirdparty/bird.git/blob - proto/rip/rip.c
Triggered updates should now actually work. Fixed metric=16 -> time it
[thirdparty/bird.git] / proto / rip / rip.c
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;
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 if (P_CF->authtype)
155 rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i);
156
157 DBG( ", sending %d blocks, ", i );
158 if (!i)
159 continue;
160
161 if (ipa_nonzero(c->daddr))
162 i = sk_send_to( s, sizeof( struct rip_packet_heading ) + i*sizeof( struct rip_block ), c->daddr, c->dport );
163 else
164 i = sk_send( s, sizeof( struct rip_packet_heading ) + i*sizeof( struct rip_block ) );
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 if ((!metric) || (metric > P_CF->infinity)) {
298 log( L_WARN "Got metric %d from %I", metric, whotoldme );
299 return;
300 }
301
302 debug( "block: %I tells me: %I/??? available, metric %d... ", whotoldme, network, metric );
303
304 advertise_entry( p, block, whotoldme );
305 }
306
307 #define BAD( x ) { log( L_WARN "RIP/%s: " x, P_NAME ); return 1; }
308
309 static int
310 rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port )
311 {
312 int i;
313 int native_class = 0, authenticated = 0;
314
315 switch( packet->heading.version ) {
316 case RIP_V1: DBG( "Rip1: " ); break;
317 case RIP_V2: DBG( "Rip2: " ); break;
318 default: BAD( "Unknown version" );
319 }
320
321 switch( packet->heading.command ) {
322 case RIPCMD_REQUEST: DBG( "Asked to send my routing table\n" );
323 if (P_CF->honour == HO_NEVER) {
324 log( L_WARN "They asked me to send routing table, but I was told not to do it\n" );
325 return 0;
326 }
327 if ((P_CF->honour == HO_NEIGHBOUR) && (!neigh_find( p, &whotoldme, 0 ))) {
328 log( L_WARN "They asked me to send routing table, but he is not my neighbour\n" );
329 return 0;
330 }
331 rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */
332 break;
333 case RIPCMD_RESPONSE: DBG( "*** Rtable from %I\n", whotoldme );
334 if (port != P_CF->port) {
335 log( L_ERR "%I send me routing info from port %d", whotoldme, port );
336 #if 0
337 return 0;
338 #else
339 log( L_ERR "...ignoring" );
340 #endif
341 }
342
343 if (!neigh_find( p, &whotoldme, 0 )) {
344 log( L_ERR "%I send me routing info but he is not my neighbour", whotoldme );
345 #if 0
346 return 0;
347 #else
348 log( L_ERR "...ignoring" );
349 #endif
350 }
351
352 for (i=0; i<num; i++) {
353 struct rip_block *block = &packet->block[i];
354 if (block->family == 0xffff)
355 if (!i) {
356 if (rip_incoming_authentication(p, (void *) block, packet, num))
357 BAD( "Authentication failed" );
358 authenticated = 1;
359 }
360 if ((!authenticated) && (P_CF->authtype != AT_NONE))
361 BAD( "Packet is not authenticated and it should be" );
362 ipa_ntoh( block->network );
363 #ifndef IPV6
364 ipa_ntoh( block->netmask );
365 ipa_ntoh( block->nexthop );
366 if (packet->heading.version == RIP_V1)
367 block->netmask = ipa_class_mask(block->network);
368 #endif
369 process_block( p, block, whotoldme );
370 }
371 break;
372 case RIPCMD_TRACEON:
373 case RIPCMD_TRACEOFF: BAD( "I was asked for traceon/traceoff" );
374 case 5: BAD( "Some Sun extension around here" );
375 default: BAD( "Unknown command" );
376 }
377
378 rip_reply(p);
379 return 0;
380 }
381
382 static int
383 rip_rx(sock *s, int size)
384 {
385 struct rip_interface *i = s->data;
386 struct proto *p = i->proto;
387 int num;
388
389 CHK_MAGIC;
390 DBG( "RIP: message came: %d bytes\n", size );
391 size -= sizeof( struct rip_packet_heading );
392 if (size < 0) BAD( "Too small packet" );
393 if (size % sizeof( struct rip_block )) BAD( "Odd sized packet" );
394 num = size / sizeof( struct rip_block );
395 if (num>25) BAD( "Too many blocks" );
396
397 rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport );
398 return 1;
399 }
400
401 /*
402 * Interface to rest of bird
403 */
404
405 static void
406 rip_dump_entry( struct rip_entry *e )
407 {
408 debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ",
409 e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric );
410 if (e->flags & RIP_F_EXTERNAL) debug( "[external]" );
411 debug( "\n" );
412 }
413
414 static void
415 rip_timer(timer *t)
416 {
417 struct proto *p = t->data;
418 struct rip_entry *e, *et;
419
420 CHK_MAGIC;
421 DBG( "RIP: tick tock\n" );
422
423 WALK_LIST_DELSAFE( e, et, P->garbage ) {
424 rte *rte;
425 rte = SKIP_BACK( struct rte, u.rip.garbage, e );
426 DBG( "Garbage: " ); rte_dump( rte );
427
428 if (now - rte->u.rip.lastmodX > P_CF->timeout_time) {
429 debug( "RIP: entry is too old: " ); rte_dump( rte );
430 e->metric = P_CF->infinity;
431 }
432
433 if (now - rte->u.rip.lastmodX > P_CF->garbage_time) {
434 debug( "RIP: entry is much too old: " ); rte_dump( rte );
435 rte_discard(p->table, rte);
436 }
437 }
438
439 /* FIXME: we need to do triggered updates */
440
441 DBG( "RIP: Broadcasting routing tables\n" );
442 {
443 struct rip_interface *rif;
444 P->tx_count ++;
445
446 WALK_LIST( rif, P->interfaces ) {
447 struct iface *iface = rif->iface;
448
449 if (!iface) continue;
450 if (rif->patt->mode & IM_QUIET) continue;
451 if (!(iface->flags & IF_UP)) continue;
452
453 rif->triggered = (P->tx_count % 6);
454 rip_sendto( p, IPA_NONE, 0, rif );
455 }
456 }
457
458 DBG( "RIP: tick tock done\n" );
459 }
460
461 static int
462 rip_start(struct proto *p)
463 {
464 struct rip_interface *rif;
465 DBG( "RIP: starting instance...\n" );
466
467 P->magic = RIP_MAGIC;
468 fib_init( &P->rtable, p->pool, sizeof( struct rip_entry ), 0, NULL );
469 init_list( &P->connections );
470 init_list( &P->garbage );
471 init_list( &P->interfaces );
472 P->timer = tm_new( p->pool );
473 P->timer->data = p;
474 P->timer->randomize = 5;
475 P->timer->recurrent = P_CF->period;
476 P->timer->hook = rip_timer;
477 tm_start( P->timer, 5 );
478 rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */
479 add_head( &P->interfaces, NODE rif );
480 CHK_MAGIC;
481
482 rip_init_instance(p);
483
484 DBG( "RIP: ...done\n");
485 return PS_UP;
486 }
487
488 static struct proto *
489 rip_init(struct proto_config *cfg)
490 {
491 struct proto *p = proto_new(cfg, sizeof(struct rip_proto));
492
493 return p;
494 }
495
496 static void
497 rip_dump(struct proto *p)
498 {
499 int i;
500 node *w, *e;
501 struct rip_interface *rif;
502 i = 0;
503
504 CHK_MAGIC;
505 WALK_LIST( w, P->connections ) {
506 struct rip_connection *n = (void *) w;
507 debug( "RIP: connection #%d: %I\n", n->num, n->addr );
508 }
509 i = 0;
510 FIB_WALK( &P->rtable, e ) {
511 debug( "RIP: entry #%d: ", i++ );
512 rip_dump_entry( E );
513 } FIB_WALK_END;
514 i = 0;
515 WALK_LIST( rif, P->interfaces ) {
516 debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy );
517 }
518 }
519
520 static int
521 rip_want_this_if(struct rip_interface *iface)
522 {
523 return 1;
524 }
525
526 static void
527 kill_iface(struct proto *p, struct rip_interface *i)
528 {
529 DBG( "RIP: Interface %s disappeared\n", i->iface->name);
530 rfree(i->sock);
531 mb_free(i);
532 }
533
534 /*
535 * new maybe null if we are creating initial send socket
536 */
537 static struct rip_interface *
538 new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt )
539 {
540 struct rip_interface *rif;
541 int want_multicast = 0;
542
543 rif = mb_allocz(p->pool, sizeof( struct rip_interface ));
544 rif->iface = new;
545 rif->proto = p;
546 rif->busy = NULL;
547 rif->patt = (struct rip_patt *) patt;
548
549 if (rif->patt)
550 want_multicast = (!(rif->patt->mode & IM_BROADCAST)) && (flags & IF_MULTICAST);
551 /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */
552
553 if (want_multicast)
554 DBG( "Doing multicasts!\n" );
555
556 rif->sock = sk_new( p->pool );
557 rif->sock->type = want_multicast?SK_UDP_MC:SK_UDP;
558 rif->sock->sport = P_CF->port;
559 rif->sock->rx_hook = rip_rx;
560 rif->sock->data = rif;
561 rif->sock->rbsize = 10240;
562 rif->sock->iface = new; /* Automagically works for dummy interface */
563 rif->sock->tbuf = mb_alloc( p->pool, sizeof( struct rip_packet ));
564 rif->sock->tx_hook = rip_tx;
565 rif->sock->err_hook = rip_tx_err;
566 rif->sock->daddr = IPA_NONE;
567 rif->sock->dport = P_CF->port;
568 if (new)
569 rif->sock->ttl = 1;
570 else
571 rif->sock->ttl = 30;
572 rif->sock->tos = IP_PREC_INTERNET_CONTROL;
573
574 if (flags & IF_BROADCAST)
575 rif->sock->daddr = new->addr->brd;
576 if (flags & IF_UNNUMBERED) {
577 rif->sock->daddr = new->addr->opposite;
578 log( L_WARN "RIP/%s: rip is not defined over unnumbered links\n", P_NAME );
579 }
580 if (want_multicast) {
581 rif->sock->daddr = ipa_from_u32(0xe0000009);
582 rif->sock->saddr = ipa_from_u32(0xe0000009);
583 }
584
585 if (!ipa_nonzero(rif->sock->daddr)) {
586 log( L_WARN "RIP/%s: interface %s is too strange for me", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
587 } else
588 if (!(rif->patt->mode & IM_NOLISTEN))
589 if (sk_open(rif->sock)<0) {
590 log( L_ERR "RIP/%s: could not listen on %s", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
591 /* Don't try to transmit into this one? Well, why not? This should not happen, anyway :-) */
592 }
593
594 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 );
595
596 return rif;
597 }
598
599 static void
600 rip_if_notify(struct proto *p, unsigned c, struct iface *iface)
601 {
602 DBG( "RIP: if notify\n" );
603 if (iface->flags & IF_IGNORE)
604 return;
605 if (c & IF_CHANGE_DOWN) {
606 struct rip_interface *i;
607 i = find_interface(p, iface);
608 if (i) {
609 rem_node(NODE i);
610 kill_iface(p, i);
611 }
612 }
613 if (c & IF_CHANGE_UP) {
614 struct rip_interface *rif;
615 struct iface_patt *k = iface_patt_match(&P_CF->iface_list, iface);
616
617 if (!k) return; /* We are not interested in this interface */
618 DBG("adding interface %s\n", iface->name );
619 rif = new_iface(p, iface, iface->flags, k);
620 add_head( &P->interfaces, NODE rif );
621 }
622 }
623
624 static struct ea_list *
625 rip_gen_attrs(struct proto *p, struct linpool *pool, int metric, u16 tag)
626 {
627 struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr));
628
629 l->next = NULL;
630 l->flags = EALF_SORTED;
631 l->count = 2;
632 l->attrs[0].id = EA_RIP_TAG;
633 l->attrs[0].flags = 0;
634 l->attrs[0].type = EAF_TYPE_INT | EAF_INLINE;
635 l->attrs[0].u.data = tag;
636 l->attrs[1].id = EA_RIP_TAG;
637 l->attrs[1].flags = 0;
638 l->attrs[1].type = EAF_TYPE_INT | EAF_INLINE;
639 l->attrs[1].u.data = metric;
640 return l;
641 }
642
643 static int
644 rip_import_control(struct proto *p, struct rte **rt, struct ea_list **attrs, struct linpool *pool)
645 {
646 if ((*rt)->attrs->proto == p) /* My own must not be touched */
647 return 1;
648
649 if ((*rt)->attrs->source != RTS_RIP) {
650 struct ea_list *new = rip_gen_attrs(p, pool, 1, 0);
651 new->next = *attrs;
652 *attrs = new;
653 }
654 return 0;
655 }
656
657 static struct ea_list *
658 rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
659 {
660 struct proto *p = rt->attrs->proto;
661 return rip_gen_attrs(p, pool, rt->u.rip.metric, rt->u.rip.tag);
662 }
663
664 static void
665 rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
666 {
667 struct proto *p = rt->attrs->proto;
668
669 rt->u.rip.tag = ea_find(attrs, EA_RIP_TAG)->u.data;
670 rt->u.rip.metric = ea_find(attrs, EA_RIP_TAG)->u.data;
671 }
672
673 static void
674 rip_rt_notify(struct proto *p, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs)
675 {
676 CHK_MAGIC;
677
678 if (old) {
679 struct rip_entry *e = fib_find( &P->rtable, &net->n.prefix, net->n.pxlen );
680 if (!e)
681 log( L_BUG "Deleting nonexistent entry?!" );
682 fib_delete( &P->rtable, e );
683 }
684
685 if (new) {
686 struct rip_entry *e;
687 if (fib_find( &P->rtable, &net->n.prefix, net->n.pxlen ))
688 log( L_BUG "Inserting entry which is already there?" );
689 e = fib_get( &P->rtable, &net->n.prefix, net->n.pxlen );
690
691 e->nexthop = new->attrs->gw;
692 e->tag = ea_find(attrs, EA_RIP_TAG)->u.data;
693 e->metric = ea_find(attrs, EA_RIP_TAG)->u.data;
694 if (e->metric > P_CF->infinity)
695 e->metric = P_CF->infinity;
696 if (!e->metric)
697 e->metric = 1;
698 e->whotoldme = new->attrs->from;
699 e->updated = e->changed = now;
700 e->flags = 0;
701 }
702 }
703
704 static int
705 rip_rte_better(struct rte *new, struct rte *old)
706 {
707 if (ipa_equal(old->attrs->from, new->attrs->from))
708 return 1;
709
710 if (old->u.rip.metric < new->u.rip.metric)
711 return 0;
712
713 if (old->u.rip.metric > new->u.rip.metric)
714 return 1;
715
716 if ((old->u.rip.metric != 16) && (new->u.rip.metric == 16)) {
717 struct proto *p = new->attrs->proto;
718 new->u.rip.lastmodX = now - P_CF->timeout_time; /* Check this: if new metric is 16, act as it was timed out */
719 }
720
721 if ((old->u.rip.metric == new->u.rip.metric) &&
722 ((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 */
723 return 1;
724
725 return 0;
726 }
727
728 static void
729 rip_rte_insert(net *net, rte *rte)
730 {
731 struct proto *p = rte->attrs->proto;
732 rte->u.rip.lastmodX = now;
733 add_head( &P->garbage, &rte->u.rip.garbage );
734 }
735
736 static void
737 rip_rte_remove(net *net, rte *rte)
738 {
739 struct proto *p = rte->attrs->proto;
740 rem_node( &rte->u.rip.garbage );
741 }
742
743 void
744 rip_init_instance(struct proto *p)
745 {
746 p->preference = DEF_PREF_RIP;
747 p->if_notify = rip_if_notify;
748 p->rt_notify = rip_rt_notify;
749 p->import_control = rip_import_control;
750 p->make_tmp_attrs = rip_make_tmp_attrs;
751 p->store_tmp_attrs = rip_store_tmp_attrs;
752 p->rte_better = rip_rte_better;
753 p->rte_insert = rip_rte_insert;
754 p->rte_remove = rip_rte_remove;
755 }
756
757 void
758 rip_init_config(struct rip_proto_config *c)
759 {
760 init_list(&c->iface_list);
761 c->infinity = 16;
762 c->port = 520;
763 c->period = 30;
764 c->garbage_time = 120+180;
765 c->timeout_time = 120;
766 c->passwords = NULL;
767 c->authtype = AT_NONE;
768 }
769
770 static void
771 rip_preconfig(struct protocol *x, struct config *c)
772 {
773 DBG( "RIP: preconfig\n" );
774 }
775
776 static void
777 rip_postconfig(struct proto_config *c)
778 {
779 }
780
781 struct protocol proto_rip = {
782 name: "RIP",
783 preconfig: rip_preconfig,
784 postconfig: rip_postconfig,
785
786 init: rip_init,
787 dump: rip_dump,
788 start: rip_start,
789 };