0
mirror of https://github.com/Indemsys/Frequency_Inverter.git synced 2026-05-05 13:17:53 +00:00
Files
2022-01-04 12:22:53 +02:00

1296 lines
39 KiB
C

/**HEADER********************************************************************
*
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
***************************************************************************
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: rip.c$
* $Version : 3.8.12.0$
* $Date : Sep-19-2012$
*
* Comments:
*
* This file contains the implementation of RIPv1 and v2,
* the Routing Information Protocol. For more details,
* refer to RFC2453.
* KNOWN BUGS:
* - just after the shutdown advertise all the routes with an max (no infinite)
* metric aka 15. thus the other routers doesnt have to wait for the end of
* the timeout. question: how do i know when the board shutdown?
* it seems impossible :( perhaps with a network call... snmp set
* - how to do the router administration ? etc.. snmp set? and the security ?
* - how to advertize particular route such as default. currently i dont
* adertize it.
* - passwd and md5 authentification extensions are not handled.
* - i missed something in the nexthop field and the reverse poison, the
* metric must be advertized with a infinite metric on the interface
* from which it has been learned.
* ok BUT only if this route has been learned with RIP. not if it is a external
* route/no static route (because there is not source interface).
* and perhaps other cases... like on demand circuit.
* > > in which case the nexthop field is usefull ?
* >
* > One obvious case -- exporting from a foreign routing protocol (like
* > OSPF or BGP).
* >
* > Another very useful case for the next hop field in RIPv2 is to allow
* > for better handling of routes on demand circuits. In the Annex, we
* > put the next hop into the advertisement even if it wasn't directly
* > reachable on that same subnet. I think Gary wrote an applicability
* > statement concerning this use afterwards.
* >
* > Suppose you have nodes A and B which can both dial into C on demand.
* > A dials in and learns a number of routes from C and advertises them on
* > a local network that B sees. A hangs up from C and then B dials in.
* >
* > When B learns routes from C, it advertises C as the next hop. When A
* > sees this, it immediately sets its local copies of those routes to
* > infinity and purges them from the world by doing a triggered
* > advertisement. That way, other hosts don't have to wait for A's old
* > routes to age out and B's to become preferred.
* TODO:
* - the icmp/redirect route must be modified to.
* - handle the case of removing the interface or the device.
* - must handle several route for a given destination with the same metric.
* - check response validity.
* - if a route metric reaches the max, the route is down.
* - the debian routed send the packet to the network broadcast and not
* the limited broadcast, what is the proper behaviour ? gated routed do
* the same. DONE i have decided it was the good one.
* - if an static route is removed, start the deletion process. dont simply
* remove it. thus the route is advertized with an infinite metric.
* - implement the POLL unofficial/gated command. i.e. a response
* without splithorizon/reverse poison.
* - DONE send/received multicast for ripv2 (and ospf) (change igmp to do
* it without socket. directly in ucb) seb said ok
*
*END************************************************************************/
/* local include */
#include <rtcs.h>
#include "rtcs_prv.h"
#include "tcpip.h"
#include "ip_prv.h"
#include "udp_prv.h"
#include "rip.h"
#include "route.h"
#if RTCSCFG_ENABLE_RIP && RTCSCFG_ENABLE_IP4
#define RND_RANGE(min,max) ((min) + RTCS_rand()%(((max)-(min))?((max)-(min)):1))
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#define RIP_PKT_MAXLEN 500 /* find the right number */
#define RIP_AF_INET 2 /* MUST be 2. rtcs AF_INET is 1! */
#define DEFAULT_ROUTE_TAG 0
#define NETWORK_COST 1 /* must be at least 1 */
#define SPLIT_HORIZON_WITHOUT_POISONNED_REVERSE 0
#define ADV_VERSION RIP_V2
static void RIP_trig_upd(void);
static boolean RIP_expire_periodic(TCPIP_EVENT_PTR);
static uint_32 RIP_send_req(void);
void RIP_service(RTCSPCB_PTR, UCB_STRUCT_PTR);
IP_ROUTE_FN RIP_routefn = {
NULL,
RIP_new_bindif,
RIP_init_static_rt,
RIP_remove_rt
};
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_init
* Returned Values : uint_32
* Comments : Initialize the RIP layer.
*
*END*-----------------------------------------------------------------*/
uint_32 RIP_init (void)
{ /* Body */
RIP_CFG_STRUCT_PTR cfg = RTCS_getcfg(RIP);
uint_32 err;
if (RTCS_getcfg(RIP)) return RTCS_OK;
/* allocate the memory */
cfg = RTCS_mem_alloc_zero(sizeof(*cfg));
if (!cfg) return RTCSERR_OUT_OF_MEMORY;
/* bind the udp port */
err = UDP_openbind_internal(IPPORT_RIP, RIP_service, &cfg->UCB);
if (err){
_mem_free(cfg);
return err;
}
RTCS_setcfg(RIP, cfg);
ROUTE_register(&RIP_routefn);
/* Start the retransmission timer to start sending immediately */
cfg->TIMER_PERIODIC.TIME = 0;
cfg->TIMER_PERIODIC.EVENT = RIP_expire_periodic;
cfg->TIMER_PERIODIC.PRIVATE = NULL;
TCPIP_Event_add(&cfg->TIMER_PERIODIC);
/* probe all the interfaces now to build the route table faster */
err = RIP_send_req();
return RTCS_OK;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_cpu_metric
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_cpu_metric(
RTCSPCB_PTR pcb, /* [IN] incoming packet */
uint_32 oldmetric
)
{ /* Body */
uint_32 metric = NETWORK_COST + oldmetric;
/* if a cost per interface is required, do that here. But before that,
** think about the unit problem between the routing protocols.
*/
return min(metric, RIP_MAX_METRIC);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_build_header
* Returned Values : uint_32
* Comments :
*
*END*-----------------------------------------------------------------*/
static void RIP_build_header(
uchar_ptr buf, /* [OUT] the rip header */
uint_8 cmd, /* [IN] the commande */
uint_8 version /* [IN] the version */
)
{ /* Body */
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)buf;
_mem_zero(buf, sizeof(RIP_HEADER));
htonc(hd->COMMAND, cmd);
htonc(hd->VERSION, version);
htons(hd->MBZ, 0);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_build_rte
* Returned Values : uint_32
* Comments :
*
*END*-----------------------------------------------------------------*/
static void RIP_build_rte(
uchar_ptr buf, /* [OUT] the route entry */
uint_16 family,
uint_16 rt_tag,
_ip_address net_addr,
_ip_address net_mask,
_ip_address next_hop,
uint_32 metric,
uint_32 rip_vers
)
{ /* Body */
RIP_ENTRY_PTR rte = (RIP_ENTRY_PTR)buf;
if (rip_vers == RIP_V2){
htons(rte->RT_TAG, rt_tag);
htonl(rte->NETMASK, net_mask);
htonl(rte->NEXTHOP, next_hop);
}else{
_mem_zero(buf, sizeof(RIP_ENTRY));
}
htons(rte->FAMILY, family);
htonl(rte->NETADDR, net_addr);
htonl(rte->METRIC, metric);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_send_pkt
* Returned Values :
* Comments :
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_send_pkt(
IP_IF_PTR ipif, /* [IN] the outgoing interface */
_ip_address ipdst, /* [IN] the destination ip */
uint_16 portdst,/* [IN] the destination port */
uchar_ptr data, /* [IN] the data to send */
uint_32 len /* [IN] the length of data to send */
)
{ /* Body */
RTCSPCB_PTR pcb;
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)data;
uint_32 err;
_ip_address ipsrc = IP_get_ipif_addr(ipif);
if (ipsrc == INADDR_ANY) {
_mem_free(data);
return RTCS_OK;
} /* Endif */
/* Allocate a PCB */
pcb = RTCSPCB_alloc_send();
if (pcb == NULL) {
_mem_free(data);
return RTCSERR_PCB_ALLOC;
} /* Endif */
//RTCSLOG_PCB_ALLOC(pcb);
/* add my data in the pcb */
err = RTCSPCB_append_fragment_autofree(pcb, len, data);
if (err) {
_mem_free(data);
RTCSLOG_PCB_FREE(pcb, err);
RTCSPCB_free(pcb);
return err;
} /* Endif */
RTCSLOG_PCB_WRITE(pcb, RTCS_LOGCTRL_PORT(IPPORT_RIP), ntohc(hd->VERSION));
/* dont advertize in limited broadcast but in ip broadcast */
if (ipdst == INADDR_BROADCAST){
_ip_address netmask;
ipdst = IP_get_ipif_addr(ipif); /* Get IP address of interface */
IP_get_netmask(ipif, ipdst, &netmask); /* Get netmask */
ipdst = ipdst | ~netmask;
} /* Endif */
/* send it */
/* use a flag to prevent the broadcast/multicast packet from
** looping back to us.
*/
return UDP_send_internal(ripcfg->UCB, ipsrc, ipdst, portdst, pcb, RTCS_MSG_NOLOOP);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_is_valid_rte
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_is_valid_rte(
RIP_ENTRY_PTR rte
)
{ /* Body */
uint_32 metric = ntohl(rte->METRIC);
_ip_address netaddr = ntohl(rte->NETADDR);
if (ntohs(rte->FAMILY) != RIP_AF_INET) return 0;
if (metric > RIP_MAX_METRIC || metric < RIP_MIN_METRIC) return 0;
if (IN_MULTICAST(netaddr) || IN_EXPERIMENTAL(netaddr)) return 0;
if (IN_LOOPBACK(netaddr)) return 0;
{
_ip_address nexthop = ntohl(rte->NEXTHOP);
if (nexthop!= 0) {
if (IP_is_local(NULL, nexthop)) {
return 0;
}
}
}
return 1;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_is_valid_resp
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_is_valid_resp(
RTCSPCB_PTR pcb /* [IN/OUT] incoming packet */
)
{ /* Body */
uint_32 ipsrc = IP_source(pcb);
uint_16 srcport = UDP_source(pcb);
/*
** check the source port is IPPORT_ROUTE, just a weak security.
** On BSD-like network stack, accessing to the ports < 1024
** requires privileges.
*/
if (srcport != IPPORT_RIP) return FALSE;
/*
** if the ipsrc is NOT on a directly connected network which
** received the packet, it is not valid
*/
if (IP_is_direct(pcb->IFSRC,ipsrc) == FALSE) return FALSE;
/*
** If this packet is one of mine, it is not valid.
** It can happen in case of more than one interfaces are
** directly connected to the same network.
*/
if (IP_is_local(NULL,ipsrc) == TRUE) return FALSE;
return 1;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_send_resp_ipif
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
struct RIP_send_resp_ipif_struct {
IP_IF_PTR ipif;
_ip_address ipdest;
uint_32 rip_vers;
uint_32 onlyChangeF;
uchar_ptr pkt;
uchar_ptr curs;
uint_16 portdst;
};
static boolean RIP_send_resp_ipif_test
(
_ip_address node_ip,
_ip_address node_mask,
pointer node_data,
pointer data
)
{ /* Body */
struct RIP_send_resp_ipif_struct _PTR_ testdata = data;
IP_ROUTE_PTR route = node_data;
IP_ROUTE_INDIRECT_PTR gate, end;
_ip_address nexthop, iplist[2];
uint_32 metric, rtype, vcount;
boolean complete = FALSE;
pointer rstart[3];
/* If this node has no entries, return */
if (!route) {
return FALSE;
} /* Endif */
rstart[0] = route->DIRECT;
rstart[1] = route->VIRTUAL;
rstart[2] = route->INDIRECT;
/*
** If there are no connected networks, return. If the node_mask is other than
** 255.255.255.255 there is a connected network. There can be a virutal
** network even when all the netmask bits are set if the node_ip is different
** than the VIRTUAL ADDRESS (like in the case of PPP)
*/
if (!~node_mask) {
if (!(route->VIRTUAL && node_ip != route->VIRTUAL->ADDRESS)) {
return FALSE;
} /* Endif */
/* Only check the VIRTUAL routes */
rstart[0] = NULL;
rstart[2] = NULL;
} /* Endif */
for (rtype = 0, gate = NULL; rtype <= 2 && !gate; rtype++) {
gate = rstart[rtype];
} /* Endfor */
if (!gate) {
return FALSE;
} /* Endif */
end = gate;
iplist[1] = node_ip;
do {
do {
vcount = 2;
do {
/* Don't gather information about tunnels */
if (gate == rstart[1] && !route->VIRTUAL->ADDRESS) {
break;
} /* Endif */
if (vcount == 1) {
iplist[0] = route->VIRTUAL->ADDRESS;
} /* Endif */
/* allocate the packet memory */
if (!testdata->pkt) {
testdata->pkt = testdata->curs = RTCS_mem_alloc_system(RIP_MAX_PKTLEN);
if (!testdata->pkt) return RTCSERR_OUT_OF_MEMORY;
/* build the header */
RIP_build_header(testdata->curs, RIPCMD_RESPONSE, testdata->rip_vers);
testdata->curs += sizeof(RIP_HEADER);
} /* Endif */
if (testdata->onlyChangeF && !gate->RIP.CHANGED_F) {
break;
} /* Endif */
/* dont advertize the icmp redirect route */
if (gate->FLAGS & RTF_REDIRECT) {
break;
} /* Endif */
metric = gate->RIP.METRIC;
#if SPLIT_HORIZON_WITHOUT_POISONNED_REVERSE
/* split horizon. rfc2453.3.4.3.
** DONT advertize a route on the interface on
** which you received it.
*/
if (gate->RIP.IPIFSRC == testdata->ipif) {
break;
} /* Endif */
#else
/* split horizon with poisoned reverse. rfc2453.3.4.3
** DO advertize a route on the interface on
** which you received it BUT with a infinit metric.
*/
if (gate->RIP.IPIFSRC == testdata->ipif) {
metric = RIP_MAX_METRIC;
} /* Endif */
#endif
/* the outgoing nexthop. rfc2453.4.4. */
nexthop = 0;
if (IP_is_direct(testdata->ipif, gate->GATEWAY)) {
if (end == route->INDIRECT) {
nexthop = gate->GATEWAY;
} /* Endif */
} /* Endif */
/* build the route entry */
RIP_build_rte(testdata->curs, RIP_AF_INET, gate->RIP.RT_TAG
, iplist[vcount - 1], node_mask
, nexthop, metric, testdata->rip_vers);
/* check if it is valid before sending it */
if (!RIP_is_valid_rte((RIP_ENTRY_PTR)testdata->curs)) {
break;
} /* Endif */
testdata->curs += sizeof(RIP_ENTRY);
/* if the packet is full, send it and come back later */
if (testdata->curs-testdata->pkt >= sizeof(RIP_HEADER)+RIP_MAX_PKTLEN) {
RIP_send_pkt(testdata->ipif, testdata->ipdest, testdata->portdst,
testdata->pkt, testdata->curs-testdata->pkt);
/* pkt is freed, reset pointer to NULL */
testdata->pkt = NULL;
} /* Endif */
vcount--;
} while (end && end == (pointer)route->VIRTUAL && vcount);
gate = gate->NEXT;
} while (gate != end);
for (gate = NULL; rtype <= 2 && !gate; rtype++) {
gate = rstart[rtype];
} /* Endfor */
end = gate;
} while(gate);
return FALSE;
} /* Endbody */
static uint_32 RIP_send_resp_ipif (
IP_IF_PTR ipif, /* [IN] the destination interface */
_ip_address ipdest, /* [IN] the destination ip address */
uint_16 portdst, /* [IN] the destination port */
uint_32 rip_vers,/* [IN] the rip version */
uint_32 onlyChangeF /* [IN] TRUE if only the changed routes are sent */
)
{ /* Body */
IP_CFG_STRUCT_PTR IP_cfg_ptr = RTCS_getcfg(IP);
struct RIP_send_resp_ipif_struct testdata;
testdata.ipif = ipif;
testdata.ipdest = ipdest;
testdata.portdst = portdst;
testdata.onlyChangeF = onlyChangeF;
testdata.rip_vers = rip_vers;
testdata.pkt = NULL;
testdata.curs = NULL;
IPRADIX_walk(&IP_cfg_ptr->ROUTE_ROOT.NODE, RIP_send_resp_ipif_test, &testdata);
/* if the packet is empty, dont send it */
if (testdata.curs-testdata.pkt == sizeof(RIP_HEADER)){
_mem_free(testdata.pkt);
} else {
/* send the packet */
RIP_send_pkt(ipif, ipdest, portdst, testdata.pkt,
testdata.curs - testdata.pkt);
} /* Endif */
return RTCS_OK;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_send_resp
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
struct RIP_send_resp_struct {
IP_IF_PTR localhost;
uint_32 onlyChangeF;
_ip_address ipdest;
uint_32 rip_vers;
uint_32 nb_try;
uint_32 nb_fail;
};
static boolean RIP_send_resp_test
(
_ip_address node_ip,
_ip_address node_mask,
pointer node_data,
pointer data
)
{ /* Body */
IP_ROUTE_PTR route = node_data;
struct RIP_send_resp_struct _PTR_ testdata = data;
uint_32 error;
if (route && route->DIRECT) {
/* Don't advertise on local interface */
if (route->DIRECT->DESTIF != testdata->localhost) {
testdata->nb_try++;
error = RIP_send_resp_ipif(route->DIRECT->DESTIF, testdata->ipdest,
IPPORT_RIP, testdata->rip_vers, testdata->onlyChangeF);
if (error != RTCS_OK) {
testdata->nb_fail++;
} /* Endif */
} /* Endif */
} /* Endif */
if (route && route->VIRTUAL && route->VIRTUAL->ADDRESS) {
/* Don't advertise on local interface */
if (route->VIRTUAL->DESTIF != testdata->localhost) {
testdata->nb_try++;
error = RIP_send_resp_ipif(route->VIRTUAL->DESTIF, testdata->ipdest,
IPPORT_RIP, testdata->rip_vers, testdata->onlyChangeF);
if (error != RTCS_OK) {
testdata->nb_fail++;
} /* Endif */
} /* Endif */
} /* Endif */
return FALSE;
} /* Endbody */
static boolean RIP_send_resp_test2
(
_ip_address node_ip,
_ip_address node_mask,
pointer node_data,
pointer data
)
{ /* Body */
IP_ROUTE_PTR route = node_data;
IP_ROUTE_INDIRECT_PTR indirect;
pointer p[3];
uint_32 i;
if (!route) {
return FALSE;
} /* Endif */
p[0] = route->DIRECT;
p[1] = route->INDIRECT;
p[2] = route->VIRTUAL;
for (i = 0; i <= 2; i++) {
if (!p[i]) {
continue;
} /* Endif */
indirect = p[i];
do {
indirect->RIP.CHANGED_F = FALSE;
indirect = indirect->NEXT;
} while(indirect != p[i]);
} /* Endfor */
return FALSE;
} /* Endbody */
static uint_32 RIP_send_resp(
uint_32 rip_vers, /* [IN] the rip version */
uint_32 onlyChangeF /* [IN] TRUE if only the changed routes are sent */
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
IP_CFG_STRUCT_PTR IP_cfg_ptr = RTCS_getcfg(IP);
struct RIP_send_resp_struct testdata;
testdata.nb_try = 0;
testdata.nb_fail = 0;
testdata.localhost = RTCS_IF_LOCALHOST_PRV;
testdata.rip_vers = rip_vers;
testdata.onlyChangeF = onlyChangeF;
if (rip_vers == RIP_V1) testdata.ipdest = INADDR_BROADCAST;
else testdata.ipdest = INADDR_RIP_GROUP;
IPRADIX_walk(&IP_cfg_ptr->ROUTE_ROOT.NODE, RIP_send_resp_test, &testdata);
/* test if the routes has been all advertized (nbFail==0 && nbTry > 0)*/
if (testdata.nb_fail) return (uint_32)RTCS_ERROR;
if (testdata.nb_try == 0) return RTCS_OK;
/* clear the flags only if required */
if (ripcfg->RT_CHANGED_F == FALSE) return RTCS_OK;
/* update the flag */
ripcfg->RT_CHANGED_F = FALSE;
/* clear the flag in all changed route entries */
IPRADIX_walk(&IP_cfg_ptr->ROUTE_ROOT.NODE, RIP_send_resp_test2, NULL);
return RTCS_OK;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_process_inreq
* Returned Values : void
* Comments : check only the header
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_process_inreq(
RTCSPCB_PTR pcb /* [IN/OUT] incoming packet */
)
{ /* Body */
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)RTCSPCB_DATA(pcb);
RIP_ENTRY_PTR rte = (RIP_ENTRY_PTR)((uchar_ptr)hd
+ sizeof(RIP_HEADER));
uint_32 pktlen = RTCSPCB_SIZE(pcb);
uint_32 err = RTCS_OK;
/* handle the general query. rfc2453.3.9.1 */
if (pktlen == sizeof(RIP_ENTRY) + sizeof(RIP_HEADER)
&& ntohs(rte->FAMILY) == 0
&& ntohl(rte->METRIC) == RIP_MAX_METRIC){
_ip_address ipsrc = IP_source(pcb);
uint_16 srcport = UDP_source(pcb);
uint_8 version = ntohc(hd->VERSION);
return RIP_send_resp_ipif (pcb->IFSRC,ipsrc,srcport,version,0);
}
/* WORK: here handle particular routes request */
return err;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_expire_gc
* Returned Values : boolean
* Comments : rfc2453.3.8
*
*END*-----------------------------------------------------------------*/
static boolean RIP_expire_gc(
TCPIP_EVENT_PTR event /* [IN/OUT] the resend event */
)
{ /* Body */
IP_ROUTE_INDIRECT_PTR gate = event->PRIVATE;
if (!gate->IS_DIRECT) {
ROUTE_remove(gate);
} /* Endif */
return FALSE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_expire_rte
* Returned Values : boolean
* Comments : rfc2453.3.8
*
*END*-----------------------------------------------------------------*/
static boolean RIP_expire_rte(
TCPIP_EVENT_PTR event /* [IN/OUT] the resend event */
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
IP_ROUTE_INDIRECT_PTR gate = event->PRIVATE;
/* update the route entry */
gate->FLAGS &= ~RTF_UP;
gate->RIP.METRIC = RIP_MAX_METRIC;
ripcfg->RT_CHANGED_F = gate->RIP.CHANGED_F = TRUE;
RIP_trig_upd();
/* start the garbage collector timer. */
gate->RIP.TIMEOUT.TIME = RIP_TIMEOUT_GC;
gate->RIP.TIMEOUT.EVENT = RIP_expire_gc;
return TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_init_timer
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static void RIP_init_timer(
IP_ROUTE_INDIRECT_PTR gate,
uint_32 metric
)
{ /* Body */
if (metric < RIP_MAX_METRIC){ /* init the timeout */
gate->RIP.TIMEOUT.TIME = RIP_TIMEOUT_DEL;
gate->RIP.TIMEOUT.EVENT = RIP_expire_rte;
}else{ /* init the garbage collection */
gate->RIP.TIMEOUT.TIME = RIP_TIMEOUT_GC;
gate->RIP.TIMEOUT.EVENT = RIP_expire_gc;
}
gate->RIP.TIMEOUT.PRIVATE = gate;
TCPIP_Event_add(&gate->RIP.TIMEOUT);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_adopt_rt
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static void RIP_adopt_rt(
RTCSPCB_PTR pcb, /* [IN] incoming packet */
IP_ROUTE_INDIRECT_PTR gate,
RIP_ENTRY_PTR rte,
uint_32 metric,
_ip_address _PTR_ network_ptr,
_ip_address _PTR_ netmask_ptr
)
{ /* Body */
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)RTCSPCB_DATA(pcb);
uint_16 rip_vers = ntohc(hd->VERSION);
_ip_address ipsrc = IP_source(pcb);
/* init the common part */
*network_ptr = ntohl(rte->NETADDR);
gate->GATEWAY = ipsrc;
if (rip_vers == RIP_V2){
_ip_address nexthop = ntohl(rte->NEXTHOP);
*netmask_ptr = ntohl(rte->NETMASK);
/* incoming nexthop. rfc2453.4.4 */
if (nexthop && IP_is_direct(pcb->IFSRC, nexthop))
gate->GATEWAY = nexthop;
}else{
*netmask_ptr = IN_DEFAULT_NET(*network_ptr);
}
/* if the metric is >= than the RIP_MAX_METRIC, the ourte is down */
if (metric < RIP_MAX_METRIC) {
gate->FLAGS |= RTF_UP;
} else {
gate->FLAGS &= ~RTF_UP;
} /* Endif */
/* init the RIP part */
gate->RIP.METRIC = metric;
gate->RIP.RT_TAG = ntohs(rte->RT_TAG);
gate->RIP.CHANGED_F = TRUE;
gate->RIP.IPIFSRC = pcb->IFSRC;
RIP_init_timer(gate, gate->RIP.METRIC);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_update_rt
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static void RIP_update_rt(
IP_ROUTE_INDIRECT_PTR gate,
RTCSPCB_PTR pcb, /* [IN] incoming packet */
RIP_ENTRY_PTR rte
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
uint_32 nmetric= RIP_cpu_metric(pcb,ntohl(rte->METRIC));
_ip_address ipsrc = IP_source(pcb);
/* dont modify a static route */
if (gate->FLAGS & RTF_STATIC)
return;
/*
** if the source isnt "authoritative" (i.e. not the current
** gateway) and have a bigger metric, ignore the route.
*/
if (ipsrc != gate->GATEWAY && nmetric > gate->RIP.METRIC)
return;
/*
** if the source isnt "authoritative" (i.e. not the current
** gateway) and have a equal metric and will "soon" timed out,
** update the route entry.
*/
if (ipsrc != gate->GATEWAY && nmetric == gate->RIP.METRIC){
uint_32 timeout = TCPIP_Event_expire(&gate->RIP.TIMEOUT);
/* if the both metrics are infinite, dont update the route. */
if (nmetric >= RIP_MAX_METRIC) return;
if (timeout > RIP_ALMOST_EXPIRED_TIME) return;
}
/* cancel the timeout/gc timer */
TCPIP_Event_cancel(&gate->RIP.TIMEOUT);
/* If the sources and the metrics are equal, just reinit the timer. */
if (ipsrc == gate->GATEWAY && gate->RIP.METRIC == nmetric){
if (nmetric < RIP_MAX_METRIC){
RIP_init_timer(gate, gate->RIP.METRIC);
}
return;
}
/*
** Reinit the route and the timer. ipsrc no longer needed, just used
** to fill all of the function parameters
*/
RIP_adopt_rt(pcb, gate, rte, nmetric, &ipsrc, &ipsrc);
/* flag it as changed */
ripcfg->RT_CHANGED_F = TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_create_rt
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static void RIP_create_rt(
RTCSPCB_PTR pcb, /* [IN] incoming packet */
RIP_ENTRY_PTR rte,
uint_8 rip_vers
)
{ /* Body */
uint_32 nmetric= RIP_cpu_metric(pcb,ntohl(rte->METRIC));
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
IP_CFG_STRUCT_PTR IP_cfg_ptr = RTCS_getcfg(IP);
IP_ROUTE_INDIRECT_PTR gate;
_ip_address network, netmask;
gate = RTCS_part_alloc(IP_cfg_ptr->GATE_PARTID);
if (!gate) return;
_mem_zero(gate, sizeof(*gate));
/* init the route */
RIP_adopt_rt(pcb, gate, rte, nmetric, &network, &netmask);
/* insert it in the table */
ROUTE_insert(gate, network, netmask);
/* flag it as changed */
ripcfg->RT_CHANGED_F = TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_process_inresp
* Returned Values :
* Comments : rfc2453.3.9.2
*
*END*-----------------------------------------------------------------*/
static void RIP_process_inresp(
RTCSPCB_PTR pcb /* [IN/OUT] incoming packet */
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
uchar_ptr pkt = RTCSPCB_DATA(pcb);
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)RTCSPCB_DATA(pcb);
RIP_ENTRY_PTR rte = (RIP_ENTRY_PTR)(pkt + sizeof(RIP_HEADER));
uint_32 pktlen = RTCSPCB_SIZE(pcb);
uint_16 rip_vers = ntohc(hd->VERSION);
IP_ROUTE_INDIRECT_PTR gate;
_ip_address netaddr, netmask;
if (pktlen < sizeof(RIP_HEADER)) {
return;
} /* Endif */
pktlen -= sizeof(RIP_HEADER);
if (RIP_is_valid_resp(pcb) == FALSE){
return;
} /* Endif */
for (; pktlen >= sizeof(RIP_ENTRY); pktlen -= sizeof(RIP_ENTRY), rte++){
if (!RIP_is_valid_rte(rte))
continue;
netaddr = ntohl(rte->NETADDR);
if (rip_vers == RIP_V2) netmask = ntohl(rte->NETMASK);
else netmask = IN_DEFAULT_NET(netaddr);
gate = ROUTE_get(netaddr, netmask);
if (!gate) RIP_create_rt(pcb, rte, rip_vers);
else RIP_update_rt(gate, pcb, rte);
}
/* if this incoming response has changed routes, do a triggered update*/
if (ripcfg->RT_CHANGED_F)
RIP_trig_upd();
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_service
* Returned Values : void
* Comments : Process incoming RIP packets. Called from UDP
*
*END*-----------------------------------------------------------------*/
void RIP_service(
RTCSPCB_PTR pcb, /* [IN/OUT] incoming packet */
UCB_STRUCT_PTR ucb_ptr /* [IN] target UCB */
)
{ /* Body */
RIP_HEADER_PTR hd = (RIP_HEADER_PTR)RTCSPCB_DATA(pcb);
/* check the version number */
if (ntohc(hd->VERSION) != RIP_V1 && ntohc(hd->VERSION) != RIP_V2) {
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
} /* Endif */
RTCSLOG_PCB_READ(pcb, RTCS_LOGCTRL_PORT(IPPORT_RIP), ntohc(hd->VERSION));
switch (ntohc(hd->COMMAND)) {
case RIPCMD_REQUEST:
RIP_process_inreq(pcb);
break;
case RIPCMD_RESPONSE:
RIP_process_inresp(pcb);
break;
default:
break;
} /* Endswitch */
RTCSLOG_PCB_FREE(pcb, RTCS_OK);
RTCSPCB_free(pcb);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_send_req_ipif
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
static uint_32 RIP_send_req_ipif (
IP_IF_PTR ipif
)
{ /* Body */
uchar_ptr pkt;
uint_32 err;
_ip_address ipdest;
/* allocate the packet memory */
pkt = RTCS_mem_alloc_system(RIP_MAX_PKTLEN);
if (!pkt) return RTCSERR_OUT_OF_MEMORY;
/* build the header */
RIP_build_header(pkt, RIPCMD_REQUEST, ADV_VERSION);
/* build handle the general query. rfc2453.3.9.1 */
RIP_build_rte(pkt+sizeof(RIP_HEADER), 0, DEFAULT_ROUTE_TAG, INADDR_ANY
, INADDR_ANY,INADDR_ANY, RIP_MAX_METRIC
, ADV_VERSION);
if (ADV_VERSION == RIP_V1) ipdest = INADDR_BROADCAST;
else ipdest = INADDR_RIP_GROUP;
/* send the packet */
err = RIP_send_pkt(ipif, ipdest, IPPORT_RIP, pkt
, sizeof(RIP_HEADER) + sizeof(RIP_ENTRY));
if (err != RTCS_OK){
return err;
}
return RTCS_OK;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_send_req
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
struct RIP_send_req_struct {
IP_IF_PTR localhost;
};
static boolean RIP_send_req_test
(
_ip_address node_ip,
_ip_address node_mask,
pointer node_data,
pointer data
)
{ /* Body */
IP_ROUTE_PTR route = node_data;
struct RIP_send_req_struct _PTR_ testdata = data;
if (route && route->DIRECT && !~node_mask) {
/* Don't advertise on local interface */
if (route->DIRECT->DESTIF != testdata->localhost) {
RIP_send_req_ipif(route->DIRECT->DESTIF);
} /* Endif */
} else if (route && route->VIRTUAL && route->VIRTUAL->ADDRESS) {
/* Don't advertise on local interface */
if (route->VIRTUAL->DESTIF != testdata->localhost) {
RIP_send_req_ipif(route->VIRTUAL->DESTIF);
} /* Endif */
} /* Endif */
return FALSE;
} /* Endbody */
static uint_32 RIP_send_req(
void
)
{ /* Body */
IP_CFG_STRUCT_PTR IP_cfg_ptr = RTCS_getcfg(IP);
struct RIP_send_req_struct testdata;
testdata.localhost = RTCS_IF_LOCALHOST_PRV;
IPRADIX_walk(&IP_cfg_ptr->ROUTE_ROOT.NODE, RIP_send_req_test, &testdata);
return RTCS_OK;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_expire_periodic
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
static boolean RIP_expire_periodic(
TCPIP_EVENT_PTR event /* [IN/OUT] the resend event */
)
{ /* Body */
RIP_send_resp(ADV_VERSION, FALSE);
event->TIME = RND_RANGE(RIP_TIME_MIN_PERIODIC,
RIP_TIME_MAX_PERIODIC);
return TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_expire_trig_upd
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
static boolean RIP_expire_trig_upd(
TCPIP_EVENT_PTR event /* [IN/OUT] the resend event */
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = event->PRIVATE;
/* if no route has been during the timer run, just leave */
if (ripcfg->RT_CHANGED_F == FALSE) {
/* update the internal flag to know if the timer is running or not */
ripcfg->TIMER_TRIG_UPD.PRIVATE = NULL;
return FALSE;
}
/* send the changed routes */
RIP_send_resp(ADV_VERSION, TRUE);
/* restart the timer */
event->TIME = RND_RANGE(RIP_TIME_MIN_TRIG_UPD, RIP_TIME_MAX_TRIG_UPD);
return TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_trig_udp
* Returned Values : boolean
* Comments :
*
*END*-----------------------------------------------------------------*/
static void RIP_trig_upd(
void
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
if (RTCS_getcfg(RIP) == NULL) return;
/* if the timer is currently running, dont send the data */
if (ripcfg->TIMER_TRIG_UPD.PRIVATE)
return;
/* send the changed routes */
RIP_send_resp(ADV_VERSION, TRUE);
/* start the timer */
ripcfg->TIMER_TRIG_UPD.TIME = RND_RANGE(RIP_TIME_MIN_TRIG_UPD,
RIP_TIME_MAX_TRIG_UPD);
ripcfg->TIMER_TRIG_UPD.EVENT = RIP_expire_trig_upd;
ripcfg->TIMER_TRIG_UPD.PRIVATE = ripcfg;
TCPIP_Event_add(&ripcfg->TIMER_TRIG_UPD);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_new_bindif
* Parameters :
* Comments :
* : - can be called before RIP_init
*
*END*-----------------------------------------------------------------*/
void RIP_new_bindif (
IP_ROUTE_DIRECT_PTR bindif /* [IN] the outgoing binded interface */
)
{ /* Body */
RIP_CFG_STRUCT_PTR cfg = RTCS_getcfg(RIP);
IP_CFG_STRUCT_PTR IP_cfg_ptr = RTCS_getcfg(IP);
ip_mreq mreq;
if (!cfg) {
return;
} /* Endif */
/* do not init the routing on the local interface */
if (bindif->NETIF == RTCS_IF_LOCALHOST_PRV) {
return;
} /* Endif */
/* init the multicast group */
mreq.imr_multiaddr.s_addr = INADDR_RIP_GROUP;
mreq.imr_interface.s_addr = bindif->ADDRESS;
IGMP_join_group(&cfg->UCB->MCB_PTR, &mreq, &cfg->UCB->IGMP_LEAVEALL);
if (bindif->NETIF) {
RIP_send_req_ipif(bindif->NETIF);
} else {
RIP_send_req_ipif(bindif->DESTIF);
} /* Endif */
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_init_static_rt
* Returned Values : void
* Comments : - called to init the RIP part of a static route
* created by ipif_add()
* : - can be called before RIP_init
*
*END*-----------------------------------------------------------------*/
void RIP_init_static_rt
(
IP_ROUTE_INDIRECT_PTR gate, /* [IN] the route to init */
uint_16 metric /* [IN] metric of this route. [0,65535] */
)
{ /* Body */
RIP_CFG_STRUCT_PTR ripcfg = RTCS_getcfg(RIP);
_mem_zero(&gate->RIP, sizeof(gate->RIP));
gate->RIP.METRIC = metric / 4096;
/* sanity check */
if (gate->RIP.METRIC < RIP_MIN_METRIC) {
gate->RIP.METRIC = RIP_MIN_METRIC;
} /* Endif */
if (gate->RIP.METRIC > RIP_MAX_METRIC) {
gate->RIP.METRIC = RIP_MAX_METRIC;
} /* Endif */
/* no src interface for a static route */
gate->RIP.IPIFSRC = NULL;
gate->RIP.CHANGED_F = TRUE;
if (ripcfg){
ripcfg->RT_CHANGED_F = TRUE;
RIP_trig_upd();
}
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : RIP_remove_rt
* Returned Values : void
* Comments : - called to close the RIP part of a route
* created by ROUTE_remove()
*
*END*-----------------------------------------------------------------*/
void RIP_remove_rt(IP_ROUTE_INDIRECT_PTR gate)
{ /* Body */
/* cancel the timeout/gc timer */
TCPIP_Event_cancel(&gate->RIP.TIMEOUT);
_mem_zero(&gate->RIP, sizeof(gate->RIP));
} /* Endbody */
#endif
/* EOF */