2020-09-30 17:17:25 +02:00

550 lines
11 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
route.c
Abstract:
Simple IP datagram routing functionality
Revision History:
05/22/2000 davidx
Created it.
--*/
#include "precomp.h"
//
// Route table (actually a list)
//
LIST_ENTRY IpRouteList;
IPADDR IpLastRteAddr;
RTE* IpLastRte;
#define INVALIDATELASTRTE() (IpLastRteAddr = 0, IpLastRte = NULL)
//
// Convenience macros for loop through all route table entries
//
#define LOOP_THRU_ROUTE_LIST(_rte) { \
RTE* _next; \
_rte = (RTE*) IpRouteList.Flink; \
for (; _rte != (RTE*) &IpRouteList; _rte = _next) { \
_next = (RTE*) _rte->Flink;
#define END_ROUTE_LIST_LOOP \
} \
}
NTSTATUS
IpInitRouteTable()
/*++
Routine Description:
Initialize the routing table
Arguments:
NONE
Return Value:
Status code
--*/
{
INVALIDATELASTRTE();
InitializeListHead(&IpRouteList);
return NETERR_OK;
}
RTE*
IpFindRTE(
IPADDR dstaddr,
IfInfo* ifp
)
/*++
Routine Description:
Find a route table entry for the specified destination address
Arguments:
dstaddr - Specifies the destination address
ifp - Optional parameter, when present it indicates the caller
is only interested in routes thru that particular interface.
Return Value:
Pointer to the route table entry for the destination
NULL if no route is found
NOTE:
This is a very simple implementation. We're assuming that our
route table will be very small (most likely a handful of entries +
a default gateway).
--*/
{
RTE* rte;
RTE* found;
RUNS_AT_DISPATCH_LEVEL
if (dstaddr == 0) return NULL;
// Fast check
if (dstaddr == IpLastRteAddr && (!ifp || !IpLastRte || ifp == IpLastRte->ifp))
return IpLastRte;
found = NULL;
LOOP_THRU_ROUTE_LIST(rte)
if ((dstaddr & rte->addrmask) == rte->dstaddr &&
(!ifp || ifp == rte->ifp)) {
// Found a matching entry:
// since the route list is sorted by mask length,
// we can stop searching right away
found = rte;
break;
}
END_ROUTE_LIST_LOOP
// Update the last route lookup information
IpLastRteAddr = dstaddr;
return (IpLastRte = found);
}
NTSTATUS
IpAddRTE(
IPADDR dstaddr,
IPADDR addrmask,
IPADDR nexthop,
IfInfo* ifp,
INT flags,
INT metric
)
/*++
Routine Description:
Add an entry to the route table
Arguments:
dstaddr, addrmask - Specifies the destination address and mask
nexthop - Specifies the next hop gateway address
ifp - Points to the output interface for the route
flags - Route flags
metric - Route metrics
Return Value:
Status code
--*/
{
RTE* newrte;
RTE* rte;
KIRQL irql;
// Validate input parameters:
// - address mask must be of the form 111...000
// be careful about the byte order
// - next hop address cannot be a broadcast address
if (!XnetIsValidSubnetMask(addrmask) ||
nexthop == 0 ||
IfBcastAddr(ifp, nexthop) && !(flags & RTEFLAG_BCAST)) {
WARNING_("Bad route to %s:", IPADDRSTR(dstaddr));
WARNING_(" mask = %s", IPADDRSTR(addrmask));
WARNING_(" nexthop = %s", IPADDRSTR(nexthop));
return NETERR_PARAM;
}
// Make sure loopback address never goes outside
ASSERT(!IS_LOOPBACK_IPADDR(dstaddr) ||
IfLoopback(ifp) &&
addrmask == CLASSA_NETMASK &&
nexthop == IPADDR_LOOPBACK);
dstaddr &= addrmask;
irql = RaiseToDpc();
// Find out if the specified route is already in the table
LOOP_THRU_ROUTE_LIST(rte)
if (rte->dstaddr == dstaddr &&
rte->addrmask == addrmask &&
(addrmask != 0 || rte->nexthop == nexthop)) {
// Update the existing route information
newrte = rte;
RemoveEntryList(&rte->links)
goto insert_route;
}
END_ROUTE_LIST_LOOP
// Add a new route table entry
newrte = XnetAlloc0(sizeof(RTE), PTAG_RTE);
if (!newrte) {
LowerFromDpc(irql);
return NETERR_MEMORY;
}
newrte->refcount = 1;
insert_route:
newrte->flags = flags;
newrte->metric = metric;
newrte->dstaddr = dstaddr;
newrte->addrmask = addrmask;
newrte->nexthop = nexthop;
CACHE_IFP_REFERENCE(newrte->ifp, ifp);
INVALIDATELASTRTE();
// Insert the new route into the table
// sorted by the mask length and route metric
LOOP_THRU_ROUTE_LIST(rte)
if (addrmask > rte->addrmask ||
addrmask == rte->addrmask && metric < rte->metric) {
break;
}
END_ROUTE_LIST_LOOP
newrte->Flink = (LIST_ENTRY*) rte;
newrte->Blink = rte->Blink;
rte->Blink->Flink = (LIST_ENTRY*) newrte;
rte->Blink = (LIST_ENTRY*) newrte;
LowerFromDpc(irql);
return NETERR_OK;
}
PRIVATE VOID
IpDeleteRouteEntry(
RTE* rte
)
/*++
Routine Description:
Delete the specified route entry
Arguments:
rte - Points to the route entry to be deleted
Return Value:
NONE
--*/
{
RUNS_AT_DISPATCH_LEVEL
INVALIDATELASTRTE();
RemoveEntryList(&rte->links)
if (RteDecRef(rte) == 0) {
// no more reference to this entry, we can delete it
XnetFree(rte);
} else {
// there is still cached reference to this entry,
// mark it as orphaned and it will be deleted
// when the last reference goes away.
rte->flags |= RTEFLAG_ORPHAN;
rte->ifp = NULL;
}
}
VOID
IpRemoveInterfaceRTE(
IfInfo* ifp,
BOOL deleteBcastRte
)
/*++
Routine Description:
Remove all routes going through the specified interface
Arguments:
ifp - Points to the interface structure
if NULL, then all routes are removed
deleteBcastRte - Whether to delete the broadcast routes
Return Value:
NONE
--*/
{
RTE* rte;
RUNS_AT_DISPATCH_LEVEL
if (IsListNull(&IpRouteList)) return;
// go through the entire route table and look for
// entries whose interface matches the specified one
LOOP_THRU_ROUTE_LIST(rte)
if (ifp && rte->ifp != ifp) continue;
if (!deleteBcastRte && IsRteBcast(rte)) continue;
IpDeleteRouteEntry(rte);
END_ROUTE_LIST_LOOP
}
VOID
IpRemoveRTE(
IPADDR dstaddr,
IPADDR addrmask,
IPADDR nexthop
)
/*++
Routine Description:
Delete a route going to the specified destination
and through the specified gateway
Arguments:
dstaddr, addrmask - Specifies the route destination
nexthop - Specifies the next hop gateway for the route
Return Value:
NONE
--*/
{
RTE* rte;
KIRQL irql = RaiseToDpc();
LOOP_THRU_ROUTE_LIST(rte)
if (rte->dstaddr == dstaddr &&
rte->addrmask == addrmask &&
rte->nexthop == nexthop) {
IpDeleteRouteEntry(rte);
break;
}
END_ROUTE_LIST_LOOP
LowerFromDpc(irql);
}
NTSTATUS
IpSetBroadcastInterface(
IfInfo* ifp
)
/*++
Routine Description:
Designate an interface for transmitting
outgoing broadcast datagrams
Arguments:
ifp - Specifies the new broadcast interface
Return Value:
Status code
--*/
{
// Remove the route entry for the previous broadcast interface
IpRemoveRTE(IPADDR_BCAST, 0xffffffff, IPADDR_BCAST);
if (ifp == NULL) return NETERR_OK;
// Create an entry for the new broadcast interface
return IpAddRTE(IPADDR_BCAST,
0xffffffff,
IPADDR_BCAST,
ifp,
RTEFLAG_BCAST|RTEFLAG_LOCAL,
DEFAULT_RTE_METRIC);
}
NTSTATUS
IpSetMulticastInterface(
IfInfo* ifp
)
/*++
Routine Description:
Designate an interface for transmitting
outgoing broadcast datagrams
Arguments:
ifp - Specifies the new broadcast interface
Return Value:
Status code
--*/
{
// Remove the route entry for the previous broadcast interface
IpRemoveRTE(CLASSD_NETID, CLASSD_NETMASK, CLASSD_NETID);
if (ifp == NULL) return NETERR_OK;
// Create an entry for the new broadcast interface
return IpAddRTE(CLASSD_NETID,
CLASSD_NETMASK,
CLASSD_NETID,
ifp,
RTEFLAG_MCAST|RTEFLAG_LOCAL,
DEFAULT_RTE_METRIC);
}
VOID
IpRedirectHostRoute(
IfInfo* ifp,
IPADDR dstaddr,
IPADDR oldgwaddr,
IPADDR newgwaddr
)
/*++
Routine Description:
Redirect the route to a destination host
Arguments:
ifp - Specifies the interface that received the redirect message
dstaddr - Destination host address
oldgwaddr - Specifies the current gateway to the destination host
newgwaddr - Specifies the new gateway address
Return Value:
NONE
--*/
{
RTE* rte;
RUNS_AT_DISPATCH_LEVEL
// The redirected destination must be a unicast address
if (!XnetIsValidUnicastAddr(dstaddr)) return;
// Validate the new gateway address:
// must be a unicast address on the same subnet
// through which the redirect message arrived
if (!XnetIsValidUnicastAddr(newgwaddr) ||
ifp->subnetAddr != (newgwaddr & ifp->addrmask))
return;
// Find the current route to the destination
// and see if the sender of the redirect message
// is the current next-hop gateway
rte = IpFindRTE(dstaddr, NULL);
if (!rte || rte->nexthop != oldgwaddr) return;
// Add a new host route
IpAddRTE(dstaddr,
0xffffffff,
newgwaddr,
ifp,
RTEFLAG_HOST,
DEFAULT_RTE_METRIC);
}
#if DBG
VOID
IpDumpRouteTable()
/*++
Routine Description:
Dump the content of the route table in the debugger
Arguments:
NONE
Return Value:
NONE
--*/
{
KIRQL irql;
RTE* rte;
DbgPrint("\n*** Route table:\n");
if (IsListNull(&IpRouteList)) return;
irql = RaiseToDpc();
LOOP_THRU_ROUTE_LIST(rte)
DbgPrint("%s/", IPADDRSTR(rte->dstaddr));
DbgPrint("%s/", IPADDRSTR(rte->addrmask));
DbgPrint("%s ", IPADDRSTR(rte->nexthop));
DbgPrint("%02x %d %d %s\n",
rte->flags,
rte->metric,
rte->refcount,
rte->ifp->ifname);
END_ROUTE_LIST_LOOP
LowerFromDpc(irql);
}
#endif // DBG