]> git.ipfire.org Git - thirdparty/bird.git/blob - proto/rip/rip.c
Spelling fixes.
[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: (nonurgent) IpV6 support: receive "route using" blocks
10 FIXME: (nonurgent) IpV6 support: generate "nexthop" blocks
11 next hops are only advisory, and they are pretty ugly in IpV6.
12 I suggest just forgetting about them.
13
14 FIXME (nonurgent): fold rip_connection into rip_interface?
15
16 FIXME: (nonurgent) allow bigger frequencies than 1 regular update in 6 seconds (?)
17 FIXME: propagation of metric=infinity into main routing table may or may not be good idea.
18
19 */
20
21 /**
22 * DOC: Routing information protocol
23 *
24 * Rip is pretty simple protocol so half of this code is interface
25 * with core. We maintain our own linklist of &rip_entry - it serves
26 * as our small routing table. Within rip_tx(), this list is
27 * walked, and packet is generated using rip_tx_prepare(). This gets
28 * tricky because we may need to send more than one packet to one
29 * destination. Struct &rip_connection is used to hold info such as how
30 * many of &rip_entry ies we already send, and is also used to protect
31 * from two concurrent sends to one destination. Each &rip_interface has
32 * at most one &rip_connection.
33 *
34 * We are not going to honor requests for sending part of
35 * routing table. That would need to turn split horizon off,
36 * etc.
37 *
38 * Triggered updates. RFC says: when triggered update was sent, don't send
39 * new one for something between 1 and 5 seconds (and send one
40 * after that). We do something else: once in 5 second
41 * we look for any changed routes and broadcast them.
42 */
43
44
45 #define LOCAL_DEBUG
46
47 #include "nest/bird.h"
48 #include "nest/iface.h"
49 #include "nest/protocol.h"
50 #include "nest/route.h"
51 #include "lib/socket.h"
52 #include "lib/resource.h"
53 #include "lib/lists.h"
54 #include "lib/timer.h"
55 #include "lib/string.h"
56
57 #include "rip.h"
58
59 #define P ((struct rip_proto *) p)
60 #define P_CF ((struct rip_proto_config *)p->cf)
61 #define E ((struct rip_entry *) e)
62
63 #define TRACE(level, msg, args...) do { if (p->debug & level) { log(L_TRACE "%s: " msg, p->name , ## args); } } while(0)
64
65 static struct rip_interface *new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt);
66
67 #define P_NAME p->name
68
69 /*
70 * Output processing
71 */
72
73 static void
74 rip_tx_err( sock *s, int err )
75 {
76 struct rip_connection *c = s->data;
77 struct proto *p = c->proto;
78 log( L_ERR "Unexpected error at rip transmit: %M", err );
79 }
80
81 static int
82 rip_tx_prepare(struct proto *p, ip_addr daddr, struct rip_block *b, struct rip_entry *e, struct rip_interface *rif, int pos )
83 {
84 DBG( "." );
85 b->tag = htons( e->tag );
86 b->network = e->n.prefix;
87 #ifndef IPV6
88 b->family = htons( 2 ); /* AF_INET */
89 b->netmask = ipa_mkmask( e->n.pxlen );
90 ipa_hton( b->netmask );
91
92 if (neigh_connected_to(p, &e->nexthop, rif->iface))
93 b->nexthop = e->nexthop;
94 else
95 b->nexthop = IPA_NONE;
96 ipa_hton( b->nexthop );
97 #else
98 b->pxlen = e->n.pxlen;
99 #endif
100 b->metric = htonl( e->metric );
101 if (neigh_connected_to(p, &e->whotoldme, rif->iface)) {
102 DBG( "(split horizon)" );
103 b->metric = htonl( P_CF->infinity );
104 }
105 ipa_hton( b->network );
106
107 return pos+1;
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 int maxi, nullupdate = 1;
119
120 DBG( "Sending to %I\n", s->daddr );
121 do {
122
123 if (c->done)
124 goto done;
125
126 DBG( "Preparing packet to send: " );
127
128 packet->heading.command = RIPCMD_RESPONSE;
129 packet->heading.version = RIP_V2;
130 packet->heading.unused = 0;
131
132 i = !!P_CF->authtype;
133 #ifndef IPV6
134 maxi = ((P_CF->authtype == AT_MD5) ? PACKET_MD5_MAX : PACKET_MAX);
135 #else
136 maxi = 5; /* We need to have at least reserve of one at end of packet */
137 #endif
138
139 FIB_ITERATE_START(&P->rtable, &c->iter, z) {
140 struct rip_entry *e = (struct rip_entry *) z;
141
142 if (!rif->triggered || (!(e->updated < now-5))) {
143 nullupdate = 0;
144 i = rip_tx_prepare( p, s->daddr, packet->block + i, e, rif, i );
145 if (i >= maxi) {
146 FIB_ITERATE_PUT(&c->iter, z);
147 goto break_loop;
148 }
149 }
150 } FIB_ITERATE_END(z);
151 c->done = 1;
152
153 break_loop:
154
155 packetlen = rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i);
156
157 DBG( ", sending %d blocks, ", i );
158 if (nullupdate) {
159 DBG( "not sending NULL update\n" );
160 c->done = 1;
161 goto done;
162 }
163 if (ipa_nonzero(c->daddr))
164 i = sk_send_to( s, packetlen, c->daddr, c->dport );
165 else
166 i = sk_send( s, packetlen );
167
168 DBG( "it wants more\n" );
169
170 } while (i>0);
171
172 if (i<0) rip_tx_err( s, i );
173 DBG( "blocked\n" );
174 return;
175
176 done:
177 DBG( "Looks like I'm" );
178 c->rif->busy = NULL;
179 rem_node(NODE c);
180 mb_free(c);
181 DBG( " done\n" );
182 return;
183 }
184
185 static void
186 rip_sendto( struct proto *p, ip_addr daddr, int dport, struct rip_interface *rif )
187 {
188 struct iface *iface = rif->iface;
189 struct rip_connection *c;
190 static int num = 0;
191
192 if (rif->busy) {
193 log (L_WARN "Interface %s is much too slow, dropping request", iface->name);
194 return;
195 }
196 c = mb_alloc( p->pool, sizeof( struct rip_connection ));
197 rif->busy = c;
198
199 c->addr = daddr;
200 c->proto = p;
201 c->num = num++;
202 c->rif = rif;
203
204 c->dport = dport;
205 c->daddr = daddr;
206 if (c->rif->sock->data != rif)
207 bug("not enough send magic");
208 #if 0
209 if (sk_open(c->send)<0) {
210 log( L_ERR "Could not open socket for data send to %I:%d on %s", daddr, dport, rif->iface->name );
211 return;
212 }
213 #endif
214
215 c->done = 0;
216 fit_init( &c->iter, &P->rtable );
217 add_head( &P->connections, NODE c );
218 TRACE(D_PACKETS, "Sending my routing table to %I:%d on %s\n", daddr, dport, rif->iface->name );
219
220 rip_tx(c->rif->sock);
221 }
222
223 static struct rip_interface*
224 find_interface(struct proto *p, struct iface *what)
225 {
226 struct rip_interface *i;
227
228 WALK_LIST (i, P->interfaces)
229 if (i->iface == what)
230 return i;
231 return NULL;
232 }
233
234 /*
235 * Input processing
236 */
237
238 /* Let main routing table know about our new entry */
239 static void
240 advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme )
241 {
242 rta *a, A;
243 rte *r;
244 net *n;
245 neighbor *neighbor;
246 struct rip_interface *rif;
247 int pxlen;
248
249 bzero(&A, sizeof(A));
250 A.proto = p;
251 A.source = RTS_RIP;
252 A.scope = SCOPE_UNIVERSE;
253 A.cast = RTC_UNICAST;
254 A.dest = RTD_ROUTER;
255 A.flags = 0;
256 #ifndef IPV6
257 A.gw = ipa_nonzero(b->nexthop) ? b->nexthop : whotoldme;
258 pxlen = ipa_mklen(b->netmask);
259 #else
260 A.gw = whotoldme; /* FIXME: next hop is in other packet for v6 */
261 pxlen = b->pxlen;
262 #endif
263 A.from = whotoldme;
264
265 /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */
266
267 neighbor = neigh_find( p, &A.gw, 0 );
268 if (!neighbor) {
269 log( L_REMOTE "%I asked me to route %I/%d using not-neighbor %I.", A.from, b->network, pxlen, A.gw );
270 return;
271 }
272
273 A.iface = neighbor->iface;
274 if (!(rif = neighbor->data)) {
275 rif = neighbor->data = find_interface(p, A.iface);
276 }
277 if (!rif) {
278 bug("Route packet using unknown interface? No.");
279 return;
280 }
281
282 /* set to: interface of nexthop */
283 a = rta_lookup(&A);
284 if (pxlen==-1) {
285 log( L_REMOTE "%I gave me invalid pxlen/netmask for %I.", A.from, b->network );
286 return;
287 }
288 n = net_get( p->table, b->network, pxlen );
289 r = rte_get_temp(a);
290 r->u.rip.metric = ntohl(b->metric) + rif->patt->metric;
291 if (r->u.rip.metric > P_CF->infinity) r->u.rip.metric = P_CF->infinity;
292 r->u.rip.tag = ntohl(b->tag);
293 r->net = n;
294 r->pflags = 0; /* Here go my flags */
295 rte_update( p->table, n, p, r );
296 DBG( "done\n" );
297 }
298
299 static void
300 process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme )
301 {
302 int metric = ntohl( block->metric );
303 ip_addr network = block->network;
304
305 CHK_MAGIC;
306 TRACE(D_ROUTES, "block: %I tells me: %I/??? available, metric %d... ", whotoldme, network, metric );
307 if ((!metric) || (metric > P_CF->infinity)) {
308 #ifdef IPV6 /* Someone is sedning us nexthop and we are ignoring it */
309 if (metric == 0xff)
310 { debug( "IpV6 nexthop ignored" ); return; }
311 #endif
312 log( L_WARN "Got metric %d from %I", metric, whotoldme );
313 return;
314 }
315
316 advertise_entry( p, block, whotoldme );
317 }
318
319 #define BAD( x ) { log( L_REMOTE "%s: " x, P_NAME ); return 1; }
320
321 static int
322 rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port )
323 {
324 int i;
325 int native_class = 0, authenticated = 0;
326
327 switch( packet->heading.version ) {
328 case RIP_V1: DBG( "Rip1: " ); break;
329 case RIP_V2: DBG( "Rip2: " ); break;
330 default: BAD( "Unknown version" );
331 }
332
333 switch( packet->heading.command ) {
334 case RIPCMD_REQUEST: DBG( "Asked to send my routing table\n" );
335 if (P_CF->honor == HO_NEVER) {
336 log( L_REMOTE "They asked me to send routing table, but I was told not to do it" );
337 return 0;
338 }
339 if ((P_CF->honor == HO_NEIGHBOR) && (!neigh_find( p, &whotoldme, 0 ))) {
340 log( L_REMOTE "They asked me to send routing table, but he is not my neighbor" );
341 return 0;
342 }
343 rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */
344 break;
345 case RIPCMD_RESPONSE: DBG( "*** Rtable from %I\n", whotoldme );
346 if (port != P_CF->port) {
347 log( L_REMOTE "%I send me routing info from port %d", whotoldme, port );
348 #if 0
349 return 0;
350 #else
351 log( L_REMOTE "...ignoring" );
352 #endif
353 }
354
355 if (!neigh_find( p, &whotoldme, 0 )) {
356 log( L_REMOTE "%I send me routing info but he is not my neighbor", whotoldme );
357 #if 0
358 return 0;
359 #else
360 log( L_REMOTE "...ignoring" );
361 #endif
362 }
363
364 for (i=0; i<num; i++) {
365 struct rip_block *block = &packet->block[i];
366 #ifndef IPV6
367 /* Authentication is not defined for v6 */
368 if (block->family == 0xffff) {
369 if (i)
370 continue; /* md5 tail has this family */
371 if (rip_incoming_authentication(p, (void *) block, packet, num, whotoldme))
372 BAD( "Authentication failed" );
373 authenticated = 1;
374 continue;
375 }
376 #endif
377 if ((!authenticated) && (P_CF->authtype != AT_NONE))
378 BAD( "Packet is not authenticated and it should be" );
379 ipa_ntoh( block->network );
380 #ifndef IPV6
381 ipa_ntoh( block->netmask );
382 ipa_ntoh( block->nexthop );
383 if (packet->heading.version == RIP_V1) /* FIXME (nonurgent): switch to disable this? */
384 block->netmask = ipa_class_mask(block->network);
385 #endif
386 process_block( p, block, whotoldme );
387 }
388 break;
389 case RIPCMD_TRACEON:
390 case RIPCMD_TRACEOFF: BAD( "I was asked for traceon/traceoff" );
391 case 5: BAD( "Some Sun extension around here" );
392 default: BAD( "Unknown command" );
393 }
394
395 return 0;
396 }
397
398 static int
399 rip_rx(sock *s, int size)
400 {
401 struct rip_interface *i = s->data;
402 struct proto *p = i->proto;
403 int num;
404
405 CHK_MAGIC;
406 DBG( "RIP: message came: %d bytes\n", size );
407 size -= sizeof( struct rip_packet_heading );
408 if (size < 0) BAD( "Too small packet" );
409 if (size % sizeof( struct rip_block )) BAD( "Odd sized packet" );
410 num = size / sizeof( struct rip_block );
411 if (num>PACKET_MAX) BAD( "Too many blocks" );
412
413 rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport );
414 return 1;
415 }
416
417 /*
418 * Interface to rest of bird
419 */
420
421 static void
422 rip_dump_entry( struct rip_entry *e )
423 {
424 debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ",
425 e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric );
426 if (e->flags & RIP_F_EXTERNAL) debug( "[external]" );
427 debug( "\n" );
428 }
429
430 static void
431 rip_timer(timer *t)
432 {
433 struct proto *p = t->data;
434 struct rip_entry *e, *et;
435
436 CHK_MAGIC;
437 DBG( "RIP: tick tock\n" );
438
439 WALK_LIST_DELSAFE( e, et, P->garbage ) {
440 rte *rte;
441 rte = SKIP_BACK( struct rte, u.rip.garbage, e );
442 DBG( "Garbage: " ); rte_dump( rte );
443
444 if (now - rte->u.rip.lastmodX > P_CF->timeout_time) {
445 TRACE(D_EVENTS, "RIP: entry is too old: %I", rte->net->n.prefix );
446 e->metric = P_CF->infinity;
447 }
448
449 if (now - rte->u.rip.lastmodX > P_CF->garbage_time) {
450 TRACE(D_EVENTS, "RIP: entry is much too old: %I", rte->net->n.prefix );
451 rte_discard(p->table, rte);
452 }
453 }
454
455 DBG( "RIP: Broadcasting routing tables\n" );
456 {
457 struct rip_interface *rif;
458 P->tx_count ++;
459
460 WALK_LIST( rif, P->interfaces ) {
461 struct iface *iface = rif->iface;
462
463 if (!iface) continue;
464 if (rif->patt->mode & IM_QUIET) continue;
465 if (!(iface->flags & IF_UP)) continue;
466
467 rif->triggered = (P->tx_count % 6);
468 rip_sendto( p, IPA_NONE, 0, rif );
469 }
470 }
471
472 DBG( "RIP: tick tock done\n" );
473 }
474
475 /**
476 * rip_start - initialize instance of rip
477 */
478 static int
479 rip_start(struct proto *p)
480 {
481 struct rip_interface *rif;
482 DBG( "RIP: starting instance...\n" );
483
484 P->magic = RIP_MAGIC;
485 fib_init( &P->rtable, p->pool, sizeof( struct rip_entry ), 0, NULL );
486 init_list( &P->connections );
487 init_list( &P->garbage );
488 init_list( &P->interfaces );
489 P->timer = tm_new( p->pool );
490 P->timer->data = p;
491 P->timer->randomize = 5;
492 P->timer->recurrent = (P_CF->period / 6)+1;
493 P->timer->hook = rip_timer;
494 tm_start( P->timer, 5 );
495 rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */
496 add_head( &P->interfaces, NODE rif );
497 CHK_MAGIC;
498
499 rip_init_instance(p);
500
501 DBG( "RIP: ...done\n");
502 return PS_UP;
503 }
504
505 static struct proto *
506 rip_init(struct proto_config *cfg)
507 {
508 struct proto *p = proto_new(cfg, sizeof(struct rip_proto));
509
510 return p;
511 }
512
513 static void
514 rip_dump(struct proto *p)
515 {
516 int i;
517 node *w, *e;
518 struct rip_interface *rif;
519 i = 0;
520
521 CHK_MAGIC;
522 WALK_LIST( w, P->connections ) {
523 struct rip_connection *n = (void *) w;
524 debug( "RIP: connection #%d: %I\n", n->num, n->addr );
525 }
526 i = 0;
527 FIB_WALK( &P->rtable, e ) {
528 debug( "RIP: entry #%d: ", i++ );
529 rip_dump_entry( E );
530 } FIB_WALK_END;
531 i = 0;
532 WALK_LIST( rif, P->interfaces ) {
533 debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy );
534 }
535 }
536
537 static void
538 rip_get_route_info(rte *rte, byte *buf)
539 {
540 buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric );
541 bsprintf(buf, " t%04x", rte->u.rip.tag );
542 }
543
544 static int
545 rip_want_this_if(struct rip_interface *iface)
546 {
547 return 1;
548 }
549
550 static void
551 kill_iface(struct proto *p, struct rip_interface *i)
552 {
553 DBG( "RIP: Interface %s disappeared\n", i->iface->name);
554 rfree(i->sock);
555 mb_free(i);
556 }
557
558 /**
559 * new_iface - actually create struct interface and start listening to it
560 * @new: interface to be created or %NULL if we are creating magic
561 * socket. Magic socket is used for listening, and is also used for
562 * sending requested responses.
563 */
564 static struct rip_interface *
565 new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt )
566 {
567 struct rip_interface *rif;
568
569 rif = mb_allocz(p->pool, sizeof( struct rip_interface ));
570 rif->iface = new;
571 rif->proto = p;
572 rif->busy = NULL;
573 rif->patt = (struct rip_patt *) patt;
574
575 if (rif->patt)
576 rif->multicast = (!(rif->patt->mode & IM_BROADCAST)) && (flags & IF_MULTICAST);
577 /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */
578
579 if (rif->multicast)
580 DBG( "Doing multicasts!\n" );
581
582 rif->sock = sk_new( p->pool );
583 rif->sock->type = rif->multicast?SK_UDP_MC:SK_UDP;
584 rif->sock->sport = P_CF->port;
585 rif->sock->rx_hook = rip_rx;
586 rif->sock->data = rif;
587 rif->sock->rbsize = 10240;
588 rif->sock->iface = new; /* Automagically works for dummy interface */
589 rif->sock->tbuf = mb_alloc( p->pool, sizeof( struct rip_packet ));
590 rif->sock->tx_hook = rip_tx;
591 rif->sock->err_hook = rip_tx_err;
592 rif->sock->daddr = IPA_NONE;
593 rif->sock->dport = P_CF->port;
594 if (new)
595 rif->sock->ttl = 1;
596 else
597 rif->sock->ttl = 30;
598 rif->sock->tos = IP_PREC_INTERNET_CONTROL;
599
600 if (new) {
601 if (new->addr->flags & IA_UNNUMBERED)
602 log( L_WARN "%s: rip is not defined over unnumbered links", P_NAME );
603 if (rif->multicast) {
604 #ifndef IPV6
605 rif->sock->daddr = ipa_from_u32(0xe0000009);
606 rif->sock->saddr = ipa_from_u32(0xe0000009);
607 #else
608 ip_pton("FF02::9", &rif->sock->daddr);
609 ip_pton("FF02::9", &rif->sock->saddr);
610 #endif
611 } else
612 rif->sock->daddr = new->addr->brd;
613 }
614
615 if (!ipa_nonzero(rif->sock->daddr)) {
616 log( L_WARN "%s: interface %s is too strange for me", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
617 } else
618 if (!(rif->patt->mode & IM_NOLISTEN))
619 if (sk_open(rif->sock)<0) {
620 log( L_ERR "%s: could not listen on %s", P_NAME, rif->iface ? rif->iface->name : "(dummy)" );
621 /* Don't try to transmit into this one? Well, why not? This should not happen, anyway :-) */
622 }
623
624 TRACE(D_EVENTS, "%s: listening on %s, port %d, mode %s (%I)", P_NAME, rif->iface ? rif->iface->name : "(dummy)", P_CF->port, rif->multicast ? "multicast" : "broadcast", rif->sock->daddr );
625
626 return rif;
627 }
628
629 static void
630 rip_real_if_add(struct object_lock *lock)
631 {
632 struct iface *iface = lock->iface;
633 struct proto *p = lock->data;
634 struct rip_interface *rif;
635 struct iface_patt *k = iface_patt_match(&P_CF->iface_list, iface);
636
637 if (!k)
638 bug("This can not happen! It existed few seconds ago!" );
639 DBG("adding interface %s\n", iface->name );
640 rif = new_iface(p, iface, iface->flags, k);
641 add_head( &P->interfaces, NODE rif );
642 rif->lock = lock;
643 }
644
645 static void
646 rip_if_notify(struct proto *p, unsigned c, struct iface *iface)
647 {
648 DBG( "RIP: if notify\n" );
649 if (iface->flags & IF_IGNORE)
650 return;
651 if (c & IF_CHANGE_DOWN) {
652 struct rip_interface *i;
653 i = find_interface(p, iface);
654 if (i) {
655 rem_node(NODE i);
656 kill_iface(p, i);
657 rfree(i->lock);
658 }
659 }
660 if (c & IF_CHANGE_UP) {
661 struct rip_interface *rif;
662 struct iface_patt *k = iface_patt_match(&P_CF->iface_list, iface);
663 struct object_lock *lock;
664
665 if (!k) return; /* We are not interested in this interface */
666
667 lock = olock_new( p->pool );
668 #ifndef IPV6
669 lock->addr = ipa_from_u32(0xe0000009); /* This is okay: we
670 may actually use
671 other address, but
672 we do not want two
673 rips at one time,
674 anyway. */
675 #else
676 ip_pton("FF02::9", &lock->addr);
677 #endif
678 lock->port = P_CF->port;
679 lock->iface = iface;
680 lock->hook = rip_real_if_add;
681 lock->data = p;
682 lock->type = OBJLOCK_UDP;
683 olock_acquire(lock);
684 }
685 }
686
687 static struct ea_list *
688 rip_gen_attrs(struct proto *p, struct linpool *pool, int metric, u16 tag)
689 {
690 struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr));
691
692 l->next = NULL;
693 l->flags = EALF_SORTED;
694 l->count = 2;
695 l->attrs[0].id = EA_RIP_TAG;
696 l->attrs[0].flags = 0;
697 l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
698 l->attrs[0].u.data = tag;
699 l->attrs[1].id = EA_RIP_METRIC;
700 l->attrs[1].flags = 0;
701 l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
702 l->attrs[1].u.data = metric;
703 return l;
704 }
705
706 static int
707 rip_import_control(struct proto *p, struct rte **rt, struct ea_list **attrs, struct linpool *pool)
708 {
709 if ((*rt)->attrs->proto == p) /* My own must not be touched */
710 return 1;
711
712 if ((*rt)->attrs->source != RTS_RIP) {
713 struct ea_list *new = rip_gen_attrs(p, pool, 1, 0);
714 new->next = *attrs;
715 *attrs = new;
716 }
717 return 0;
718 }
719
720 static struct ea_list *
721 rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
722 {
723 struct proto *p = rt->attrs->proto;
724 return rip_gen_attrs(p, pool, rt->u.rip.metric, rt->u.rip.tag);
725 }
726
727 static void
728 rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
729 {
730 struct proto *p = rt->attrs->proto;
731
732 rt->u.rip.tag = ea_find(attrs, EA_RIP_TAG)->u.data;
733 rt->u.rip.metric = ea_find(attrs, EA_RIP_METRIC)->u.data;
734 }
735
736 static void
737 rip_rt_notify(struct proto *p, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs)
738 {
739 CHK_MAGIC;
740
741 if (old) {
742 struct rip_entry *e = fib_find( &P->rtable, &net->n.prefix, net->n.pxlen );
743 if (!e)
744 log( L_BUG "Deleting nonexistent entry?!" );
745 fib_delete( &P->rtable, e );
746 }
747
748 if (new) {
749 struct rip_entry *e;
750 if (fib_find( &P->rtable, &net->n.prefix, net->n.pxlen ))
751 log( L_BUG "Inserting entry which is already there?" );
752 e = fib_get( &P->rtable, &net->n.prefix, net->n.pxlen );
753
754 e->nexthop = new->attrs->gw;
755 e->metric = 0;
756 e->whotoldme = IPA_NONE;
757
758 e->tag = ea_find(attrs, EA_RIP_TAG)->u.data;
759 e->metric = ea_find(attrs, EA_RIP_METRIC)->u.data;
760 if (e->metric > P_CF->infinity)
761 e->metric = P_CF->infinity;
762
763 if (new->attrs->proto == p)
764 e->whotoldme = new->attrs->from;
765
766 if (!e->metric) /* That's okay: this way user can set his own value for external
767 routes in rip. */
768 e->metric = 5;
769 e->updated = e->changed = now;
770 e->flags = 0;
771 }
772 }
773
774 static int
775 rip_rte_better(struct rte *new, struct rte *old)
776 {
777 struct proto *p = new->attrs->proto;
778
779 if (ipa_equal(old->attrs->from, new->attrs->from))
780 return 1;
781
782 if (old->u.rip.metric < new->u.rip.metric)
783 return 0;
784
785 if (old->u.rip.metric > new->u.rip.metric)
786 return 1;
787
788 if ((old->u.rip.metric < 16) && (new->u.rip.metric == P_CF->infinity)) {
789 new->u.rip.lastmodX = now - P_CF->timeout_time; /* Check this: if new metric is 16, act as it was timed out */
790 }
791
792 if (old->attrs->proto == new->attrs->proto) /* This does not make much sense for different protocols */
793 if ((old->u.rip.metric == new->u.rip.metric) &&
794 ((now - old->u.rip.lastmodX) > (P_CF->timeout_time / 2)))
795 return 1;
796
797 return 0;
798 }
799
800 static void
801 rip_rte_insert(net *net, rte *rte)
802 {
803 struct proto *p = rte->attrs->proto;
804 rte->u.rip.lastmodX = now;
805 add_head( &P->garbage, &rte->u.rip.garbage );
806 }
807
808 static void
809 rip_rte_remove(net *net, rte *rte)
810 {
811 struct proto *p = rte->attrs->proto;
812 rem_node( &rte->u.rip.garbage );
813 }
814
815 void
816 rip_init_instance(struct proto *p)
817 {
818 p->preference = DEF_PREF_RIP;
819 p->if_notify = rip_if_notify;
820 p->rt_notify = rip_rt_notify;
821 p->import_control = rip_import_control;
822 p->make_tmp_attrs = rip_make_tmp_attrs;
823 p->store_tmp_attrs = rip_store_tmp_attrs;
824 p->rte_better = rip_rte_better;
825 p->rte_insert = rip_rte_insert;
826 p->rte_remove = rip_rte_remove;
827 }
828
829 void
830 rip_init_config(struct rip_proto_config *c)
831 {
832 init_list(&c->iface_list);
833 c->infinity = 16;
834 c->port = 520;
835 c->period = 30;
836 c->garbage_time = 120+180;
837 c->timeout_time = 120;
838 c->passwords = NULL;
839 c->authtype = AT_NONE;
840 }
841
842 static void
843 rip_preconfig(struct protocol *x, struct config *c)
844 {
845 DBG( "RIP: preconfig\n" );
846 }
847
848 static int
849 rip_get_attr(eattr *a, byte *buf)
850 {
851 unsigned int i = EA_ID(a->id);
852 struct attr_desc *d;
853
854 switch (a->id) {
855 case EA_RIP_METRIC: buf += bsprintf( buf, "metric: %d", a->u.data ); return GA_FULL;
856 case EA_RIP_TAG: buf += bsprintf( buf, "tag: %d", a->u.data ); return GA_FULL;
857 default: return GA_UNKNOWN;
858 }
859 }
860
861 struct protocol proto_rip = {
862 name: "RIP",
863 template: "rip%d",
864 attr_class: EAP_RIP,
865 preconfig: rip_preconfig,
866 get_route_info: rip_get_route_info,
867 get_attr: rip_get_attr,
868
869 init: rip_init,
870 dump: rip_dump,
871 start: rip_start,
872 };