/*******************************************************************/
/*	      Copyright(c)  1993 Microsoft Corporation		   */
/*******************************************************************/

//***
//
// Filename:	route.c
//
// Description: route packet routine
//
// Author:	Stefan Solomon (stefans)    October 11, 1993.
//
// Revision History:
//
//***

#include    "rtdefs.h"

//***
//
// Function:	RoutePacket
//
// Descr:	Routes this packet
//
// Params:	Packet
//
// Returns:	none
//
//***

VOID
RoutePacket(PPACKET_TAG     pktp)
{
    PUCHAR		hdrp;	 // points to the IPX packet header
    PUCHAR		destnet; // points to destination network
    UINT		segment; // routing table segment
    KIRQL		oldirql;
    PIPX_ROUTE_ENTRY	tabrtep;
    PNICCB		dstniccbp, srcniccbp;

    // check if we know about the destination network
    hdrp = pktp->DataBufferp;
    destnet = hdrp + IPXH_DESTNET;

    segment = IpxGetSegment(destnet);

    KeAcquireSpinLock(&SegmentLocksTable[segment], &oldirql);

    if((tabrtep = IpxGetRoute(segment, destnet)) == NULL) {

	// no such route
	KeReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
	RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: route not found!\n"));
	FreeRcvPkt(pktp);
	return;
    }

    // check if the destination route is reachable through another nic than
    // the nic we received the packet from.
    if(pktp->PacketOwnerNicCbp->NicId == tabrtep->NicId) {

	// we can't send the packet back where it came from => discard it
	KeReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
	FreeRcvPkt(pktp);
	return;
    }

    // if either the src or the dst of the packet is a WAN nic, there are
    // a series of checks to perform

    // get the destination nic for this packet
    dstniccbp = NicCbPtrTab[tabrtep->NicId];

    // get the source nic for this packet
    srcniccbp = NicCbPtrTab[pktp->PacketOwnerNicCbp->NicId];

    // 1. check if one of the two nics is WAN and the other is a LAN nic disabled
    //	  for WAN traffic
    if( ((dstniccbp->DeviceType == NdisMediumWan) &&
	 (srcniccbp->DeviceType != NdisMediumWan) &&
	 (srcniccbp->WanRoutingDisabled))	  ||

	((dstniccbp->DeviceType != NdisMediumWan) &&
	 (srcniccbp->DeviceType == NdisMediumWan) &&
	 (dstniccbp->WanRoutingDisabled)) ) {

	RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: discard pkt because WAN traffic disabled for this LAN\n"));
	KeReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
	FreeRcvPkt(pktp);
	return;
    }

    // 2. check if one of the two nics is WAN and has a client role and
    //	  LAN-WAN-LAN traffic is disabled
    if(!LanWanLan) {

	if( ((dstniccbp->DeviceType == NdisMediumWan) &&
	     (dstniccbp->WanConnectionClient)) ||

	    ((srcniccbp->DeviceType == NdisMediumWan) &&
	     (srcniccbp->WanConnectionClient)) ) {

	    RtPrint(DBG_ROUTE, ("IpxRouter: RoutePacket: discard pkt because WAN has client role\n"));
	    KeReleaseSpinLock(&SegmentLocksTable[segment], oldirql);
	    FreeRcvPkt(pktp);
	    return;
	}
    }

    // general send preparation
    pktp->RemoteAddress.NicId = tabrtep->NicId;

    //*** check if the network is directly connected ***

    if(tabrtep->Flags & IPX_ROUTER_LOCAL_NET) {

	// prepare the packet for a direct send to the destination node
	memcpy(&pktp->RemoteAddress.MacAddress,
	       hdrp + IPXH_DESTNODE,
	       IPX_NODE_LEN);
    }
    else
    {
	// prepare the packet to be sent to the next router in the path to
	// the destination node
	memcpy(&pktp->RemoteAddress.MacAddress,
	       tabrtep->NextRouter,
	       IPX_NODE_LEN);
    }

    KeReleaseSpinLock(&SegmentLocksTable[segment], oldirql);

    // increment the nr of hops in the packet
    *(hdrp + IPXH_XPORTCTL) += 1;

    // queue the packet at the sending nic and send it
    SendPacket(pktp);

    return;
}
