home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3
/
hamradioversion3.0examsandprograms1992.iso
/
packet
/
n17jsrc
/
rip.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-02-10
|
17KB
|
676 lines
/* This file contains code to implement the Routing Information Protocol (RIP)
* and is derived from 4.2BSD code. Mike Karels of Berkeley has stated on
* TCP-IP that the code may be freely used as long as UC Berkeley is
* credited. (Well, here's some credit :-). AGB 4-28-88
* Further documentation on the RIP protocol is now available in Charles
* Hedrick's draft RFC, as yet unnumbered. AGB 5-6-88
*
* The RIP RFC has now been issued as RFC1058. AGB 7-23-88
*
* Code gutted and substantially rewritten. KA9Q 9/89
*/
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "udp.h"
#include "timer.h"
#include "iface.h"
#include "ip.h"
#include "internet.h"
#include "rip.h"
#include "arp.h"
struct rip_stat Rip_stat;
int16 Rip_trace;
int Rip_merge;
struct rip_list *Rip_list;
struct udp_cb *Rip_cb;
struct rip_refuse *Rip_refuse;
static void rip_rx __ARGS((struct iface *iface,struct udp_cb *sock,int cnt));
static void proc_rip __ARGS((struct iface *iface,int32 gateway,
struct rip_route *ep));
static char *putheader __ARGS((char *cp,char command,char version));
static char *putentry __ARGS((char *cp,int16 fam,int32 target,int32 metric));
static void rip_shout __ARGS((void *p));
static void send_routes __ARGS((int32 dest,int16 port,int split,int trig));
/* Send RIP CMD_RESPONSE packet(s) to the specified rip_list entry */
static void
rip_shout(p)
void *p;
{
register struct rip_list *rl;
rl = (struct rip_list *)p;
stop_timer(&rl->rip_time);
send_routes(rl->dest,RIP_PORT,(rl->flags & RIP_SPLIT),0);
set_timer(&rl->rip_time,rl->interval*1000L);
start_timer(&rl->rip_time);
}
/* Send the routing table. */
static void
send_routes(dest,port,split,trig)
int32 dest; /* IP destination address to send to */
int16 port;
int split; /* Do split horizon? */
int trig; /* Send only triggered updates? */
{
char *cp;
int i,bits,numroutes,maxroutes;
int16 pktsize;
struct mbuf *bp;
struct route *rp;
struct socket lsock,fsock;
struct iface *iface;
if((rp = rt_lookup(dest)) == NULLROUTE)
return; /* No route exists, can't do it */
iface = rp->iface;
/* Compute maximum packet size and number of routes we can send */
pktsize = ip_mtu(dest) - IPLEN;
pktsize = min(pktsize,MAXRIPPACKET);
maxroutes = (pktsize - RIPHEADER) / RIPROUTE;
lsock.address = INADDR_ANY;
lsock.port = RIP_PORT;
fsock.address = dest;
fsock.port = port;
/* Allocate space for a full size RIP packet and generate header */
if((bp = alloc_mbuf(pktsize)) == NULLBUF)
return;
numroutes = 0;
cp = putheader(bp->data,RIPCMD_RESPONSE,RIPVERSION);
/* Emit default route, if appropriate */
if(R_default.iface != NULLIF && !(R_default.flags & RTPRIVATE)
&& (!trig || (R_default.flags & RTTRIG))){
if(!split || iface != R_default.iface){
cp = putentry(cp,RIP_IPFAM,0,R_default.metric);
numroutes++;
} else if(trig){
cp = putentry(cp,RIP_IPFAM,0,RIP_INFINITY);
numroutes++;
}
}
for(bits=0;bits<32;bits++){
for(i=0;i<HASHMOD;i++){
for(rp = Routes[bits][i];rp != NULLROUTE;rp=rp->next){
if((rp->flags & RTPRIVATE)
|| (trig && !(rp->flags & RTTRIG)))
continue;
if(numroutes >= maxroutes){
/* Packet full, flush and make another */
bp->cnt = RIPHEADER + numroutes * RIPROUTE;
send_udp(&lsock,&fsock,0,0,bp,bp->cnt,0,0);
Rip_stat.output++;
if((bp = alloc_mbuf(pktsize)) == NULLBUF)
return;
numroutes = 0;
cp = putheader(bp->data,RIPCMD_RESPONSE,RIPVERSION);
}
if(!split || iface != rp->iface){
cp = putentry(cp,RIP_IPFAM,rp->target,rp->metric);
numroutes++;
} else if(trig){
cp = putentry(cp,RIP_IPFAM,rp->target,RIP_INFINITY);
numroutes++;
}
}
}
}
if(numroutes != 0){
bp->cnt = RIPHEADER + numroutes * RIPROUTE;
send_udp(&lsock,&fsock,0,0,bp,bp->cnt,0,0);
Rip_stat.output++;
} else {
free_p(bp);
}
}
/* Add an entry to the rip broadcast list */
int
rip_add(dest,interval,flags)
int32 dest;
int32 interval;
char flags;
{
register struct rip_list *rl;
struct route *rp;
for(rl = Rip_list; rl != NULLRL; rl = rl->next)
if(rl->dest == dest)
return 0;
if((rp = rt_lookup(dest)) == NULLROUTE){
tprintf("%s is unreachable\n",inet_ntoa(dest));
return 1;
}
/* get a chunk of memory for the rip interface descriptor */
rl = (struct rip_list *)callocw(1,sizeof(struct rip_list));
/* tack this record on as the first in the list */
rl->next = Rip_list;
if(rl->next != NULLRL)
rl->next->prev = rl;
Rip_list = rl;
rl->dest = dest;
/* and the interface ptr, tick interval and flags */
rl->iface = rp->iface;
rl->interval = interval;
rl->flags = flags;
/* and set up the timer stuff */
set_timer(&rl->rip_time,interval*1000L);
rl->rip_time.func = rip_shout;
rl->rip_time.arg = rl;
start_timer(&rl->rip_time);
return 0;
}
/* add a gateway to the rip_refuse list which allows us to ignore their
* advertisements
*/
int
riprefadd(gateway)
int32 gateway;
{
register struct rip_refuse *rl;
for(rl = Rip_refuse; rl != NULLREF; rl = rl->next)
if(rl->target == gateway)
return 0; /* Already in table */
/* get a chunk of memory for the rip interface descriptor */
rl = (struct rip_refuse *)callocw(1,sizeof(struct rip_refuse));
/* tack this record on as the first in the list */
rl->next = Rip_refuse;
if(rl->next != NULLREF)
rl->next->prev = rl;
Rip_refuse = rl;
/* fill in the gateway to ignore */
rl->target = gateway;
return 0;
}
/* drop a RIP target */
int
rip_drop(dest)
int32 dest;
{
register struct rip_list *rl;
for(rl = Rip_list; rl != NULLRL; rl = rl->next)
if(rl->dest == dest)
break;
/* leave if we didn't find it */
if(rl == NULLRL)
return 0;
/* stop the timer */
stop_timer(&rl->rip_time);
/* Unlink from list */
if(rl->next != NULLRL)
rl->next->prev = rl->prev;
if(rl->prev != NULLRL)
rl->prev->next = rl->next;
else
Rip_list = rl->next;
/* and deallocate the descriptor memory */
free((char *)rl);
return 0;
}
/* drop a RIP-refuse target from the rip_refuse list */
int
riprefdrop(gateway)
int32 gateway;
{
register struct rip_refuse *rl;
for(rl = Rip_refuse; rl != NULLREF; rl = rl->next)
if(rl->target == gateway)
break;
/* leave if we didn't find it */
if(rl == NULLREF)
return 0;
/* Unlink from list */
if(rl->next != NULLREF)
rl->next->prev = rl->prev;
if(rl->prev != NULLREF)
rl->prev->next = rl->next;
else
Rip_refuse = rl->next;
/* and deallocate the structure memory */
free((char *)rl);
return 0;
}
/* function to output a RIP CMD_RESPONSE packet for the rip_trigger list */
void
rip_trigger()
{
register struct rip_list *rl;
int bits,i;
struct route *rp;
for(rl=Rip_list;rl != NULLRL;rl = rl->next){
send_routes(rl->dest,RIP_PORT,(rl->flags & RIP_SPLIT),1);
}
/* Clear the trigger list */
R_default.flags &= ~RTTRIG;
for(bits=0;bits<32;bits++){
for(i=0;i<HASHMOD;i++){
for(rp = Routes[bits][i];rp != NULLROUTE;rp = rp->next){
rp->flags &= ~RTTRIG;
}
}
}
}
/* Start RIP agent listening at local RIP UDP port */
int
rip_init()
{
struct socket lsock;
lsock.address = INADDR_ANY;
lsock.port = RIP_PORT;
if(Rip_cb == NULLUDP)
Rip_cb = open_udp(&lsock,rip_rx);
return 0;
}
/* Process RIP input received from 'interface'. */
static void
rip_rx(iface,sock,cnt)
struct iface *iface;
struct udp_cb *sock;
int cnt;
{
struct mbuf *bp;
struct socket fsock;
int cmd;
register struct rip_refuse *rfl;
struct rip_route entry;
struct route *rp;
/* receive the RIP packet */
recv_udp(sock,&fsock,&bp);
/* increment the rcvd cnt */
Rip_stat.rcvd++;
/* check the gateway of this packet against the rip_refuse list and
* discard it if a match is found
*/
for(rfl=Rip_refuse;rfl != NULLREF;rfl = rfl->next){
if(fsock.address == rfl->target){
Rip_stat.refusals++;
if(Rip_trace > 1)
printf("RIP refused from %s\n",
inet_ntoa(fsock.address));
free_p(bp);
return;
}
}
cmd = PULLCHAR(&bp);
/* Check the version of the frame */
if(PULLCHAR(&bp) != RIPVERSION){
free_p(bp);
Rip_stat.version++;
return;
}
switch(cmd){
case RIPCMD_RESPONSE:
if(Rip_trace > 1)
printf("RIPCMD_RESPONSE from %s \n",inet_ntoa(fsock.address));
Rip_stat.response++;
(void)pull16(&bp); /* remove one word of padding */
while(len_p(bp) >= RIPROUTE){
pullentry(&entry,&bp);
proc_rip(iface,fsock.address,&entry);
}
/* If we can't reach the sender of this update, or if
* our existing route is not through the interface we
* got this update on, add him as a host specific entry
*/
if((rp = rt_blookup(fsock.address,32)) != NULLROUTE){
/* Host-specific route already exists, refresh it */
start_timer(&rp->timer);
} else if((rp = rt_lookup(fsock.address)) == NULLROUTE
|| rp->iface != iface){
entry.addr_fam = RIP_IPFAM;
entry.target = fsock.address;
entry.metric = 0; /* will get incremented to 1 */
proc_rip(iface,fsock.address,&entry);
}
if(Rip_merge)
rt_merge(Rip_trace);
rip_trigger();
break;
case RIPCMD_REQUEST:
if(Rip_trace > 1)
printf("RIPCMD_REQUEST\n");
Rip_stat.request++;
/* For now, just send the whole table with split horizon
* enabled when the source port is RIP_PORT, and send
* the whole table with split horizon disable when another
* source port is used. This should be replaced with a more
* complete implementation that checks for non-global requests
*/
if(fsock.port == RIP_PORT)
send_routes(fsock.address,fsock.port,1,0);
else
send_routes(fsock.address,fsock.port,0,0);
break;
default:
if(Rip_trace > 1)
printf("RIPCMD: Unknown Type\n");
Rip_stat.unknown++;
break;
} /* switch */
free_p(bp);
}
/* Apply a set of heuristics for determining the number of significant bits
* (i.e., the address mask) in the target address. Needed since RIP doesn't
* include the address mask for each entry.
*/
int
nbits(target)
int32 target;
{
int bits;
if(target == 0)
return 0; /* Special case: 0.0.0.0 is the default route */
/* Check the host-part bytes of
* the address to check for byte-wide zeros
* which we'll consider to be subnet routes.
* e.g. 44.80.0.0 will be considered to be equal to 44.80/16
* whereas 44.80.1.0 will be considered to be 44.80.1/24
*/
switch (hibyte(hiword(target)) >> 6) {
case 3: /* Class C address */
/*is it a host address ? i.e. are there any 1's in the
* host part ?
*/
if(target & 0xff)
bits = 32;
else
bits = 24;
break;
case 2: /* Class B address */
if(target & 0xff)
bits = 32;
else if(target & 0xff00)
bits = 24;
else
bits = 16;
break;
case 0: /* Class A address */
case 1:
if(target & 0xff)
bits = 32;
else if(target & 0xff00)
bits = 24;
else if(target & 0xff0000)
bits = 16;
else
bits = 8;
}
return bits;
}
/* Remove and process a RIP response entry from a packet */
static void
proc_rip(iface,gateway,ep)
struct iface *iface;
int32 gateway;
register struct rip_route *ep;
{
int32 interval;
unsigned int bits;
register struct route *rp;
struct rip_list *rl;
int add = 0; /* action flags */
int drop = 0;
int trigger = 0;
if(ep->addr_fam != RIP_IPFAM) {
/* Skip non-IP addresses */
if(Rip_trace > 1)
printf("RIP_rx: Not an IP RIP packet !\n");
Rip_stat.addr_family++;
return;
}
/* Guess at the mask, since it's not explicit */
bits = nbits(ep->target);
/* Don't ever add a route to myself through somebody! */
if(bits == 32 && ismyaddr(ep->target) != NULLIF){
if(Rip_trace > 1){
printf("route to self: %s %ld\n",
inet_ntoa(ep->target),ep->metric);
}
return;
}
/* Update metric to reflect link cost */
ep->metric++;
ep->metric = min(ep->metric,RIP_INFINITY);
/* Find existing entry, if any */
rp = rt_blookup(ep->target,bits);
/* Don't touch private routes */
if(rp != NULLROUTE && (rp->flags & RTPRIVATE))
return;
if(rp == NULLROUTE){
if(ep->metric < RIP_INFINITY){
/* New route; add it and trigger an update */
add++;
trigger++;
}
} else if(rp->metric == RIP_INFINITY){
/* Route is in hold-down; ignore this guy */
if(Rip_trace > 0){
printf("ignored (hold-down): %s %lu\n",
inet_ntoa(ep->target),ep->metric);
}
} else if(rp->gateway == gateway && rp->iface == iface){
/* This is the gateway for the entry we already have;
* restart the timer
*/
start_timer(&rp->timer);
if(rp->metric != ep->metric){
/* Metric has changed. Update it and trigger an
* update. If route has become unavailable, start
* the hold-down timeout.
*/
if(Rip_trace){
printf("metric change: %s %lu -> %lu\n",
inet_ntoa(ep->target),rp->metric,ep->metric);
}
if(ep->metric == RIP_INFINITY)
rt_timeout(rp); /* Enter hold-down timeout */
else
rp->metric = ep->metric;
trigger++;
}
} else {
/* Entry is from a different gateway than the current route */
if(ep->metric < rp->metric){
/* Switch to a new gateway */
if(Rip_trace > 0){
printf("metric better: %s %lu\n",
inet_ntoa(ep->target),ep->metric);
}
drop++;
add++;
trigger++;
} else {
/* Metric is no better, stay with current route */
if(Rip_trace > 1){
printf("metric not better: %s %lu\n",
inet_ntoa(ep->target),ep->metric);
}
}
}
if(drop){
/* Switching to a better gateway; delete old entry */
if(Rip_trace){
printf("route drop [%s]/%u",
inet_ntoa(ep->target),bits);
if(rp != NULLROUTE)
printf(" %s %s %lu",rp->iface->name,
inet_ntoa(rp->gateway),rp->metric);
printf("\n");
}
rt_drop(ep->target,bits);
}
if(add){
/* Add a new entry */
interval = RIP_TTL;
for(rl=Rip_list; rl != NULLRL; rl = rl->next){
if(rl->iface == iface){
interval = rl->interval * 4;
break;
}
}
if(Rip_trace > 0){
printf("route add [%s]/%u %s",inet_ntoa(ep->target),
bits,iface->name);
printf(" [%s] %u\n",inet_ntoa(gateway),
(int)ep->metric);
}
rp = rt_add(ep->target,(unsigned) bits,gateway,iface,
(int) ep->metric,interval,0);
}
/* If the route changed, mark it for a triggered update */
if(trigger){
rp->flags |= RTTRIG;
}
}
/* Send a RIP request packet to the specified destination */
int
ripreq(dest,replyport)
int32 dest;
int16 replyport;
{
struct mbuf *bp;
struct socket lsock,fsock;
char *cp;
lsock.address = INADDR_ANY;
lsock.port = replyport;
/* if we were given a valid dest addr, ask it (the routers on that net)
* for a default gateway
*/
if(dest == 0)
return 0;
fsock.address = dest;
fsock.port = RIP_PORT;
/* Send out one RIP Request packet as a broadcast to 'dest' */
if((bp = alloc_mbuf(RIPHEADER + RIPROUTE)) == NULLBUF)
return -1;
cp = putheader(bp->data,RIPCMD_REQUEST,RIPVERSION);
cp = putentry(cp,0,0L,RIP_INFINITY);
bp->cnt = RIPHEADER + RIPROUTE;
send_udp(&lsock, &fsock,0,0,bp,bp->cnt,0,0);
Rip_stat.output++;
return 0;
}
void
pullentry(ep,bpp)
register struct rip_route *ep;
struct mbuf **bpp;
{
ep->addr_fam = pull16(bpp);
(void)pull16(bpp);
ep->target = pull32(bpp);
(void)pull32(bpp);
(void)pull32(bpp);
ep->metric = pull32(bpp);
}
/* Write the header of a RIP packet */
static char *
putheader(cp,command,version)
register char *cp;
char command;
char version;
{
*cp++ = command;
*cp++ = version;
return put16(cp,0);
}
/* Write a single entry into a rip packet */
static char *
putentry(cp,fam,target,metric)
register char *cp;
int16 fam;
int32 target;
int32 metric;
{
cp = put16(cp,fam);
cp = put16(cp,0);
cp = put32(cp,target);
cp = put32(cp,0L);
cp = put32(cp,0L);
return put32(cp,metric);
}
/* Route timeout handler. If route has already been marked for deletion
* then delete it. Otherwise mark for deletion and restart timer.
*/
void
rt_timeout(s)
void *s;
{
register struct route *rp = (struct route *)s;
stop_timer(&rp->timer);
if(rp->metric < RIP_INFINITY){
rp->metric = RIP_INFINITY;
if(dur_timer(&rp->timer) == 0)
set_timer(&rp->timer,RIP_TTL*1000L);
/* wait 2/3 of timeout before garbage collect */
set_timer(&rp->timer,dur_timer(&rp->timer)*2/3);
rp->timer.func = (void *)rt_timeout;
rp->timer.arg = (void *)rp;
start_timer(&rp->timer);
/* Route changed; mark it for triggered update */
rp->flags |= RTTRIG;
rip_trigger();
} else {
rt_drop(rp->target,rp->bits);
}
}