1998-07-10 03:39:04 +08:00
/*
* Rest in pieces - RIP protocol
*
1999-05-11 17:53:45 +08:00
* Copyright ( c ) 1998 , 1999 Pavel Machek < pavel @ ucw . cz >
1998-07-10 03:39:04 +08:00
*
* Can be freely distributed and used under the terms of the GNU GPL .
1999-10-11 22:19:29 +08:00
*
1999-10-12 21:04:50 +08:00
FIXME : IpV6 support : packet size
2000-04-26 19:33:03 +08:00
FIXME : ( nonurgent ) IpV6 support : receive " route using " blocks
FIXME : ( nonurgent ) IpV6 support : generate " nexthop " blocks
next hops are only advisory , and they are pretty ugly in IpV6 .
I suggest just forgetting about them .
1999-11-25 21:38:25 +08:00
1999-12-01 20:52:57 +08:00
FIXME ( nonurgent ) : fold rip_connection into rip_interface ?
1999-11-10 19:52:36 +08:00
1999-12-08 20:51:45 +08:00
FIXME : ( nonurgent ) allow bigger frequencies than 1 regular update in 6 seconds ( ? )
FIXME : propagation of metric = infinity into main routing table may or may not be good idea .
1998-07-10 03:39:04 +08:00
*/
# define LOCAL_DEBUG
# include "nest/bird.h"
# include "nest/iface.h"
# include "nest/protocol.h"
# include "nest/route.h"
# include "lib/socket.h"
# include "lib/resource.h"
# include "lib/lists.h"
# include "lib/timer.h"
2000-04-01 07:30:21 +08:00
# include "lib/string.h"
1998-07-10 03:39:04 +08:00
# include "rip.h"
1999-02-15 21:34:43 +08:00
# define P ((struct rip_proto *) p)
# define P_CF ((struct rip_proto_config *)p->cf)
# define E ((struct rip_entry *) e)
2000-03-10 00:38:51 +08:00
# define TRACE(level, msg, args...) do { if (p->debug & level) { log(L_TRACE "%s: " msg, p->name , ## args); } } while(0)
2000-03-09 23:12:41 +08:00
1999-06-01 04:30:16 +08:00
static struct rip_interface * new_iface ( struct proto * p , struct iface * new , unsigned long flags , struct iface_patt * patt ) ;
1999-03-17 18:20:23 +08:00
# define P_NAME p->name
1999-02-09 06:50:32 +08:00
1998-07-29 05:44:11 +08:00
/*
* Output processing
*/
static void
rip_tx_err ( sock * s , int err )
{
struct rip_connection * c = s - > data ;
struct proto * p = c - > proto ;
1998-12-20 22:29:06 +08:00
log ( L_ERR " Unexpected error at rip transmit: %m " ) ;
1998-07-29 05:44:11 +08:00
}
2000-04-26 19:07:57 +08:00
static int
rip_tx_prepare ( struct proto * p , ip_addr daddr , struct rip_block * b , struct rip_entry * e , struct rip_interface * rif , int pos )
1999-11-25 21:38:25 +08:00
{
DBG ( " . " ) ;
b - > tag = htons ( e - > tag ) ;
b - > network = e - > n . prefix ;
# ifndef IPV6
2000-04-29 23:45:30 +08:00
b - > family = htons ( 2 ) ; /* AF_INET */
1999-11-25 21:38:25 +08:00
b - > netmask = ipa_mkmask ( e - > n . pxlen ) ;
ipa_hton ( b - > netmask ) ;
2000-03-27 20:21:11 +08:00
if ( neigh_connected_to ( p , & e - > nexthop , rif - > iface ) )
b - > nexthop = e - > nexthop ;
else
b - > nexthop = IPA_NONE ;
1999-11-25 21:38:25 +08:00
ipa_hton ( b - > nexthop ) ;
# else
b - > pxlen = e - > n . pxlen ;
# endif
b - > metric = htonl ( e - > metric ) ;
2000-03-27 20:21:11 +08:00
if ( neigh_connected_to ( p , & e - > whotoldme , rif - > iface ) ) {
2000-03-13 21:31:00 +08:00
DBG ( " (split horizon) " ) ;
1999-12-08 20:51:45 +08:00
b - > metric = htonl ( P_CF - > infinity ) ;
1999-11-25 21:38:25 +08:00
}
ipa_hton ( b - > network ) ;
2000-04-26 19:07:57 +08:00
return pos + 1 ;
1999-11-25 21:38:25 +08:00
}
1998-07-29 05:44:11 +08:00
static void
rip_tx ( sock * s )
{
1998-10-26 23:35:19 +08:00
struct rip_interface * rif = s - > data ;
struct rip_connection * c = rif - > busy ;
1998-07-29 05:44:11 +08:00
struct proto * p = c - > proto ;
struct rip_packet * packet = ( void * ) s - > tbuf ;
1999-11-25 22:54:08 +08:00
int i , packetlen ;
2000-04-26 19:07:57 +08:00
int maxi , nullupdate = 1 ;
1998-07-29 05:44:11 +08:00
1999-10-02 18:44:48 +08:00
DBG ( " Sending to %I \n " , s - > daddr ) ;
1998-12-23 03:41:04 +08:00
do {
1998-07-29 05:44:11 +08:00
2000-03-23 20:08:40 +08:00
if ( c - > done )
goto done ;
1998-07-29 05:44:11 +08:00
1998-12-23 03:41:04 +08:00
DBG ( " Preparing packet to send: " ) ;
packet - > heading . command = RIPCMD_RESPONSE ;
packet - > heading . version = RIP_V2 ;
packet - > heading . unused = 0 ;
1999-05-11 17:53:45 +08:00
i = ! ! P_CF - > authtype ;
2000-04-26 19:33:03 +08:00
# ifndef IPV6
2000-04-26 19:07:57 +08:00
maxi = ( ( P_CF - > authtype = = AT_MD5 ) ? PACKET_MD5_MAX : PACKET_MAX ) ;
2000-04-26 19:33:03 +08:00
# else
maxi = 5 ; /* We need to have at least reserve of one at end of packet */
# endif
2000-04-26 19:07:57 +08:00
1998-12-23 03:41:04 +08:00
FIB_ITERATE_START ( & P - > rtable , & c - > iter , z ) {
struct rip_entry * e = ( struct rip_entry * ) z ;
1999-12-01 21:44:42 +08:00
if ( ! rif - > triggered | | ( ! ( e - > updated < now - 5 ) ) ) {
2000-04-26 19:07:57 +08:00
nullupdate = 0 ;
i = rip_tx_prepare ( p , s - > daddr , packet - > block + i , e , rif , i ) ;
if ( i > = maxi ) {
1999-12-01 21:44:42 +08:00
FIB_ITERATE_PUT ( & c - > iter , z ) ;
goto break_loop ;
}
1998-12-23 03:41:04 +08:00
}
1999-02-14 03:14:16 +08:00
} FIB_ITERATE_END ( z ) ;
1998-12-23 03:41:04 +08:00
c - > done = 1 ;
break_loop :
1999-11-25 22:54:08 +08:00
packetlen = rip_outgoing_authentication ( p , ( void * ) & packet - > block [ 0 ] , packet , i ) ;
1999-05-11 17:53:45 +08:00
1998-12-23 03:41:04 +08:00
DBG ( " , sending %d blocks, " , i ) ;
2000-04-26 19:07:57 +08:00
if ( nullupdate ) {
2000-01-26 22:12:18 +08:00
DBG ( " not sending NULL update \n " ) ;
2000-03-22 22:26:03 +08:00
c - > done = 1 ;
2000-03-23 20:08:40 +08:00
goto done ;
2000-01-26 22:12:18 +08:00
}
2000-03-23 20:08:40 +08:00
if ( ipa_nonzero ( c - > daddr ) )
i = sk_send_to ( s , packetlen , c - > daddr , c - > dport ) ;
else
i = sk_send ( s , packetlen ) ;
1998-07-29 05:44:11 +08:00
1998-12-10 04:08:57 +08:00
DBG ( " it wants more \n " ) ;
1998-07-29 05:44:11 +08:00
1998-12-23 03:41:04 +08:00
} while ( i > 0 ) ;
if ( i < 0 ) rip_tx_err ( s , i ) ;
1998-12-10 04:08:57 +08:00
DBG ( " blocked \n " ) ;
1999-11-25 21:38:25 +08:00
return ;
2000-03-23 20:08:40 +08:00
done :
DBG ( " Looks like I'm " ) ;
c - > rif - > busy = NULL ;
rem_node ( NODE c ) ;
mb_free ( c ) ;
DBG ( " done \n " ) ;
return ;
1998-07-29 05:44:11 +08:00
}
static void
1998-10-26 23:35:19 +08:00
rip_sendto ( struct proto * p , ip_addr daddr , int dport , struct rip_interface * rif )
1998-07-29 05:44:11 +08:00
{
1998-10-26 23:35:19 +08:00
struct iface * iface = rif - > iface ;
2000-03-27 02:01:27 +08:00
struct rip_connection * c ;
1998-07-29 05:44:11 +08:00
static int num = 0 ;
1998-10-26 23:35:19 +08:00
if ( rif - > busy ) {
1998-12-20 22:29:06 +08:00
log ( L_WARN " Interface %s is much too slow, dropping request " , iface - > name ) ;
1998-10-26 23:35:19 +08:00
return ;
}
2000-03-27 02:01:27 +08:00
c = mb_alloc ( p - > pool , sizeof ( struct rip_connection ) ) ;
1998-10-26 23:35:19 +08:00
rif - > busy = c ;
1998-10-21 00:45:53 +08:00
1998-07-29 05:44:11 +08:00
c - > addr = daddr ;
c - > proto = p ;
c - > num = num + + ;
1998-10-26 23:35:19 +08:00
c - > rif = rif ;
1998-07-29 05:44:11 +08:00
1998-10-26 23:35:19 +08:00
c - > dport = dport ;
c - > daddr = daddr ;
if ( c - > rif - > sock - > data ! = rif )
1998-12-20 22:29:06 +08:00
bug ( " not enough send magic " ) ;
1998-10-26 23:35:19 +08:00
#if 0
1998-07-29 05:44:11 +08:00
if ( sk_open ( c - > send ) < 0 ) {
1998-12-20 22:29:06 +08:00
log ( L_ERR " Could not open socket for data send to %I:%d on %s " , daddr , dport , rif - > iface - > name ) ;
1998-07-29 05:44:11 +08:00
return ;
}
1998-10-26 23:35:19 +08:00
# endif
1998-07-29 05:44:11 +08:00
1998-12-23 03:41:04 +08:00
c - > done = 0 ;
fit_init ( & c - > iter , & P - > rtable ) ;
1998-07-29 05:44:11 +08:00
add_head ( & P - > connections , NODE c ) ;
2000-03-10 00:38:51 +08:00
TRACE ( D_PACKETS , " Sending my routing table to %I:%d on %s \n " , daddr , dport , rif - > iface - > name ) ;
1998-07-29 05:44:11 +08:00
1998-10-26 23:35:19 +08:00
rip_tx ( c - > rif - > sock ) ;
1998-07-29 05:44:11 +08:00
}
1999-03-17 18:20:23 +08:00
static struct rip_interface *
1998-12-04 19:45:51 +08:00
find_interface ( struct proto * p , struct iface * what )
{
struct rip_interface * i ;
1998-12-10 04:08:57 +08:00
1998-12-04 19:45:51 +08:00
WALK_LIST ( i , P - > interfaces )
if ( i - > iface = = what )
return i ;
return NULL ;
}
1998-07-29 05:44:11 +08:00
/*
* Input processing
*/
1998-10-13 22:32:18 +08:00
/* Let main routing table know about our new entry */
static void
advertise_entry ( struct proto * p , struct rip_block * b , ip_addr whotoldme )
{
rta * a , A ;
rte * r ;
net * n ;
1998-10-21 00:12:43 +08:00
neighbor * neighbor ;
1999-03-17 18:20:23 +08:00
struct rip_interface * rif ;
1999-10-11 22:19:29 +08:00
int pxlen ;
1998-10-17 18:25:22 +08:00
1998-10-13 22:32:18 +08:00
bzero ( & A , sizeof ( A ) ) ;
A . proto = p ;
A . source = RTS_RIP ;
A . scope = SCOPE_UNIVERSE ;
A . cast = RTC_UNICAST ;
A . dest = RTD_ROUTER ;
A . flags = 0 ;
1999-10-11 22:19:29 +08:00
# ifndef IPV6
1998-10-13 22:32:18 +08:00
A . gw = ipa_nonzero ( b - > nexthop ) ? b - > nexthop : whotoldme ;
1999-10-11 22:19:29 +08:00
pxlen = ipa_mklen ( b - > netmask ) ;
# else
A . gw = whotoldme ; /* FIXME: next hop is in other packet for v6 */
pxlen = b - > pxlen ;
# endif
1998-10-13 22:32:18 +08:00
A . from = whotoldme ;
1999-03-17 18:20:23 +08:00
1999-11-04 22:39:51 +08:00
/* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */
1999-03-17 18:20:23 +08:00
1998-10-21 00:12:43 +08:00
neighbor = neigh_find ( p , & A . gw , 0 ) ;
if ( ! neighbor ) {
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " %I asked me to route %I/%d using not-neighbor %I. " , A . from , b - > network , pxlen , A . gw ) ;
1998-10-21 00:12:43 +08:00
return ;
}
A . iface = neighbor - > iface ;
1999-03-17 18:20:23 +08:00
if ( ! ( rif = neighbor - > data ) ) {
rif = neighbor - > data = find_interface ( p , A . iface ) ;
1998-12-10 04:08:57 +08:00
}
1999-03-17 18:20:23 +08:00
if ( ! rif ) {
bug ( " Route packet using unknown interface? No. " ) ;
return ;
}
1998-10-13 22:32:18 +08:00
/* set to: interface of nexthop */
a = rta_lookup ( & A ) ;
1999-10-11 22:19:29 +08:00
if ( pxlen = = - 1 ) {
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " %I gave me invalid pxlen/netmask for %I. " , A . from , b - > network ) ;
1999-03-17 18:20:23 +08:00
return ;
}
1999-10-11 22:19:29 +08:00
n = net_get ( p - > table , b - > network , pxlen ) ;
1998-10-13 22:32:18 +08:00
r = rte_get_temp ( a ) ;
1999-06-01 03:43:08 +08:00
r - > u . rip . metric = ntohl ( b - > metric ) + rif - > patt - > metric ;
1999-02-15 21:34:43 +08:00
if ( r - > u . rip . metric > P_CF - > infinity ) r - > u . rip . metric = P_CF - > infinity ;
1998-10-13 22:32:18 +08:00
r - > u . rip . tag = ntohl ( b - > tag ) ;
1998-10-21 00:12:43 +08:00
r - > net = n ;
1998-10-13 22:32:18 +08:00
r - > pflags = 0 ; /* Here go my flags */
1999-05-18 04:16:53 +08:00
rte_update ( p - > table , n , p , r ) ;
1998-12-10 04:08:57 +08:00
DBG ( " done \n " ) ;
1998-10-13 22:32:18 +08:00
}
1998-07-10 03:39:04 +08:00
static void
process_block ( struct proto * p , struct rip_block * block , ip_addr whotoldme )
{
int metric = ntohl ( block - > metric ) ;
1998-07-29 05:44:11 +08:00
ip_addr network = block - > network ;
1998-07-10 03:39:04 +08:00
CHK_MAGIC ;
2000-03-10 00:38:51 +08:00
TRACE ( D_ROUTES , " block: %I tells me: %I/??? available, metric %d... " , whotoldme , network , metric ) ;
1999-02-15 21:34:43 +08:00
if ( ( ! metric ) | | ( metric > P_CF - > infinity ) ) {
2000-04-26 19:33:03 +08:00
# ifdef IPV6 /* Someone is sedning us nexthop and we are ignoring it */
if ( metric = = 0xff )
{ debug ( " IpV6 nexthop ignored " ) ; return ; }
# endif
1998-12-20 22:29:06 +08:00
log ( L_WARN " Got metric %d from %I " , metric , whotoldme ) ;
1998-07-10 03:39:04 +08:00
return ;
}
1998-10-13 22:32:18 +08:00
advertise_entry ( p , block , whotoldme ) ;
1998-07-10 03:39:04 +08:00
}
2000-03-13 21:31:00 +08:00
# define BAD( x ) { log( L_REMOTE "%s: " x, P_NAME ); return 1; }
1998-07-29 05:44:11 +08:00
static int
1998-07-10 03:39:04 +08:00
rip_process_packet ( struct proto * p , struct rip_packet * packet , int num , ip_addr whotoldme , int port )
{
int i ;
1999-11-04 22:26:18 +08:00
int native_class = 0 , authenticated = 0 ;
1998-07-10 03:39:04 +08:00
switch ( packet - > heading . version ) {
1998-12-10 04:08:57 +08:00
case RIP_V1 : DBG ( " Rip1: " ) ; break ;
case RIP_V2 : DBG ( " Rip2: " ) ; break ;
1998-07-10 03:39:04 +08:00
default : BAD ( " Unknown version " ) ;
}
switch ( packet - > heading . command ) {
1998-12-10 04:08:57 +08:00
case RIPCMD_REQUEST : DBG ( " Asked to send my routing table \n " ) ;
1999-08-18 21:19:33 +08:00
if ( P_CF - > honour = = HO_NEVER ) {
2000-03-13 21:31:00 +08:00
log ( L_REMOTE " They asked me to send routing table, but I was told not to do it " ) ;
1999-08-18 21:19:33 +08:00
return 0 ;
}
if ( ( P_CF - > honour = = HO_NEIGHBOUR ) & & ( ! neigh_find ( p , & whotoldme , 0 ) ) ) {
2000-03-13 21:31:00 +08:00
log ( L_REMOTE " They asked me to send routing table, but he is not my neighbour " ) ;
1999-08-18 21:19:33 +08:00
return 0 ;
}
rip_sendto ( p , whotoldme , port , HEAD ( P - > interfaces ) ) ; /* no broadcast */
1998-07-29 05:44:11 +08:00
break ;
1998-12-10 04:08:57 +08:00
case RIPCMD_RESPONSE : DBG ( " *** Rtable from %I \n " , whotoldme ) ;
1999-02-15 21:34:43 +08:00
if ( port ! = P_CF - > port ) {
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " %I send me routing info from port %d " , whotoldme , port ) ;
1998-10-21 00:45:53 +08:00
#if 0
1998-07-29 05:44:11 +08:00
return 0 ;
1998-10-21 00:45:53 +08:00
# else
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " ...ignoring " ) ;
1998-10-21 00:45:53 +08:00
# endif
1998-07-10 03:39:04 +08:00
}
if ( ! neigh_find ( p , & whotoldme , 0 ) ) {
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " %I send me routing info but he is not my neighbour " , whotoldme ) ;
1999-10-02 18:44:48 +08:00
#if 0
1998-07-29 05:44:11 +08:00
return 0 ;
1999-10-02 18:44:48 +08:00
# else
2000-03-09 22:59:10 +08:00
log ( L_REMOTE " ...ignoring " ) ;
1999-10-02 18:44:48 +08:00
# endif
1998-07-10 03:39:04 +08:00
}
for ( i = 0 ; i < num ; i + + ) {
struct rip_block * block = & packet - > block [ i ] ;
2000-04-29 23:45:30 +08:00
# ifndef IPV6
/* Authentication is not defined for v6 */
1999-11-25 22:54:08 +08:00
if ( block - > family = = 0xffff ) {
if ( i )
1999-11-25 23:03:12 +08:00
continue ; /* md5 tail has this family */
1999-12-01 20:52:57 +08:00
if ( rip_incoming_authentication ( p , ( void * ) block , packet , num , whotoldme ) )
1999-11-25 22:54:08 +08:00
BAD ( " Authentication failed " ) ;
authenticated = 1 ;
continue ;
}
2000-04-29 23:45:30 +08:00
# endif
1999-11-04 22:26:18 +08:00
if ( ( ! authenticated ) & & ( P_CF - > authtype ! = AT_NONE ) )
BAD ( " Packet is not authenticated and it should be " ) ;
1998-07-29 05:44:11 +08:00
ipa_ntoh ( block - > network ) ;
1999-10-11 22:19:29 +08:00
# ifndef IPV6
1998-07-29 05:44:11 +08:00
ipa_ntoh ( block - > netmask ) ;
ipa_ntoh ( block - > nexthop ) ;
2000-03-27 02:01:27 +08:00
if ( packet - > heading . version = = RIP_V1 ) /* FIXME (nonurgent): switch to disable this? */
1999-10-12 15:46:08 +08:00
block - > netmask = ipa_class_mask ( block - > network ) ;
1999-10-11 22:19:29 +08:00
# endif
1998-07-10 03:39:04 +08:00
process_block ( p , block , whotoldme ) ;
}
break ;
1998-10-08 03:33:50 +08:00
case RIPCMD_TRACEON :
1998-12-20 22:29:06 +08:00
case RIPCMD_TRACEOFF : BAD ( " I was asked for traceon/traceoff " ) ;
case 5 : BAD ( " Some Sun extension around here " ) ;
1998-07-10 03:39:04 +08:00
default : BAD ( " Unknown command " ) ;
}
1998-07-29 05:44:11 +08:00
return 0 ;
1998-07-10 03:39:04 +08:00
}
1998-07-29 05:44:11 +08:00
static int
1998-07-10 03:39:04 +08:00
rip_rx ( sock * s , int size )
{
1998-10-26 23:35:19 +08:00
struct rip_interface * i = s - > data ;
struct proto * p = i - > proto ;
1998-07-10 03:39:04 +08:00
int num ;
CHK_MAGIC ;
1998-12-10 04:08:57 +08:00
DBG ( " RIP: message came: %d bytes \n " , size ) ;
1998-07-10 03:39:04 +08:00
size - = sizeof ( struct rip_packet_heading ) ;
if ( size < 0 ) BAD ( " Too small packet " ) ;
if ( size % sizeof ( struct rip_block ) ) BAD ( " Odd sized packet " ) ;
num = size / sizeof ( struct rip_block ) ;
2000-04-26 19:07:57 +08:00
if ( num > PACKET_MAX ) BAD ( " Too many blocks " ) ;
1998-07-10 03:39:04 +08:00
rip_process_packet ( p , ( struct rip_packet * ) s - > rbuf , num , s - > faddr , s - > fport ) ;
1998-07-29 05:44:11 +08:00
return 1 ;
1998-07-10 03:39:04 +08:00
}
1998-07-29 05:44:11 +08:00
/*
* Interface to rest of bird
*/
1998-07-10 03:39:04 +08:00
static void
rip_dump_entry ( struct rip_entry * e )
{
1998-10-08 03:33:50 +08:00
debug ( " %I told me %d/%d ago: to %I/%d go via %I, metric %d " ,
1998-12-23 03:41:04 +08:00
e - > whotoldme , e - > updated - now , e - > changed - now , e - > n . prefix , e - > n . pxlen , e - > nexthop , e - > metric ) ;
1998-10-08 03:33:50 +08:00
if ( e - > flags & RIP_F_EXTERNAL ) debug ( " [external] " ) ;
debug ( " \n " ) ;
1998-07-10 03:39:04 +08:00
}
static void
rip_timer ( timer * t )
{
struct proto * p = t - > data ;
struct rip_entry * e , * et ;
CHK_MAGIC ;
1998-12-10 04:08:57 +08:00
DBG ( " RIP: tick tock \n " ) ;
1998-10-21 00:12:43 +08:00
WALK_LIST_DELSAFE ( e , et , P - > garbage ) {
rte * rte ;
rte = SKIP_BACK ( struct rte , u . rip . garbage , e ) ;
1998-12-10 04:08:57 +08:00
DBG ( " Garbage: " ) ; rte_dump ( rte ) ;
1998-10-21 00:12:43 +08:00
1999-11-25 20:01:45 +08:00
if ( now - rte - > u . rip . lastmodX > P_CF - > timeout_time ) {
2000-03-13 21:31:00 +08:00
TRACE ( D_EVENTS , " RIP: entry is too old: %I " , rte - > net - > n . prefix ) ;
1999-11-10 19:52:36 +08:00
e - > metric = P_CF - > infinity ;
}
1999-11-25 20:01:45 +08:00
if ( now - rte - > u . rip . lastmodX > P_CF - > garbage_time ) {
2000-03-13 21:31:00 +08:00
TRACE ( D_EVENTS , " RIP: entry is much too old: %I " , rte - > net - > n . prefix ) ;
1999-05-18 04:16:53 +08:00
rte_discard ( p - > table , rte ) ;
1998-10-21 00:12:43 +08:00
}
1998-07-10 03:39:04 +08:00
}
1998-07-29 05:44:11 +08:00
1998-12-10 04:08:57 +08:00
DBG ( " RIP: Broadcasting routing tables \n " ) ;
1998-10-21 00:12:43 +08:00
{
1998-12-23 03:41:04 +08:00
struct rip_interface * rif ;
1999-11-25 21:38:25 +08:00
P - > tx_count + + ;
1998-10-26 23:35:19 +08:00
1998-12-23 03:41:04 +08:00
WALK_LIST ( rif , P - > interfaces ) {
struct iface * iface = rif - > iface ;
1998-07-29 05:44:11 +08:00
1998-12-23 03:41:04 +08:00
if ( ! iface ) continue ;
1999-05-11 17:53:45 +08:00
if ( rif - > patt - > mode & IM_QUIET ) continue ;
1998-10-21 00:12:43 +08:00
if ( ! ( iface - > flags & IF_UP ) ) continue ;
1999-11-25 21:38:25 +08:00
rif - > triggered = ( P - > tx_count % 6 ) ;
1998-12-23 03:41:04 +08:00
rip_sendto ( p , IPA_NONE , 0 , rif ) ;
1998-10-21 00:12:43 +08:00
}
}
1998-10-08 03:33:50 +08:00
1998-12-10 04:08:57 +08:00
DBG ( " RIP: tick tock done \n " ) ;
1998-07-10 03:39:04 +08:00
}
2000-04-28 18:14:59 +08:00
/**
* rip_start - initialize instance of rip
*
* Rip is pretty simple protocol so half of this code is interface
* with core . We maintain our own linklist of & rip_entry - it serves
* as our small routing table . Within rip_tx ( ) , this list is
* walked , and packet is generated using rip_tx_prepare ( ) . This gets
* tricky because we may need to send more than one packet to one
* destination . Struct & rip_connection is used to hold info such as how
* many of & rip_entry ies we already send , and is also used to protect
* from two concurrent sends to one destination . Each & rip_interface has
* at most one & rip_connection .
*
* We are not going to honour requests for sending part of
* routing table . That would need to turn split horizon off ,
* etc .
*
* Triggered updates . RFC says : when triggered update was sent , don ' t send
* new one for something between 1 and 5 seconds ( and send one
* after that ) . We do something else : once in 5 second
* we look for any changed routes and broadcast them .
*/
1999-02-09 06:50:32 +08:00
static int
1998-07-10 03:39:04 +08:00
rip_start ( struct proto * p )
{
1998-12-23 03:41:04 +08:00
struct rip_interface * rif ;
1998-12-10 04:08:57 +08:00
DBG ( " RIP: starting instance... \n " ) ;
1998-07-10 03:39:04 +08:00
P - > magic = RIP_MAGIC ;
1998-12-23 03:41:04 +08:00
fib_init ( & P - > rtable , p - > pool , sizeof ( struct rip_entry ) , 0 , NULL ) ;
1998-07-10 03:39:04 +08:00
init_list ( & P - > connections ) ;
1998-10-21 00:12:43 +08:00
init_list ( & P - > garbage ) ;
1998-10-26 23:35:19 +08:00
init_list ( & P - > interfaces ) ;
1998-07-10 03:39:04 +08:00
P - > timer = tm_new ( p - > pool ) ;
P - > timer - > data = p ;
P - > timer - > randomize = 5 ;
1999-12-08 20:51:45 +08:00
P - > timer - > recurrent = ( P_CF - > period / 6 ) + 1 ;
1998-07-10 03:39:04 +08:00
P - > timer - > hook = rip_timer ;
1998-07-29 05:44:11 +08:00
tm_start ( P - > timer , 5 ) ;
1999-05-11 17:53:45 +08:00
rif = new_iface ( p , NULL , 0 , NULL ) ; /* Initialize dummy interface */
1998-12-23 03:41:04 +08:00
add_head ( & P - > interfaces , NODE rif ) ;
1998-10-15 23:12:24 +08:00
CHK_MAGIC ;
1998-07-10 03:39:04 +08:00
1999-03-17 21:05:25 +08:00
rip_init_instance ( p ) ;
1998-12-10 04:08:57 +08:00
DBG ( " RIP: ...done \n " ) ;
1999-02-09 06:50:32 +08:00
return PS_UP ;
1998-07-10 03:39:04 +08:00
}
1999-02-09 06:50:32 +08:00
static struct proto *
rip_init ( struct proto_config * cfg )
1998-07-10 03:39:04 +08:00
{
1999-03-02 05:18:01 +08:00
struct proto * p = proto_new ( cfg , sizeof ( struct rip_proto ) ) ;
1999-02-09 06:50:32 +08:00
return p ;
1998-07-10 03:39:04 +08:00
}
static void
rip_dump ( struct proto * p )
{
int i ;
node * w , * e ;
1998-10-26 23:35:19 +08:00
struct rip_interface * rif ;
1998-07-10 03:39:04 +08:00
i = 0 ;
1999-03-02 05:18:01 +08:00
CHK_MAGIC ;
1998-07-10 03:39:04 +08:00
WALK_LIST ( w , P - > connections ) {
1998-07-29 05:44:11 +08:00
struct rip_connection * n = ( void * ) w ;
debug ( " RIP: connection #%d: %I \n " , n - > num , n - > addr ) ;
1998-07-10 03:39:04 +08:00
}
i = 0 ;
1998-12-23 03:41:04 +08:00
FIB_WALK ( & P - > rtable , e ) {
1998-07-10 03:39:04 +08:00
debug ( " RIP: entry #%d: " , i + + ) ;
rip_dump_entry ( E ) ;
1998-12-23 03:41:04 +08:00
} FIB_WALK_END ;
1998-10-26 23:35:19 +08:00
i = 0 ;
WALK_LIST ( rif , P - > interfaces ) {
1998-12-09 23:22:40 +08:00
debug ( " RIP: interface #%d: %s, %I, busy = %x \n " , i + + , rif - > iface ? rif - > iface - > name : " (dummy) " , rif - > sock - > daddr , rif - > busy ) ;
1998-10-26 23:35:19 +08:00
}
}
1999-12-08 21:33:44 +08:00
static void
2000-01-26 20:07:18 +08:00
rip_get_route_info ( rte * rte , byte * buf )
1999-12-08 21:33:44 +08:00
{
2000-04-01 07:33:03 +08:00
buf + = bsprintf ( buf , " (%d/%d) " , rte - > pref , rte - > u . rip . metric ) ;
bsprintf ( buf , " t%04x " , rte - > u . rip . tag ) ;
1999-12-08 21:33:44 +08:00
}
1998-10-26 23:35:19 +08:00
static int
rip_want_this_if ( struct rip_interface * iface )
{
return 1 ;
}
static void
kill_iface ( struct proto * p , struct rip_interface * i )
{
1998-12-10 04:08:57 +08:00
DBG ( " RIP: Interface %s disappeared \n " , i - > iface - > name ) ;
1998-10-26 23:35:19 +08:00
rfree ( i - > sock ) ;
mb_free ( i ) ;
}
2000-04-28 17:55:52 +08:00
/**
* new_iface - actually create struct interface and start listening to it
* @ new : interface to be created or % NULL if we are creating magic
* socket . Magic socket is used for listening , and is also used for
* sending requested responses .
1998-12-09 23:22:40 +08:00
*/
1999-06-01 04:30:16 +08:00
static struct rip_interface *
1999-05-11 17:53:45 +08:00
new_iface ( struct proto * p , struct iface * new , unsigned long flags , struct iface_patt * patt )
1998-10-26 23:35:19 +08:00
{
1998-12-09 23:22:40 +08:00
struct rip_interface * rif ;
1999-06-01 03:37:16 +08:00
rif = mb_allocz ( p - > pool , sizeof ( struct rip_interface ) ) ;
1998-12-09 23:22:40 +08:00
rif - > iface = new ;
rif - > proto = p ;
1998-12-23 03:41:04 +08:00
rif - > busy = NULL ;
1999-05-11 17:53:45 +08:00
rif - > patt = ( struct rip_patt * ) patt ;
1998-12-09 23:22:40 +08:00
1999-05-11 17:53:45 +08:00
if ( rif - > patt )
2000-03-27 02:01:27 +08:00
rif - > multicast = ( ! ( rif - > patt - > mode & IM_BROADCAST ) ) & & ( flags & IF_MULTICAST ) ;
1999-05-11 17:53:45 +08:00
/* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */
1998-12-09 23:22:40 +08:00
2000-03-27 02:01:27 +08:00
if ( rif - > multicast )
1999-06-01 04:30:16 +08:00
DBG ( " Doing multicasts! \n " ) ;
1998-12-09 23:22:40 +08:00
rif - > sock = sk_new ( p - > pool ) ;
2000-03-27 02:01:27 +08:00
rif - > sock - > type = rif - > multicast ? SK_UDP_MC : SK_UDP ;
1999-02-15 21:34:43 +08:00
rif - > sock - > sport = P_CF - > port ;
1998-12-09 23:22:40 +08:00
rif - > sock - > rx_hook = rip_rx ;
rif - > sock - > data = rif ;
rif - > sock - > rbsize = 10240 ;
1998-12-23 03:41:04 +08:00
rif - > sock - > iface = new ; /* Automagically works for dummy interface */
1998-12-09 23:22:40 +08:00
rif - > sock - > tbuf = mb_alloc ( p - > pool , sizeof ( struct rip_packet ) ) ;
rif - > sock - > tx_hook = rip_tx ;
rif - > sock - > err_hook = rip_tx_err ;
rif - > sock - > daddr = IPA_NONE ;
1999-02-15 21:34:43 +08:00
rif - > sock - > dport = P_CF - > port ;
1999-08-18 21:19:33 +08:00
if ( new )
rif - > sock - > ttl = 1 ;
else
rif - > sock - > ttl = 30 ;
1999-06-01 21:57:24 +08:00
rif - > sock - > tos = IP_PREC_INTERNET_CONTROL ;
1998-12-09 23:22:40 +08:00
2000-03-22 22:26:03 +08:00
if ( new ) {
if ( new - > addr - > flags & IA_UNNUMBERED )
log ( L_WARN " %s: rip is not defined over unnumbered links " , P_NAME ) ;
2000-03-27 02:01:27 +08:00
if ( rif - > multicast ) {
2000-04-26 19:33:03 +08:00
# ifndef IPV6
2000-03-22 22:26:03 +08:00
rif - > sock - > daddr = ipa_from_u32 ( 0xe0000009 ) ;
rif - > sock - > saddr = ipa_from_u32 ( 0xe0000009 ) ;
2000-04-26 19:33:03 +08:00
# else
2000-04-29 23:45:30 +08:00
ip_pton ( " FF02::9 " , & rif - > sock - > daddr ) ;
ip_pton ( " FF02::9 " , & rif - > sock - > saddr ) ;
2000-04-26 19:33:03 +08:00
# endif
2000-03-27 02:01:27 +08:00
} else
rif - > sock - > daddr = new - > addr - > brd ;
1999-06-01 04:34:48 +08:00
}
1998-12-09 23:22:40 +08:00
1999-05-11 17:53:45 +08:00
if ( ! ipa_nonzero ( rif - > sock - > daddr ) ) {
2000-03-13 21:31:00 +08:00
log ( L_WARN " %s: interface %s is too strange for me " , P_NAME , rif - > iface ? rif - > iface - > name : " (dummy) " ) ;
1999-05-11 17:53:45 +08:00
} else
if ( ! ( rif - > patt - > mode & IM_NOLISTEN ) )
1999-06-01 04:30:16 +08:00
if ( sk_open ( rif - > sock ) < 0 ) {
2000-03-13 21:31:00 +08:00
log ( L_ERR " %s: could not listen on %s " , P_NAME , rif - > iface ? rif - > iface - > name : " (dummy) " ) ;
1999-08-18 21:19:33 +08:00
/* Don't try to transmit into this one? Well, why not? This should not happen, anyway :-) */
1999-06-01 04:30:16 +08:00
}
2000-03-27 02:01:27 +08:00
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 ) ;
1998-10-26 23:35:19 +08:00
1998-12-09 23:22:40 +08:00
return rif ;
1998-07-10 03:39:04 +08:00
}
2000-03-22 22:26:03 +08:00
static void
rip_real_if_add ( struct object_lock * lock )
{
struct iface * iface = lock - > iface ;
struct proto * p = lock - > data ;
struct rip_interface * rif ;
struct iface_patt * k = iface_patt_match ( & P_CF - > iface_list , iface ) ;
if ( ! k )
bug ( " This can not happen! It existed few seconds ago! " ) ;
DBG ( " adding interface %s \n " , iface - > name ) ;
rif = new_iface ( p , iface , iface - > flags , k ) ;
add_head ( & P - > interfaces , NODE rif ) ;
rif - > lock = lock ;
}
1998-07-29 05:44:11 +08:00
static void
1999-05-07 05:38:11 +08:00
rip_if_notify ( struct proto * p , unsigned c , struct iface * iface )
1998-07-29 05:44:11 +08:00
{
1998-12-10 04:08:57 +08:00
DBG ( " RIP: if notify \n " ) ;
1999-05-07 05:38:11 +08:00
if ( iface - > flags & IF_IGNORE )
return ;
if ( c & IF_CHANGE_DOWN ) {
1998-10-26 23:35:19 +08:00
struct rip_interface * i ;
1999-05-07 05:38:11 +08:00
i = find_interface ( p , iface ) ;
1998-12-04 19:45:51 +08:00
if ( i ) {
rem_node ( NODE i ) ;
kill_iface ( p , i ) ;
2000-03-22 22:26:03 +08:00
rfree ( i - > lock ) ;
1998-12-04 19:45:51 +08:00
}
1998-10-26 23:35:19 +08:00
}
1999-05-07 05:38:11 +08:00
if ( c & IF_CHANGE_UP ) {
1998-12-09 23:22:40 +08:00
struct rip_interface * rif ;
1999-05-07 05:38:11 +08:00
struct iface_patt * k = iface_patt_match ( & P_CF - > iface_list , iface ) ;
2000-03-22 22:26:03 +08:00
struct object_lock * lock ;
1998-12-04 19:45:51 +08:00
if ( ! k ) return ; /* We are not interested in this interface */
2000-03-22 22:26:03 +08:00
lock = olock_new ( p - > pool ) ;
2000-04-29 23:45:30 +08:00
# ifndef IPV6
2000-04-28 17:55:52 +08:00
lock - > addr = ipa_from_u32 ( 0xe0000009 ) ; /* This is okay: we
may actually use
other address , but
we do not want two
rips at one time ,
anyway . */
2000-04-29 23:45:30 +08:00
# else
ip_pton ( " FF02::9 " , & lock - > addr ) ;
# endif
2000-03-22 22:26:03 +08:00
lock - > port = P_CF - > port ;
lock - > iface = iface ;
lock - > hook = rip_real_if_add ;
lock - > data = p ;
2000-03-27 02:01:27 +08:00
lock - > type = OBJLOCK_UDP ;
2000-03-22 22:26:03 +08:00
olock_acquire ( lock ) ;
1998-10-26 23:35:19 +08:00
}
1998-07-29 05:44:11 +08:00
}
1999-06-01 03:16:22 +08:00
static struct ea_list *
rip_gen_attrs ( struct proto * p , struct linpool * pool , int metric , u16 tag )
{
struct ea_list * l = lp_alloc ( pool , sizeof ( struct ea_list ) + 2 * sizeof ( eattr ) ) ;
l - > next = NULL ;
l - > flags = EALF_SORTED ;
l - > count = 2 ;
l - > attrs [ 0 ] . id = EA_RIP_TAG ;
l - > attrs [ 0 ] . flags = 0 ;
2000-03-05 06:30:44 +08:00
l - > attrs [ 0 ] . type = EAF_TYPE_INT | EAF_TEMP ;
1999-06-01 03:16:22 +08:00
l - > attrs [ 0 ] . u . data = tag ;
1999-12-08 21:33:44 +08:00
l - > attrs [ 1 ] . id = EA_RIP_METRIC ;
1999-06-01 03:16:22 +08:00
l - > attrs [ 1 ] . flags = 0 ;
2000-03-05 06:30:44 +08:00
l - > attrs [ 1 ] . type = EAF_TYPE_INT | EAF_TEMP ;
1999-06-01 03:16:22 +08:00
l - > attrs [ 1 ] . u . data = metric ;
return l ;
}
static int
rip_import_control ( struct proto * p , struct rte * * rt , struct ea_list * * attrs , struct linpool * pool )
{
if ( ( * rt ) - > attrs - > proto = = p ) /* My own must not be touched */
return 1 ;
if ( ( * rt ) - > attrs - > source ! = RTS_RIP ) {
struct ea_list * new = rip_gen_attrs ( p , pool , 1 , 0 ) ;
new - > next = * attrs ;
* attrs = new ;
}
return 0 ;
}
static struct ea_list *
rip_make_tmp_attrs ( struct rte * rt , struct linpool * pool )
{
struct proto * p = rt - > attrs - > proto ;
return rip_gen_attrs ( p , pool , rt - > u . rip . metric , rt - > u . rip . tag ) ;
}
static void
rip_store_tmp_attrs ( struct rte * rt , struct ea_list * attrs )
{
struct proto * p = rt - > attrs - > proto ;
rt - > u . rip . tag = ea_find ( attrs , EA_RIP_TAG ) - > u . data ;
1999-12-08 21:33:44 +08:00
rt - > u . rip . metric = ea_find ( attrs , EA_RIP_METRIC ) - > u . data ;
1999-06-01 03:16:22 +08:00
}
1998-10-08 03:33:50 +08:00
static void
1999-06-01 03:16:22 +08:00
rip_rt_notify ( struct proto * p , struct network * net , struct rte * new , struct rte * old , struct ea_list * attrs )
1998-10-08 03:33:50 +08:00
{
1998-10-15 23:12:24 +08:00
CHK_MAGIC ;
1998-10-08 03:33:50 +08:00
if ( old ) {
1998-12-23 03:41:04 +08:00
struct rip_entry * e = fib_find ( & P - > rtable , & net - > n . prefix , net - > n . pxlen ) ;
1998-10-08 03:33:50 +08:00
if ( ! e )
1998-12-23 03:41:04 +08:00
log ( L_BUG " Deleting nonexistent entry?! " ) ;
fib_delete ( & P - > rtable , e ) ;
1998-10-08 03:33:50 +08:00
}
if ( new ) {
1998-12-23 03:41:04 +08:00
struct rip_entry * e ;
if ( fib_find ( & P - > rtable , & net - > n . prefix , net - > n . pxlen ) )
log ( L_BUG " Inserting entry which is already there? " ) ;
e = fib_get ( & P - > rtable , & net - > n . prefix , net - > n . pxlen ) ;
1998-10-08 03:33:50 +08:00
1998-10-17 18:25:22 +08:00
e - > nexthop = new - > attrs - > gw ;
2000-03-27 20:21:11 +08:00
e - > metric = 0 ;
e - > whotoldme = IPA_NONE ;
2000-03-29 16:58:06 +08:00
e - > tag = ea_find ( attrs , EA_RIP_TAG ) - > u . data ;
e - > metric = ea_find ( attrs , EA_RIP_METRIC ) - > u . data ;
if ( e - > metric > P_CF - > infinity )
e - > metric = P_CF - > infinity ;
if ( new - > attrs - > proto = = p )
2000-03-27 20:21:11 +08:00
e - > whotoldme = new - > attrs - > from ;
2000-04-07 17:02:17 +08:00
if ( ! e - > metric ) /* That's okay: this way user can set his own value for external
2000-03-29 16:58:06 +08:00
routes in rip . */
1999-12-08 21:33:44 +08:00
e - > metric = 5 ;
1998-10-08 03:33:50 +08:00
e - > updated = e - > changed = now ;
1998-10-13 22:32:18 +08:00
e - > flags = 0 ;
1998-10-08 03:33:50 +08:00
}
}
1998-10-21 00:12:43 +08:00
static int
1998-10-13 22:32:18 +08:00
rip_rte_better ( struct rte * new , struct rte * old )
{
2000-03-29 16:58:06 +08:00
struct proto * p = new - > attrs - > proto ;
1999-11-10 19:52:36 +08:00
if ( ipa_equal ( old - > attrs - > from , new - > attrs - > from ) )
return 1 ;
1998-10-13 22:32:18 +08:00
if ( old - > u . rip . metric < new - > u . rip . metric )
return 0 ;
if ( old - > u . rip . metric > new - > u . rip . metric )
return 1 ;
2000-03-29 16:58:06 +08:00
if ( ( old - > u . rip . metric < 16 ) & & ( new - > u . rip . metric = = P_CF - > infinity ) ) {
1999-11-25 20:01:45 +08:00
new - > u . rip . lastmodX = now - P_CF - > timeout_time ; /* Check this: if new metric is 16, act as it was timed out */
}
2000-03-29 16:58:06 +08:00
if ( old - > attrs - > proto = = new - > attrs - > proto ) /* This does not make much sense for different protocols */
if ( ( old - > u . rip . metric = = new - > u . rip . metric ) & &
( ( now - old - > u . rip . lastmodX ) > ( P_CF - > timeout_time / 2 ) ) )
return 1 ;
1998-10-13 22:32:18 +08:00
return 0 ;
}
1998-10-21 00:12:43 +08:00
static void
rip_rte_insert ( net * net , rte * rte )
{
struct proto * p = rte - > attrs - > proto ;
1999-11-25 20:01:45 +08:00
rte - > u . rip . lastmodX = now ;
1998-10-21 00:12:43 +08:00
add_head ( & P - > garbage , & rte - > u . rip . garbage ) ;
}
static void
rip_rte_remove ( net * net , rte * rte )
{
struct proto * p = rte - > attrs - > proto ;
rem_node ( & rte - > u . rip . garbage ) ;
}
1998-11-28 05:08:37 +08:00
void
rip_init_instance ( struct proto * p )
1998-07-10 03:39:04 +08:00
{
1998-10-21 00:12:43 +08:00
p - > preference = DEF_PREF_RIP ;
1998-07-10 03:39:04 +08:00
p - > if_notify = rip_if_notify ;
1998-10-08 03:33:50 +08:00
p - > rt_notify = rip_rt_notify ;
1999-06-01 03:16:22 +08:00
p - > import_control = rip_import_control ;
1999-06-01 04:30:16 +08:00
p - > make_tmp_attrs = rip_make_tmp_attrs ;
p - > store_tmp_attrs = rip_store_tmp_attrs ;
1998-10-13 22:32:18 +08:00
p - > rte_better = rip_rte_better ;
1998-10-21 00:12:43 +08:00
p - > rte_insert = rip_rte_insert ;
p - > rte_remove = rip_rte_remove ;
1999-03-02 05:18:01 +08:00
}
1998-12-04 19:45:51 +08:00
1999-03-02 05:18:01 +08:00
void
rip_init_config ( struct rip_proto_config * c )
{
init_list ( & c - > iface_list ) ;
c - > infinity = 16 ;
c - > port = 520 ;
c - > period = 30 ;
c - > garbage_time = 120 + 180 ;
1999-11-10 19:52:36 +08:00
c - > timeout_time = 120 ;
1999-05-26 22:37:47 +08:00
c - > passwords = NULL ;
1999-05-11 17:53:45 +08:00
c - > authtype = AT_NONE ;
1998-07-10 03:39:04 +08:00
}
1998-11-28 05:08:37 +08:00
static void
1999-02-09 06:50:32 +08:00
rip_preconfig ( struct protocol * x , struct config * c )
1998-11-28 05:08:37 +08:00
{
1998-12-10 04:08:57 +08:00
DBG ( " RIP: preconfig \n " ) ;
1998-11-28 05:08:37 +08:00
}
2000-04-28 18:14:59 +08:00
static int
rip_get_attr ( eattr * a , byte * buf )
{
unsigned int i = EA_ID ( a - > id ) ;
struct attr_desc * d ;
switch ( a - > id ) {
case EA_RIP_METRIC : buf + = bsprintf ( buf , " metric: %d " , a - > u . data ) ; return GA_FULL ;
case EA_RIP_TAG : buf + = bsprintf ( buf , " tag: %d " , a - > u . data ) ; return GA_FULL ;
default : return GA_UNKNOWN ;
}
}
1998-07-10 03:39:04 +08:00
struct protocol proto_rip = {
1999-02-09 06:50:32 +08:00
name : " RIP " ,
2000-01-17 19:52:50 +08:00
template : " rip%d " ,
2000-04-01 18:20:12 +08:00
attr_class : EAP_RIP ,
1999-02-09 06:50:32 +08:00
preconfig : rip_preconfig ,
1999-12-08 21:33:44 +08:00
get_route_info : rip_get_route_info ,
2000-04-28 18:14:59 +08:00
get_attr : rip_get_attr ,
1999-02-09 06:50:32 +08:00
init : rip_init ,
dump : rip_dump ,
start : rip_start ,
1998-07-10 03:39:04 +08:00
} ;