]>
git.ipfire.org Git - thirdparty/bird.git/blob - proto/rip/rip.c
2 * Rest in pieces - RIP protocol
4 * Copyright (c) 1998, 1999 Pavel Machek <pavel@ucw.cz>
6 * Can be freely distributed and used under the terms of the GNU GPL.
8 FIXME: IpV6 support: packet size
9 FIXME: IpV6 support: use right address for broadcasts
10 FIXME: IpV6 support: receive "route using" blocks
12 FIXME (nonurgent): fold rip_connection into rip_interface?
14 We are not going to honour requests for sending part of
15 routing table. That would need to turn split horizon off,
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.
23 FIXME: (nonurgent) allow bigger frequencies than 1 regular update in 6 seconds (?)
24 FIXME: propagation of metric=infinity into main routing table may or may not be good idea.
33 #include "nest/bird.h"
34 #include "nest/iface.h"
35 #include "nest/protocol.h"
36 #include "nest/route.h"
37 #include "lib/socket.h"
38 #include "lib/resource.h"
39 #include "lib/lists.h"
40 #include "lib/timer.h"
41 #include "lib/string.h"
45 #define P ((struct rip_proto *) p)
46 #define P_CF ((struct rip_proto_config *)p->cf)
47 #define E ((struct rip_entry *) e)
49 #define TRACE(level, msg, args...) do { if (p->debug & level) { log(L_TRACE "%s: " msg, p->name , ## args); } } while(0)
51 static struct rip_interface
*new_iface(struct proto
*p
, struct iface
*new, unsigned long flags
, struct iface_patt
*patt
);
53 #define P_NAME p->name
60 rip_tx_err( sock
*s
, int err
)
62 struct rip_connection
*c
= s
->data
;
63 struct proto
*p
= c
->proto
;
64 log( L_ERR
"Unexpected error at rip transmit: %m" );
68 rip_tx_prepare(struct proto
*p
, ip_addr daddr
, struct rip_block
*b
, struct rip_entry
*e
, struct rip_interface
*rif
)
71 b
->family
= htons( 2 ); /* AF_INET */
72 b
->tag
= htons( e
->tag
);
73 b
->network
= e
->n
.prefix
;
75 b
->netmask
= ipa_mkmask( e
->n
.pxlen
);
76 ipa_hton( b
->netmask
);
78 if (neigh_connected_to(p
, &e
->nexthop
, rif
->iface
))
79 b
->nexthop
= e
->nexthop
;
81 b
->nexthop
= IPA_NONE
;
82 ipa_hton( b
->nexthop
);
84 b
->pxlen
= e
->n
.pxlen
;
86 b
->metric
= htonl( e
->metric
);
87 if (neigh_connected_to(p
, &e
->whotoldme
, rif
->iface
)) {
88 DBG( "(split horizon)" );
89 b
->metric
= htonl( P_CF
->infinity
);
91 ipa_hton( b
->network
);
97 struct rip_interface
*rif
= s
->data
;
98 struct rip_connection
*c
= rif
->busy
;
99 struct proto
*p
= c
->proto
;
100 struct rip_packet
*packet
= (void *) s
->tbuf
;
103 DBG( "Sending to %I\n", s
->daddr
);
109 DBG( "Preparing packet to send: " );
111 packet
->heading
.command
= RIPCMD_RESPONSE
;
112 packet
->heading
.version
= RIP_V2
;
113 packet
->heading
.unused
= 0;
115 i
= !!P_CF
->authtype
;
116 FIB_ITERATE_START(&P
->rtable
, &c
->iter
, z
) {
117 struct rip_entry
*e
= (struct rip_entry
*) z
;
119 if (!rif
->triggered
|| (!(e
->updated
< now
-5))) {
121 rip_tx_prepare( p
, s
->daddr
, packet
->block
+ i
, e
, rif
);
122 if (i
++ == ((P_CF
->authtype
== AT_MD5
) ? PACKET_MD5_MAX
: PACKET_MAX
)) {
123 FIB_ITERATE_PUT(&c
->iter
, z
);
127 } FIB_ITERATE_END(z
);
132 packetlen
= rip_outgoing_authentication(p
, (void *) &packet
->block
[0], packet
, i
);
134 DBG( ", sending %d blocks, ", i
);
135 if (i
== !!P_CF
->authtype
) {
136 DBG( "not sending NULL update\n" );
140 if (ipa_nonzero(c
->daddr
))
141 i
= sk_send_to( s
, packetlen
, c
->daddr
, c
->dport
);
143 i
= sk_send( s
, packetlen
);
145 DBG( "it wants more\n" );
149 if (i
<0) rip_tx_err( s
, i
);
154 DBG( "Looks like I'm" );
163 rip_sendto( struct proto
*p
, ip_addr daddr
, int dport
, struct rip_interface
*rif
)
165 struct iface
*iface
= rif
->iface
;
166 struct rip_connection
*c
;
170 log (L_WARN
"Interface %s is much too slow, dropping request", iface
->name
);
173 c
= mb_alloc( p
->pool
, sizeof( struct rip_connection
));
183 if (c
->rif
->sock
->data
!= rif
)
184 bug("not enough send magic");
186 if (sk_open(c
->send
)<0) {
187 log( L_ERR
"Could not open socket for data send to %I:%d on %s", daddr
, dport
, rif
->iface
->name
);
193 fit_init( &c
->iter
, &P
->rtable
);
194 add_head( &P
->connections
, NODE c
);
195 TRACE(D_PACKETS
, "Sending my routing table to %I:%d on %s\n", daddr
, dport
, rif
->iface
->name
);
197 rip_tx(c
->rif
->sock
);
200 static struct rip_interface
*
201 find_interface(struct proto
*p
, struct iface
*what
)
203 struct rip_interface
*i
;
205 WALK_LIST (i
, P
->interfaces
)
206 if (i
->iface
== what
)
215 /* Let main routing table know about our new entry */
217 advertise_entry( struct proto
*p
, struct rip_block
*b
, ip_addr whotoldme
)
223 struct rip_interface
*rif
;
226 bzero(&A
, sizeof(A
));
229 A
.scope
= SCOPE_UNIVERSE
;
230 A
.cast
= RTC_UNICAST
;
234 A
.gw
= ipa_nonzero(b
->nexthop
) ? b
->nexthop
: whotoldme
;
235 pxlen
= ipa_mklen(b
->netmask
);
237 A
.gw
= whotoldme
; /* FIXME: next hop is in other packet for v6 */
242 /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */
244 neighbor
= neigh_find( p
, &A
.gw
, 0 );
246 log( L_REMOTE
"%I asked me to route %I/%d using not-neighbor %I.", A
.from
, b
->network
, pxlen
, A
.gw
);
250 A
.iface
= neighbor
->iface
;
251 if (!(rif
= neighbor
->data
)) {
252 rif
= neighbor
->data
= find_interface(p
, A
.iface
);
255 bug("Route packet using unknown interface? No.");
259 /* set to: interface of nexthop */
262 log( L_REMOTE
"%I gave me invalid pxlen/netmask for %I.", A
.from
, b
->network
);
265 n
= net_get( p
->table
, b
->network
, pxlen
);
267 r
->u
.rip
.metric
= ntohl(b
->metric
) + rif
->patt
->metric
;
268 if (r
->u
.rip
.metric
> P_CF
->infinity
) r
->u
.rip
.metric
= P_CF
->infinity
;
269 r
->u
.rip
.tag
= ntohl(b
->tag
);
271 r
->pflags
= 0; /* Here go my flags */
272 rte_update( p
->table
, n
, p
, r
);
277 process_block( struct proto
*p
, struct rip_block
*block
, ip_addr whotoldme
)
279 int metric
= ntohl( block
->metric
);
280 ip_addr network
= block
->network
;
283 TRACE(D_ROUTES
, "block: %I tells me: %I/??? available, metric %d... ", whotoldme
, network
, metric
);
284 if ((!metric
) || (metric
> P_CF
->infinity
)) {
285 log( L_WARN
"Got metric %d from %I", metric
, whotoldme
);
289 advertise_entry( p
, block
, whotoldme
);
292 #define BAD( x ) { log( L_REMOTE "%s: " x, P_NAME ); return 1; }
295 rip_process_packet( struct proto
*p
, struct rip_packet
*packet
, int num
, ip_addr whotoldme
, int port
)
298 int native_class
= 0, authenticated
= 0;
300 switch( packet
->heading
.version
) {
301 case RIP_V1
: DBG( "Rip1: " ); break;
302 case RIP_V2
: DBG( "Rip2: " ); break;
303 default: BAD( "Unknown version" );
306 switch( packet
->heading
.command
) {
307 case RIPCMD_REQUEST
: DBG( "Asked to send my routing table\n" );
308 if (P_CF
->honour
== HO_NEVER
) {
309 log( L_REMOTE
"They asked me to send routing table, but I was told not to do it" );
312 if ((P_CF
->honour
== HO_NEIGHBOUR
) && (!neigh_find( p
, &whotoldme
, 0 ))) {
313 log( L_REMOTE
"They asked me to send routing table, but he is not my neighbour" );
316 rip_sendto( p
, whotoldme
, port
, HEAD(P
->interfaces
) ); /* no broadcast */
318 case RIPCMD_RESPONSE
: DBG( "*** Rtable from %I\n", whotoldme
);
319 if (port
!= P_CF
->port
) {
320 log( L_REMOTE
"%I send me routing info from port %d", whotoldme
, port
);
324 log( L_REMOTE
"...ignoring" );
328 if (!neigh_find( p
, &whotoldme
, 0 )) {
329 log( L_REMOTE
"%I send me routing info but he is not my neighbour", whotoldme
);
333 log( L_REMOTE
"...ignoring" );
337 for (i
=0; i
<num
; i
++) {
338 struct rip_block
*block
= &packet
->block
[i
];
339 if (block
->family
== 0xffff) {
341 continue; /* md5 tail has this family */
342 if (rip_incoming_authentication(p
, (void *) block
, packet
, num
, whotoldme
))
343 BAD( "Authentication failed" );
347 if ((!authenticated
) && (P_CF
->authtype
!= AT_NONE
))
348 BAD( "Packet is not authenticated and it should be" );
349 ipa_ntoh( block
->network
);
351 ipa_ntoh( block
->netmask
);
352 ipa_ntoh( block
->nexthop
);
353 if (packet
->heading
.version
== RIP_V1
) /* FIXME (nonurgent): switch to disable this? */
354 block
->netmask
= ipa_class_mask(block
->network
);
356 process_block( p
, block
, whotoldme
);
360 case RIPCMD_TRACEOFF
: BAD( "I was asked for traceon/traceoff" );
361 case 5: BAD( "Some Sun extension around here" );
362 default: BAD( "Unknown command" );
369 rip_rx(sock
*s
, int size
)
371 struct rip_interface
*i
= s
->data
;
372 struct proto
*p
= i
->proto
;
376 DBG( "RIP: message came: %d bytes\n", size
);
377 size
-= sizeof( struct rip_packet_heading
);
378 if (size
< 0) BAD( "Too small packet" );
379 if (size
% sizeof( struct rip_block
)) BAD( "Odd sized packet" );
380 num
= size
/ sizeof( struct rip_block
);
381 if (num
>25) BAD( "Too many blocks" );
383 rip_process_packet( p
, (struct rip_packet
*) s
->rbuf
, num
, s
->faddr
, s
->fport
);
388 * Interface to rest of bird
392 rip_dump_entry( struct rip_entry
*e
)
394 debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ",
395 e
->whotoldme
, e
->updated
-now
, e
->changed
-now
, e
->n
.prefix
, e
->n
.pxlen
, e
->nexthop
, e
->metric
);
396 if (e
->flags
& RIP_F_EXTERNAL
) debug( "[external]" );
403 struct proto
*p
= t
->data
;
404 struct rip_entry
*e
, *et
;
407 DBG( "RIP: tick tock\n" );
409 WALK_LIST_DELSAFE( e
, et
, P
->garbage
) {
411 rte
= SKIP_BACK( struct rte
, u
.rip
.garbage
, e
);
412 DBG( "Garbage: " ); rte_dump( rte
);
414 if (now
- rte
->u
.rip
.lastmodX
> P_CF
->timeout_time
) {
415 TRACE(D_EVENTS
, "RIP: entry is too old: %I", rte
->net
->n
.prefix
);
416 e
->metric
= P_CF
->infinity
;
419 if (now
- rte
->u
.rip
.lastmodX
> P_CF
->garbage_time
) {
420 TRACE(D_EVENTS
, "RIP: entry is much too old: %I", rte
->net
->n
.prefix
);
421 rte_discard(p
->table
, rte
);
425 DBG( "RIP: Broadcasting routing tables\n" );
427 struct rip_interface
*rif
;
430 WALK_LIST( rif
, P
->interfaces
) {
431 struct iface
*iface
= rif
->iface
;
433 if (!iface
) continue;
434 if (rif
->patt
->mode
& IM_QUIET
) continue;
435 if (!(iface
->flags
& IF_UP
)) continue;
437 rif
->triggered
= (P
->tx_count
% 6);
438 rip_sendto( p
, IPA_NONE
, 0, rif
);
442 DBG( "RIP: tick tock done\n" );
446 rip_start(struct proto
*p
)
448 struct rip_interface
*rif
;
449 DBG( "RIP: starting instance...\n" );
451 P
->magic
= RIP_MAGIC
;
452 fib_init( &P
->rtable
, p
->pool
, sizeof( struct rip_entry
), 0, NULL
);
453 init_list( &P
->connections
);
454 init_list( &P
->garbage
);
455 init_list( &P
->interfaces
);
456 P
->timer
= tm_new( p
->pool
);
458 P
->timer
->randomize
= 5;
459 P
->timer
->recurrent
= (P_CF
->period
/ 6)+1;
460 P
->timer
->hook
= rip_timer
;
461 tm_start( P
->timer
, 5 );
462 rif
= new_iface(p
, NULL
, 0, NULL
); /* Initialize dummy interface */
463 add_head( &P
->interfaces
, NODE rif
);
466 rip_init_instance(p
);
468 DBG( "RIP: ...done\n");
472 static struct proto
*
473 rip_init(struct proto_config
*cfg
)
475 struct proto
*p
= proto_new(cfg
, sizeof(struct rip_proto
));
481 rip_dump(struct proto
*p
)
485 struct rip_interface
*rif
;
489 WALK_LIST( w
, P
->connections
) {
490 struct rip_connection
*n
= (void *) w
;
491 debug( "RIP: connection #%d: %I\n", n
->num
, n
->addr
);
494 FIB_WALK( &P
->rtable
, e
) {
495 debug( "RIP: entry #%d: ", i
++ );
499 WALK_LIST( rif
, P
->interfaces
) {
500 debug( "RIP: interface #%d: %s, %I, busy = %x\n", i
++, rif
->iface
?rif
->iface
->name
:"(dummy)", rif
->sock
->daddr
, rif
->busy
);
505 rip_get_route_info(rte
*rte
, byte
*buf
)
507 buf
+= sprintf(buf
, " (%d/%d)", rte
->pref
, rte
->u
.rip
.metric
);
508 sprintf(buf
, " t%04x", rte
->u
.rip
.tag
);
512 rip_want_this_if(struct rip_interface
*iface
)
518 kill_iface(struct proto
*p
, struct rip_interface
*i
)
520 DBG( "RIP: Interface %s disappeared\n", i
->iface
->name
);
526 * new maybe null if we are creating initial send socket
528 static struct rip_interface
*
529 new_iface(struct proto
*p
, struct iface
*new, unsigned long flags
, struct iface_patt
*patt
)
531 struct rip_interface
*rif
;
533 rif
= mb_allocz(p
->pool
, sizeof( struct rip_interface
));
537 rif
->patt
= (struct rip_patt
*) patt
;
540 rif
->multicast
= (!(rif
->patt
->mode
& IM_BROADCAST
)) && (flags
& IF_MULTICAST
);
541 /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */
544 DBG( "Doing multicasts!\n" );
546 rif
->sock
= sk_new( p
->pool
);
547 rif
->sock
->type
= rif
->multicast
?SK_UDP_MC
:SK_UDP
;
548 rif
->sock
->sport
= P_CF
->port
;
549 rif
->sock
->rx_hook
= rip_rx
;
550 rif
->sock
->data
= rif
;
551 rif
->sock
->rbsize
= 10240;
552 rif
->sock
->iface
= new; /* Automagically works for dummy interface */
553 rif
->sock
->tbuf
= mb_alloc( p
->pool
, sizeof( struct rip_packet
));
554 rif
->sock
->tx_hook
= rip_tx
;
555 rif
->sock
->err_hook
= rip_tx_err
;
556 rif
->sock
->daddr
= IPA_NONE
;
557 rif
->sock
->dport
= P_CF
->port
;
562 rif
->sock
->tos
= IP_PREC_INTERNET_CONTROL
;
565 if (new->addr
->flags
& IA_UNNUMBERED
)
566 log( L_WARN
"%s: rip is not defined over unnumbered links", P_NAME
);
567 if (rif
->multicast
) {
568 rif
->sock
->daddr
= ipa_from_u32(0xe0000009);
569 rif
->sock
->saddr
= ipa_from_u32(0xe0000009);
571 rif
->sock
->daddr
= new->addr
->brd
;
574 if (!ipa_nonzero(rif
->sock
->daddr
)) {
575 log( L_WARN
"%s: interface %s is too strange for me", P_NAME
, rif
->iface
? rif
->iface
->name
: "(dummy)" );
577 if (!(rif
->patt
->mode
& IM_NOLISTEN
))
578 if (sk_open(rif
->sock
)<0) {
579 log( L_ERR
"%s: could not listen on %s", P_NAME
, rif
->iface
? rif
->iface
->name
: "(dummy)" );
580 /* Don't try to transmit into this one? Well, why not? This should not happen, anyway :-) */
583 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
);
589 rip_real_if_add(struct object_lock
*lock
)
591 struct iface
*iface
= lock
->iface
;
592 struct proto
*p
= lock
->data
;
593 struct rip_interface
*rif
;
594 struct iface_patt
*k
= iface_patt_match(&P_CF
->iface_list
, iface
);
597 bug("This can not happen! It existed few seconds ago!" );
598 DBG("adding interface %s\n", iface
->name
);
599 rif
= new_iface(p
, iface
, iface
->flags
, k
);
600 add_head( &P
->interfaces
, NODE rif
);
605 rip_if_notify(struct proto
*p
, unsigned c
, struct iface
*iface
)
607 DBG( "RIP: if notify\n" );
608 if (iface
->flags
& IF_IGNORE
)
610 if (c
& IF_CHANGE_DOWN
) {
611 struct rip_interface
*i
;
612 i
= find_interface(p
, iface
);
619 if (c
& IF_CHANGE_UP
) {
620 struct rip_interface
*rif
;
621 struct iface_patt
*k
= iface_patt_match(&P_CF
->iface_list
, iface
);
622 struct object_lock
*lock
;
624 if (!k
) return; /* We are not interested in this interface */
626 lock
= olock_new( p
->pool
);
627 lock
->addr
= ipa_from_u32(0xe0000009);
628 lock
->port
= P_CF
->port
;
630 lock
->hook
= rip_real_if_add
;
632 lock
->type
= OBJLOCK_UDP
;
637 static struct ea_list
*
638 rip_gen_attrs(struct proto
*p
, struct linpool
*pool
, int metric
, u16 tag
)
640 struct ea_list
*l
= lp_alloc(pool
, sizeof(struct ea_list
) + 2*sizeof(eattr
));
643 l
->flags
= EALF_SORTED
;
645 l
->attrs
[0].id
= EA_RIP_TAG
;
646 l
->attrs
[0].flags
= 0;
647 l
->attrs
[0].type
= EAF_TYPE_INT
| EAF_TEMP
;
648 l
->attrs
[0].u
.data
= tag
;
649 l
->attrs
[1].id
= EA_RIP_METRIC
;
650 l
->attrs
[1].flags
= 0;
651 l
->attrs
[1].type
= EAF_TYPE_INT
| EAF_TEMP
;
652 l
->attrs
[1].u
.data
= metric
;
657 rip_import_control(struct proto
*p
, struct rte
**rt
, struct ea_list
**attrs
, struct linpool
*pool
)
659 if ((*rt
)->attrs
->proto
== p
) /* My own must not be touched */
662 if ((*rt
)->attrs
->source
!= RTS_RIP
) {
663 struct ea_list
*new = rip_gen_attrs(p
, pool
, 1, 0);
670 static struct ea_list
*
671 rip_make_tmp_attrs(struct rte
*rt
, struct linpool
*pool
)
673 struct proto
*p
= rt
->attrs
->proto
;
674 return rip_gen_attrs(p
, pool
, rt
->u
.rip
.metric
, rt
->u
.rip
.tag
);
678 rip_store_tmp_attrs(struct rte
*rt
, struct ea_list
*attrs
)
680 struct proto
*p
= rt
->attrs
->proto
;
682 rt
->u
.rip
.tag
= ea_find(attrs
, EA_RIP_TAG
)->u
.data
;
683 rt
->u
.rip
.metric
= ea_find(attrs
, EA_RIP_METRIC
)->u
.data
;
687 rip_rt_notify(struct proto
*p
, struct network
*net
, struct rte
*new, struct rte
*old
, struct ea_list
*attrs
)
692 struct rip_entry
*e
= fib_find( &P
->rtable
, &net
->n
.prefix
, net
->n
.pxlen
);
694 log( L_BUG
"Deleting nonexistent entry?!" );
695 fib_delete( &P
->rtable
, e
);
700 if (fib_find( &P
->rtable
, &net
->n
.prefix
, net
->n
.pxlen
))
701 log( L_BUG
"Inserting entry which is already there?" );
702 e
= fib_get( &P
->rtable
, &net
->n
.prefix
, net
->n
.pxlen
);
704 e
->nexthop
= new->attrs
->gw
;
706 e
->whotoldme
= IPA_NONE
;
708 e
->tag
= ea_find(attrs
, EA_RIP_TAG
)->u
.data
;
709 e
->metric
= ea_find(attrs
, EA_RIP_METRIC
)->u
.data
;
710 if (e
->metric
> P_CF
->infinity
)
711 e
->metric
= P_CF
->infinity
;
713 if (new->attrs
->proto
== p
)
714 e
->whotoldme
= new->attrs
->from
;
716 if (!e
->metric
) /* This is metric for external routes. Notice
717 that I do honour it even if it comes from other protocol than this
718 rip. That's okay: this way user can set his own value for external
721 e
->updated
= e
->changed
= now
;
727 rip_rte_better(struct rte
*new, struct rte
*old
)
729 struct proto
*p
= new->attrs
->proto
;
731 if (ipa_equal(old
->attrs
->from
, new->attrs
->from
))
734 if (old
->u
.rip
.metric
< new->u
.rip
.metric
)
737 if (old
->u
.rip
.metric
> new->u
.rip
.metric
)
740 if ((old
->u
.rip
.metric
< 16) && (new->u
.rip
.metric
== P_CF
->infinity
)) {
741 new->u
.rip
.lastmodX
= now
- P_CF
->timeout_time
; /* Check this: if new metric is 16, act as it was timed out */
744 if (old
->attrs
->proto
== new->attrs
->proto
) /* This does not make much sense for different protocols */
745 if ((old
->u
.rip
.metric
== new->u
.rip
.metric
) &&
746 ((now
- old
->u
.rip
.lastmodX
) > (P_CF
->timeout_time
/ 2)))
753 rip_rte_insert(net
*net
, rte
*rte
)
755 struct proto
*p
= rte
->attrs
->proto
;
756 rte
->u
.rip
.lastmodX
= now
;
757 add_head( &P
->garbage
, &rte
->u
.rip
.garbage
);
761 rip_rte_remove(net
*net
, rte
*rte
)
763 struct proto
*p
= rte
->attrs
->proto
;
764 rem_node( &rte
->u
.rip
.garbage
);
768 rip_init_instance(struct proto
*p
)
770 p
->preference
= DEF_PREF_RIP
;
771 p
->if_notify
= rip_if_notify
;
772 p
->rt_notify
= rip_rt_notify
;
773 p
->import_control
= rip_import_control
;
774 p
->make_tmp_attrs
= rip_make_tmp_attrs
;
775 p
->store_tmp_attrs
= rip_store_tmp_attrs
;
776 p
->rte_better
= rip_rte_better
;
777 p
->rte_insert
= rip_rte_insert
;
778 p
->rte_remove
= rip_rte_remove
;
782 rip_init_config(struct rip_proto_config
*c
)
784 init_list(&c
->iface_list
);
788 c
->garbage_time
= 120+180;
789 c
->timeout_time
= 120;
791 c
->authtype
= AT_NONE
;
795 rip_preconfig(struct protocol
*x
, struct config
*c
)
797 DBG( "RIP: preconfig\n" );
800 struct protocol proto_rip
= {
803 preconfig
: rip_preconfig
,
804 get_route_info
: rip_get_route_info
,