/*++

Copyright (c) 1998-1999  Microsoft Corporation

Module Name:

    arp.c

Abstract:

    ARP1394 ARP request/response handling code.

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    josephj     03-28-99    Created

Notes:

--*/
#include <precomp.h>

//
// File-specific debugging defaults.
//
#define TM_CURRENT   TM_ARP

// #define ARP_DEFAULT_MAXREC 0xD
#define ARP_DEFAULT_MAXREC 0x8


#define LOGSTATS_SuccessfulArpQueries(_pIF) \
    NdisInterlockedIncrement(&((_pIF)->stats.arpcache.SuccessfulQueries))
#define LOGSTATS_FailedArpQueried(_pIF) \
    NdisInterlockedIncrement(&((_pIF)->stats.arpcache.FailedQueries))
#define LOGSTATS_TotalQueries(_pIF) \
    NdisInterlockedIncrement(&((_pIF)->stats.arpcache.TotalQueries))
#define LOGSTATS_TotalArpResponses(_pIF) \
    NdisInterlockedIncrement(&((_pIF)->stats.arpcache.TotalResponses))

//=========================================================================
//                  L O C A L   P R O T O T Y P E S
//=========================================================================
  
NDIS_STATUS
arpSendArpRequest(
    PARPCB_REMOTE_IP pRemoteIp,
    PRM_STACK_RECORD pSR
    );

VOID
arpProcessArpRequest(
    PARP1394_INTERFACE          pIF,    // NOLOCKIN NOLOCKOUT
    PIP1394_ARP_PKT_INFO    pPktInfo,
    PRM_STACK_RECORD            pSR
    );

VOID
arpProcessArpResponse(
    PARP1394_INTERFACE          pIF,    // NOLOCKIN NOLOCKOUT
    PIP1394_ARP_PKT_INFO    pPktInfo,
    PRM_STACK_RECORD            pSR
    );

VOID
arpTryAbortResolutionTask(
    PARPCB_REMOTE_IP pRemoteIp,
    PRM_STACK_RECORD pSR
    );

NDIS_STATUS
arpParseArpPkt(
    IN   PIP1394_ARP_PKT  pArpPkt,
    IN   UINT                       cbBufferSize,
    OUT  PIP1394_ARP_PKT_INFO   pPktInfo
    )
/*++
Routine Description:

    Parse the contents of IP/1394 ARP packet data starting at
    pArpPkt. Place the results into pPktInfo.

Arguments:

    pArpPkt     - Contains the unaligned contents of an ip/1394 ARP Pkt.
    pPktInfo    - Unitialized structure to be filled with the parsed contents of the
                  pkt.

Return Value:

    NDIS_STATUS_FAILURE if the parse failed (typically because of invalid pkt
                        contents.)
    NDIS_STATUS_SUCCESS on successful parsing.
    
--*/
{
    ENTER("arpParseArpPkt", 0x20098dc0)
    NDIS_STATUS                 Status;
    DBGSTMT(CHAR *szError   = "General failure";)

    Status  = NDIS_STATUS_FAILURE;

    do
    {
        UINT SenderMaxRec;
        UINT OpCode;

        // Verify length.
        //
        if (cbBufferSize < sizeof(*pArpPkt))
        {
            DBGSTMT(szError = "pkt size too small";)
            break;
        }

        // Verify constant fields.
        //

    #if 0 // Reserved is no "NodeId, which contains the nodeid of the sending
          // node, if known (0 otherwise).
        if (pArpPkt->header.Reserved != 0)
        {
            DBGSTMT(szError = "header.Reserved!=0";)
            break;
        }
    #endif // 0

        if (N2H_USHORT(pArpPkt->header.EtherType) != NIC1394_ETHERTYPE_ARP)
        {
            DBGSTMT(szError = "header.EtherType!=ARP";)
            break;
        }

        if (N2H_USHORT(pArpPkt->hardware_type) != IP1394_HARDWARE_TYPE)
        {
            DBGSTMT(szError = "Invalid hardware_type";)
            break;
        }

        if (N2H_USHORT(pArpPkt->protocol_type) != IP1394_PROTOCOL_TYPE)
        {
            DBGSTMT(szError = "Invalid protocol_type";)
            break;
        }

        if (pArpPkt->hw_addr_len != IP1394_HW_ADDR_LEN)
        {
            DBGSTMT(szError = "Invalid hw_addr_len";)
            break;
        }


        if (pArpPkt->IP_addr_len != sizeof(ULONG))
        {
            DBGSTMT(szError = "Invalid IP_addr_len";)
            break;
        }


        // Opcode
        //
        {
            OpCode = N2H_USHORT(pArpPkt->opcode);
    
            if (    OpCode != IP1394_ARP_REQUEST
                &&  OpCode != IP1394_ARP_RESPONSE)
            {
                DBGSTMT(szError = "Invalid opcode";)
                break;
            }
        }


        // Max send block size...
        //
        {
            UINT maxrec =  pArpPkt->sender_maxrec;

            if (IP1394_IS_VALID_MAXREC(maxrec))
            {
                SenderMaxRec = maxrec;
            }
            else
            {
                DBGSTMT(szError = "Invalid sender_maxrec";)
                break;
            }
        }

        //
        // Pkt appears valid, let's fill out the parsed information....
        //
    
        ARP_ZEROSTRUCT(pPktInfo);

        pPktInfo->OpCode            =  OpCode;
        pPktInfo->SenderMaxRec  =  SenderMaxRec;
    
        // Speed code...
        //
        {
            UINT SenderMaxSpeedCode;

            //
            // We rely on the fact that the RFC speed code constants
            // (IP1394_SSPD_*) are identical to the corresponding
            // constants defined in 1394.h (SCODE_*). Let's ensure this...
            //

            #if (IP1394_SSPD_S100 != SCODE_100_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            #if (IP1394_SSPD_S200 != SCODE_200_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            #if (IP1394_SSPD_S400 != SCODE_400_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            #if (IP1394_SSPD_S800 != SCODE_800_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            #if (IP1394_SSPD_S1600 != SCODE_1600_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            #if (IP1394_SSPD_S3200 != SCODE_3200_RATE)
                #error "RFC Speed code out of sync with 1394.h"
            #endif
    
            SenderMaxSpeedCode = pArpPkt->sspd;

            if (SenderMaxSpeedCode >  SCODE_3200_RATE)
            {
                //
                // This is either a bad value, or a rate higher than we know about.
                // We can't distinguish between the two, so we just set the speed to
                // the highest we know about.
                // TODO: 3/28/99 JosephJ not sure if this is the correct
                // behaviour -- maybe we should fail -- I'll be asking the
                // working group to rule on this shortly...
                //
                SenderMaxSpeedCode = SCODE_3200_RATE;
            }

            pPktInfo->SenderMaxSpeedCode = SenderMaxSpeedCode;
        }


        // Unique ID -- we also need to swap DWORDS to convert from network byte
        // order.
        //
        {
            PUINT puSrc   = (PUINT) & (pArpPkt->sender_unique_ID);
            PUINT puDest  = (PUINT) & (pPktInfo->SenderHwAddr.UniqueID);
            // pPktInfo->SenderHwAddr.UniqueID = pArpPkt->sender_unique_ID;
            puDest[0] = puSrc[1];
            puDest[1] = puSrc[0];
        }

        pPktInfo->SenderHwAddr.Off_Low  =H2N_ULONG(pArpPkt->sender_unicast_FIFO_lo);
        pPktInfo->SenderHwAddr.Off_High =H2N_USHORT(pArpPkt->sender_unicast_FIFO_hi);

        // These remain network byte order...
        //
        pPktInfo->SenderIpAddress       = (IP_ADDRESS) pArpPkt->sender_IP_address;
        pPktInfo->TargetIpAddress       = (IP_ADDRESS) pArpPkt->target_IP_address;

        // Extract the Src Node Address
        //
        {
            PNDIS1394_UNFRAGMENTED_HEADER pHeader = (PNDIS1394_UNFRAGMENTED_HEADER)&pArpPkt->header;

            if (pHeader->u1.fHeaderHasSourceAddress == TRUE)
            {
                pPktInfo->SourceNodeAdddress = pHeader->u1.SourceAddress;
                pPktInfo->fPktHasNodeAddress = TRUE;
            }

        }
        Status = NDIS_STATUS_SUCCESS;
        

    } while (FALSE);

    if (FAIL(Status))
    {
        TR_INFO(("Bad arp pkt data at 0x%p (%s)\n",  pArpPkt, szError));
    }
    else
    {
        TR_WARN(("Received ARP PKT. UID=0x%I64x FIFO=0x%04lx:0x%08lx OP=%lu SIP=0x%04lx TIP=0x%04lx.\n",
                pPktInfo->SenderHwAddr.UniqueID,
                pPktInfo->SenderHwAddr.Off_High,
                pPktInfo->SenderHwAddr.Off_Low,
                pPktInfo->OpCode,
                pPktInfo->SenderIpAddress,
                pPktInfo->TargetIpAddress
                ));

    }

    EXIT()

    return Status;
}


VOID
arpPrepareArpPkt(
    IN      PIP1394_ARP_PKT_INFO    pPktInfo,
    // IN       UINT                        SenderMaxRec,
    OUT     PIP1394_ARP_PKT   pArpPkt
    )
/*++

Routine Description:

    Use information in pArpPktInfo to prepare an arp packet starting at
    pvArpPkt.

Arguments:

    pPktInfo        -   Parsed version of the arp request/response packet.
    // SenderMaxRec -   max_rec value of the local host
    pArpPkt         -   unitialized memory in which to store the packet contents.
                        This memory must have a min size of sizeof(*pArpPkt).
--*/
{
    // UINT SenderMaxRec;
    UINT OpCode;

    ARP_ZEROSTRUCT(pArpPkt);

    pArpPkt->header.EtherType       = H2N_USHORT(NIC1394_ETHERTYPE_ARP);
    pArpPkt->hardware_type          = H2N_USHORT(IP1394_HARDWARE_TYPE);
    pArpPkt->protocol_type          = H2N_USHORT(IP1394_PROTOCOL_TYPE);
    pArpPkt->hw_addr_len            = (UCHAR) IP1394_HW_ADDR_LEN;
    pArpPkt->IP_addr_len            = (UCHAR) sizeof(ULONG);
    pArpPkt->opcode                 = H2N_USHORT(pPktInfo->OpCode);
    pArpPkt->sender_maxrec          = (UCHAR) pPktInfo->SenderMaxRec;

    //
    // We rely on the fact that the RFC speed code constants
    // (IP1394_SSPD_*) are identical to the corresponding
    // constants defined in 1394.h (SCODE_*). We have a bunch of compiler-time
    // checks to ensure this (see  arpParseArpPkt(...)).
    // 
    pArpPkt->sspd                   =  (UCHAR) pPktInfo->SenderMaxSpeedCode;

    // Unique ID -- we also need to swap DWORDS to convert to network byte order.
    //
    {
        PUINT puSrc   = (PUINT) & (pPktInfo->SenderHwAddr.UniqueID);
        PUINT puDest  = (PUINT) & (pArpPkt->sender_unique_ID);
        // pArpPkt->sender_unique_ID        =  pPktInfo->SenderHwAddr.UniqueID;
        puDest[0] = puSrc[1];
        puDest[1] = puSrc[0];
    }

    pArpPkt->sender_unicast_FIFO_lo = N2H_ULONG(pPktInfo->SenderHwAddr.Off_Low);
    pArpPkt->sender_unicast_FIFO_hi = N2H_USHORT(pPktInfo->SenderHwAddr.Off_High);

    // These are already in network byte order...
    //
    pArpPkt->sender_IP_address      =   (ULONG) pPktInfo->SenderIpAddress;
    pArpPkt->target_IP_address      =   (ULONG) pPktInfo->TargetIpAddress;

}

NDIS_STATUS
arpPrepareArpResponse(
    IN      PARP1394_INTERFACE          pIF,            // NOLOCKIN NOLOCKOUT
    IN      PIP1394_ARP_PKT_INFO    pArpRequest,
    OUT     PIP1394_ARP_PKT_INFO    pArpResponse,
    IN      PRM_STACK_RECORD            pSR
    )
/*++

Routine Description:

    If it makes sense to do so, prepare (in pArpResponse) an arp response to
    the arp request in pArpRequest.

Arguments:

    pIF             -   Interface control block.
    pArpRequest     -   Parsed version of the ARP request packet.
    pArpResponse    -   Uninitialized memory to hold the parsed version of the
                        ARP response packet (if there is a response).

Return Value:

    NDIS_STATUS_SUCCESS if the response was filled out.
    Some NDIS error code otherwise.

--*/
{
    ENTER("arpPrepareArpResponse", 0x0d7e0e60)
    NDIS_STATUS         Status;
    PARPCB_LOCAL_IP     pLocalIp;

    Status      = NDIS_STATUS_FAILURE;
    pLocalIp    = NULL;

    RM_ASSERT_OBJUNLOCKED(&pIF->Hdr, pSR);

    do
    {
        IP_ADDRESS  TargetIpAddress =  pArpRequest->TargetIpAddress;

        // Lookup local IP
        //
        Status =  RM_LOOKUP_AND_LOCK_OBJECT_IN_GROUP(
                        &pIF->LocalIpGroup,
                        (PVOID) ULongToPtr (TargetIpAddress),
                        (RM_OBJECT_HEADER**) &pLocalIp,
                        pSR
                        );
    
        if (FAIL(Status))
        {
            pLocalIp = NULL;
            break;
        }
        
        Status = NDIS_STATUS_FAILURE;

        RM_DBG_ASSERT_LOCKED(&pLocalIp->Hdr, pSR);
        ASSERT(TargetIpAddress == pLocalIp->IpAddress);
    
        if (ARP_LOCAL_IP_IS_UNLOADING(pLocalIp)) break;

        //
        // If the local IP is non-unicast, don't respond!
        //
        if (pLocalIp->IpAddressType != LLIP_ADDR_LOCAL)
        {
            TR_WARN(("Ignoring arp request for non-unicast address 0x%08lx.\n",
                TargetIpAddress));
            break;
        }

        //
        // We do serve the target IP address. Let's fill out pArpResponse...
        //

        ARP_ZEROSTRUCT(pArpResponse);
        pArpResponse->OpCode            = IP1394_ARP_RESPONSE;
        pArpResponse->SenderIpAddress   = TargetIpAddress;

        // This field is unused in the response, but we fill it anyway..
        // 11/19/1999 From Kaz Honda of Sony: we should fill it with destination
        // IP address (i.e. the ip address of the sender of the arp request).
        // because that is analogous to what ethernet arp does. Note that
        // the ip/1394 rfc says that the field should be ignored, but we do
        // this anyway.
        //
        // pArpResponse->TargetIpAddress    = TargetIpAddress;
        pArpResponse->TargetIpAddress   =  pArpRequest->SenderIpAddress;

        // Fill out adapter info..
        //
        {
            PARP1394_ADAPTER pAdapter =  (PARP1394_ADAPTER) RM_PARENT_OBJECT(pIF);
            pArpResponse->SenderHwAddr.UniqueID  = pAdapter->info.LocalUniqueID;
            pArpResponse->SenderHwAddr.Off_Low   = pIF->recvinfo.offset.Off_Low;
            pArpResponse->SenderHwAddr.Off_High  = pIF->recvinfo.offset.Off_High;
            pArpResponse->SenderMaxRec= pAdapter->info.MaxRec;
            pArpResponse->SenderMaxSpeedCode= pAdapter->info.MaxSpeedCode;
        }

        Status = NDIS_STATUS_SUCCESS;

    } while (FALSE);
    
    if (pLocalIp != NULL)
    {
        UNLOCKOBJ(pLocalIp, pSR);
        RmTmpDereferenceObject(&pLocalIp->Hdr, pSR);
    }

    RM_ASSERT_OBJUNLOCKED(&pIF->Hdr, pSR);

    EXIT()
    return Status;
}


NDIS_STATUS
arpTaskResolveIpAddress(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )
{
    NDIS_STATUS         Status;
    PARPCB_REMOTE_IP    pRemoteIp;
    PTASK_RESOLVE_IP_ADDRESS pResolutionTask;
    enum
    {
        PEND_ResponseTimeout
    };
    ENTER("TaskResolveIpAddress", 0x3dd4b434)

    Status          = NDIS_STATUS_FAILURE;
    pRemoteIp       = (PARPCB_REMOTE_IP) RM_PARENT_OBJECT(pTask);
    pResolutionTask = (PTASK_RESOLVE_IP_ADDRESS) pTask;

    switch(Code)
    {
        case RM_TASKOP_START:
        {
            DBGMARK(0x7de307cc);
            //
            // There should NOT be another resolution task running
            // on pRemoteIp. Why? Because a resolution task is ONLY
            // started in the context of a send-pkts task, and there can be
            // ONLY one active send-pkts task on pRemoteIp at any one time.
            //

            LOCKOBJ(pRemoteIp, pSR);
            if (pRemoteIp->pResolutionTask != NULL)
            {
                ASSERT(!"pRemoteIp->pResolutionTask != NULL");
                UNLOCKOBJ(pRemoteIp, pSR);
                Status = NDIS_STATUS_FAILURE;
                break;
            }

            //
            // Make ourselves the official resolution task.
            //
            // Although it's tempting to put pTask as entity1 and
            // pTask->Hdr.szDescption as entity2 below, we specify NULL for both so
            // that we can be sure that ONLY one resolution task can be active at any
            // one time. TODO: modify addassoc semantics to get both advantages.
            //
            DBG_ADDASSOC(
                &pRemoteIp->Hdr,
                NULL,                           // Entity1
                NULL,                           // Entity2
                ARPASSOC_RESOLUTION_IF_TASK,
                "   Resolution task\n",
                pSR
                );
            pRemoteIp->pResolutionTask = pTask;
            pResolutionTask->RetriesLeft = 3;

            // Now we do a fake suspend/resume so we move on to the next stage.
            //
            RmSuspendTask(pTask, PEND_ResponseTimeout, pSR);
            UNLOCKOBJ(pRemoteIp, pSR);
            RmResumeTask(pTask, NDIS_STATUS_SUCCESS, pSR);

            Status = NDIS_STATUS_PENDING;
        }
        break;

        case  RM_TASKOP_PENDCOMPLETE:
        {
            switch(RM_PEND_CODE(pTask))
            {
                case PEND_ResponseTimeout:
                {
                    DBGMARK(0x3b5562e6);
                    LOCKOBJ(pRemoteIp, pSR);

                    //
                    // Let's see if the address is resolved and/or we're
                    // to abort the resolution task because perhaps pRemoteIp
                    // is going away.
                    //

                    if (pRemoteIp->pUnloadTask != NULL)
                    {
                        OBJLOG0(
                            pRemoteIp,
                            "Aborting address resolution because we're unloading.\n"
                            );
                        Status = NDIS_STATUS_FAILURE;
                        UNLOCKOBJ(pRemoteIp, pSR);
                        break;
                    }
                    if (pRemoteIp->pDest != NULL)
                    {
                        LOGSTATS_SuccessfulArpQueries(IF_FROM_REMOTEIP(pRemoteIp));
                        OBJLOG1(
                            pRemoteIp,
                            "Resolved Ip Address; pDest = 0x%p\n",
                            pRemoteIp->pDest
                            );
                      ASSERT(
                      CHECK_REMOTEIP_RESOLVE_STATE(pRemoteIp, ARPREMOTEIP_RESOLVED)
                      );
                      Status = NDIS_STATUS_SUCCESS;
                      UNLOCKOBJ(pRemoteIp, pSR);
                      break;
                    }

                    //
                    // We need to resolve this address..
                    //

                    if (pResolutionTask->RetriesLeft)
                    {
                        pResolutionTask->RetriesLeft--;

                        // Build an ARP request and send it out.
                        //
                        Status = arpSendArpRequest(pRemoteIp, pSR);

                        // pRemoteIp's lock is released by the above call.
                        //
                        RM_ASSERT_OBJUNLOCKED(&pRemoteIp->Hdr, pSR);

                        //
                        // We ignore the return status of the above call -- so
                        // whether or not the request could be sent out,
                        // we suspend this task for resolution-timeout seconds.
                        //
        
                        RmSuspendTask(pTask, PEND_ResponseTimeout, pSR);
        
                        RmResumeTaskDelayed(
                            pTask, 
                            0,
                            ARP1394_ADDRESS_RESOLUTION_TIMEOUT,
                            &pResolutionTask->Timer,
                            pSR
                            );

                        Status = NDIS_STATUS_PENDING;
                    }
                    else
                    {
                        LOGSTATS_FailedArpQueried(IF_FROM_REMOTEIP(pRemoteIp));
                        // Oops -- couldn't resolve this address.
                        //
                        OBJLOG1(
                            pRemoteIp,
                            "Timed out trying to resolve address 0x%08lx\n",
                            pRemoteIp->IpAddress
                            );
                        UNLOCKOBJ(pRemoteIp, pSR);
                        Status = NDIS_STATUS_FAILURE;
                    }
                }
                break;
            }
        }
        break;

        case RM_TASKOP_END:
        {
            //
            // We are done with address resolution. Clear ourselves
            // as the official address resolution task of pRemoteIp.
            //
            LOCKOBJ(pRemoteIp, pSR);

            DBGMARK(0x6bd6d27a);

            if (pRemoteIp->pResolutionTask != pTask)
            {
                ASSERT(FALSE);
            }
            else
            {
        
                // Delete the association added when setting the resolution task
                //
                DBG_DELASSOC(
                    &pRemoteIp->Hdr,
                    NULL,
                    NULL,
                    ARPASSOC_RESOLUTION_IF_TASK,
                    pSR
                    );
            
                pRemoteIp->pResolutionTask = NULL;

            }

            UNLOCKOBJ(pRemoteIp, pSR);

            // Propagate the status code
            //
            Status = (NDIS_STATUS) UserParam;
        }
        break;

        default:
        {
            ASSERTEX(!"Unknown task op", pTask);
        }
        break;

    } // switch (Code)


    RM_ASSERT_NOLOCKS(pSR);
    EXIT()

    return Status;
}

NDIS_STATUS
arpSendArpRequest(
    PARPCB_REMOTE_IP pRemoteIp, // LOCKIN NOLOCKOUT
    PRM_STACK_RECORD pSR
    )
{
    NDIS_STATUS Status;
    PARP1394_INTERFACE pIF;
    PNDIS_PACKET    pNdisPacket;
    PIP1394_ARP_PKT pPktData;
    IPAddr TargetIpAddress = 0;
    
    ENTER("arpSendArpRequest", 0xcecfc632)
    RM_ASSERT_OBJLOCKED(&pRemoteIp->Hdr, pSR);

    pIF = (PARP1394_INTERFACE)  RM_PARENT_OBJECT(pRemoteIp);

    DBGMARK(0xb90e9ffc);

    Status = arpAllocateControlPacket(
                pIF,
                sizeof(IP1394_ARP_PKT),
                ARP1394_PACKET_FLAGS_ARP,
                &pNdisPacket,
                &pPktData,
                pSR
                );

    if (FAIL(Status))
    {
        UNLOCKOBJ(pRemoteIp, pSR);
    }
    else
    {
        IP1394_ARP_PKT_INFO     PktInfo;
        PARP1394_ADAPTER pAdapter =  (PARP1394_ADAPTER) RM_PARENT_OBJECT(pIF);

        //
        // If we are running in bridge the Target Ip Address is stored in 
        // the BridgeTargetIpAddress Field. Otherwise it is in the Key.
        //
        ASSERT (pRemoteIp->IpAddress != 0);            

        // Prepare the packet.
        //
        PktInfo.SenderHwAddr.UniqueID   = pAdapter->info.LocalUniqueID;
        PktInfo.SenderHwAddr.Off_Low    = pIF->recvinfo.offset.Off_Low;
        PktInfo.SenderHwAddr.Off_High   = pIF->recvinfo.offset.Off_High;
        PktInfo.OpCode                  = IP1394_ARP_REQUEST;
        PktInfo.SenderMaxRec            = pAdapter->info.MaxRec;
        PktInfo.SenderMaxSpeedCode      = pAdapter->info.MaxSpeedCode;
        PktInfo.TargetIpAddress         = pRemoteIp->IpAddress;
        PktInfo.SenderIpAddress         = pIF->ip.DefaultLocalAddress;


        arpPrepareArpPkt(
                &PktInfo,
                // ARP_DEFAULT_MAXREC, // SenderMaxRec TODO
                pPktData
                );

        RmTmpReferenceObject(&pIF->Hdr, pSR);
        UNLOCKOBJ(pRemoteIp, pSR);
        RM_ASSERT_NOLOCKS(pSR);

        TR_WARN(("Attempting to send ARP Req PKT. UID=0x%I64x FIFO=0x%04lx:0x%08lx OP=%lu SIP=0x%04lx TIP=0x%04lx.\n",
                PktInfo.SenderHwAddr.UniqueID,
                PktInfo.SenderHwAddr.Off_High,
                PktInfo.SenderHwAddr.Off_Low,
                PktInfo.OpCode,
                PktInfo.SenderIpAddress,
                PktInfo.TargetIpAddress
                ));

        LOGSTATS_TotalQueries(pIF);

        ARP_FASTREADLOCK_IF_SEND_LOCK(pIF);

        // Actually send the packet (this will silently fail and free the pkt
        // if we're not in a position to send the pkt.)
        //
        arpSendControlPkt(
                pIF,            // LOCKIN NOLOCKOUT (IF send lk)
                pNdisPacket,
                pIF->pBroadcastDest,
                pSR
                );
        
        RM_ASSERT_NOLOCKS(pSR);
        RmTmpDereferenceObject(&pIF->Hdr, pSR);
    }

    EXIT()
    return Status;
}

VOID
arpSendControlPkt(
    IN  ARP1394_INTERFACE       *   pIF,            // LOCKIN NOLOCKOUT (IF send lk)
    IN  PNDIS_PACKET                pNdisPacket,
    PARPCB_DEST                     pDest,
    IN  PRM_STACK_RECORD            pSR
    )
/*++


Routine Description:

    Send a packet on the broadcast channel.

Arguments:

    pIF             - Our interface object
    pNdisPacket     - Packet to send

--*/
{
    NDIS_STATUS Status;
    MYBOOL      fRet;
    ENTER("arpSendControlPkt", 0x2debf9b7)

    DBGMARK(0xe6823818);

    //
    // If we can't send now, we fail.
    //
    if (pDest==NULL || !ARP_CAN_SEND_ON_DEST(pDest))
    {
        ARP_FASTUNLOCK_IF_SEND_LOCK(pIF);

        TR_WARN(("Couldn't send control pkt 0x%p.\n", pNdisPacket));

        arpFreeControlPacket(
            pIF,
            pNdisPacket,
            pSR
            );

        return;                             // EARLY RETURN
    }

    arpRefSendPkt( pNdisPacket, pDest);

    // Release the IF send lock.
    //
    ARP_FASTUNLOCK_IF_SEND_LOCK(pIF);

    // Actually send the packet
    //
#if ARPDBG_FAKE_SEND
    arpDbgFakeNdisCoSendPackets(
            pDest->VcHdr.NdisVcHandle,
            &pNdisPacket,
            1,
            &pDest->Hdr,
            &pDest->VcHdr
        );
#else   // !ARPDBG_FAKE_SEND
    NdisCoSendPackets(
            pDest->VcHdr.NdisVcHandle,
            &pNdisPacket,
            1
        );
#endif  // !ARPDBG_FAKE_SEND
    
    EXIT()

}


VOID
arpProcessArpPkt(
    PARP1394_INTERFACE pIF, // NOLOCKIN NOLOCKOUT
    PIP1394_ARP_PKT pArpPkt,
    UINT                cbBufferSize
    )
/*++
    Process an arp packet (request OR response) from the 1394 bus.
--*/
{
    NDIS_STATUS Status;
    IP1394_ARP_PKT_INFO     PktInfo;
    ENTER("arpProcessArpPkt", 0x6e81a8fa)
    RM_DECLARE_STACK_RECORD(sr)

    DBGMARK(0x03f6830e);

    Status = arpParseArpPkt(
                pArpPkt,
                cbBufferSize,
                &PktInfo
                );


    if (!FAIL(Status))
    {
        if (PktInfo.OpCode ==  IP1394_ARP_REQUEST)
        {
            arpProcessArpRequest(pIF, &PktInfo, &sr);
        }
        else
        {
            ASSERT(PktInfo.OpCode == IP1394_ARP_RESPONSE);
            arpProcessArpResponse(pIF, &PktInfo, &sr);
        }
    }

    RM_ASSERT_CLEAR(&sr);

    EXIT()

}


VOID
arpProcessArpRequest(
    PARP1394_INTERFACE          pIF,    // NOLOCKIN NOLOCKOUT
    PIP1394_ARP_PKT_INFO    pPktInfo,
    PRM_STACK_RECORD            pSR
    )
{
    IP1394_ARP_PKT_INFO     ResponsePktInfo;
    NDIS_STATUS Status;
    ENTER("arpProcessArpRequest", 0xd33fa61d)

    RM_ASSERT_NOLOCKS(pSR);

    // pStatsCmd->TotalResponses        = pIF->stats.arpcache.TotalResponses;
    // pStatsCmd->TotalLookups          = pIF->stats.arpcache.TotalLookups;

    Status = arpPrepareArpResponse(
                pIF,            // NOLOCKIN NOLOCKOUT
                pPktInfo,
                &ResponsePktInfo,
                pSR
                );

    if (!FAIL(Status))
    {
        ARP_DEST_PARAMS         DestParams;
        PNDIS_PACKET            pNdisPacket;
        PIP1394_ARP_PKT         pPktData;
    
        ARP_ZEROSTRUCT(&DestParams);
        DestParams.HwAddr.AddressType   = NIC1394AddressType_FIFO;
        DestParams.HwAddr.FifoAddress   = pPktInfo->SenderHwAddr; // Struct copy

        //
        // Update our arp cache with information from the sender's portion of
        // the arp request.
        //
        arpUpdateArpCache(
                pIF,
                pPktInfo->SenderIpAddress, // Remote IP Address,
                NULL,                   // Sender's Ethernet Address
                &DestParams,             // Remote Destination HW Address
                TRUE,                      // If required, create an entry for this.,
                pSR
                );

        //
        // Let's send the response!
        //

        Status = arpAllocateControlPacket(
                    pIF,
                    sizeof(IP1394_ARP_PKT),
                    ARP1394_PACKET_FLAGS_ARP,
                    &pNdisPacket,
                    &pPktData,
                    pSR
                    );
    
        if (!FAIL(Status))
        {
            LOGSTATS_TotalArpResponses(pIF);

            // Prepare the packet.
            //
            arpPrepareArpPkt(
                    &ResponsePktInfo,
                    // ARP_DEFAULT_MAXREC, // SenderMaxRec TODO
                    pPktData
                    );
    
            RM_ASSERT_NOLOCKS(pSR);
    
        TR_WARN(("Attempting to send ARP Resp PKT. UID=0x%I64x FIFO=0x%04lx:0x%08lx OP=%lu SIP=0x%04lx TIP=0x%04lx.\n",
                pPktInfo->SenderHwAddr.UniqueID,
                pPktInfo->SenderHwAddr.Off_High,
                pPktInfo->SenderHwAddr.Off_Low,
                pPktInfo->OpCode,
                pPktInfo->SenderIpAddress,
                pPktInfo->TargetIpAddress
                ));
    
            ARP_FASTREADLOCK_IF_SEND_LOCK(pIF);
    
            // Actually send the packet (this will silently fail and free the pkt
            // if we're not in a position to send the pkt.)
            //
            arpSendControlPkt(
                    pIF,            // LOCKIN NOLOCKOUT (IF send lk)
                    pNdisPacket,
                    pIF->pBroadcastDest,
                    pSR
                    );
            
        }
    }

    RM_ASSERT_NOLOCKS(pSR);
}

VOID
arpProcessArpResponse(
    PARP1394_INTERFACE          pIF,    // NOLOCKIN NOLOCKOUT
    PIP1394_ARP_PKT_INFO    pPktInfo,
    PRM_STACK_RECORD            pSR
    )
{
    ARP_DEST_PARAMS     DestParams;
    RM_ASSERT_NOLOCKS(pSR);
    ARP_ZEROSTRUCT(&DestParams);

    DestParams.HwAddr.AddressType       = NIC1394AddressType_FIFO;
    DestParams.HwAddr.FifoAddress       = pPktInfo->SenderHwAddr; // Struct copy

    arpUpdateArpCache(
            pIF,
            pPktInfo->SenderIpAddress, // Remote IP Address
            NULL,                     // Senders Mac Address (Bridge only)
            &DestParams,        // Remote Destination HW Address
            FALSE,          // Don't update unless we already have an entry for it.
            pSR
            );
}


VOID
arpUpdateArpCache(
    PARP1394_INTERFACE          pIF,    // NOLOCKIN NOLOCKOUT
    IP_ADDRESS                  RemoteIpAddress,
    ENetAddr                    *pRemoteEthAddress,
    PARP_DEST_PARAMS            pDestParams,
    MYBOOL                      fCreateIfRequired,
    PRM_STACK_RECORD            pSR
    )
/*++
    Update cache and also abort any resolution task that may be going on.
--*/
{
    REMOTE_DEST_KEY RemoteDestKey;
    PARP1394_ADAPTER pAdapter = (PARP1394_ADAPTER)RM_PARENT_OBJECT(pIF);
    ENTER("arpUpdateArpCache", 0x3a16a415)
    LOCKOBJ(pIF, pSR);

    do
    {
        ARPCB_REMOTE_IP *pRemoteIp = NULL;
        INT             fCreated = FALSE;
        UINT            RemoteIpCreateFlags = 0;
        NDIS_STATUS     Status;

        DBGMARK(0xd3b27d1f);

        if (fCreateIfRequired)
        {
            RemoteIpCreateFlags |= RM_CREATE;
        }

        // Create the Key from the passed in parameters
        // 
        if (ARP_BRIDGE_ENABLED(pAdapter) == TRUE) 
        {
            ASSERT (pRemoteEthAddress != NULL);
            RemoteDestKey.ENetAddress = *pRemoteEthAddress;
            TR_INFO(("Arp1394 - Bridge update cache Mac Address %x %x %x %x %x %x\n",
                        RemoteDestKey.ENetAddress.addr[0], 
                        RemoteDestKey.ENetAddress.addr[1],
                        RemoteDestKey.ENetAddress.addr[2],
                        RemoteDestKey.ENetAddress.addr[3],
                        RemoteDestKey.ENetAddress.addr[4],
                        RemoteDestKey.ENetAddress.addr[5]));
        }
        else
        {
            REMOTE_DEST_KEY_INIT(&RemoteDestKey);
          
            RemoteDestKey.IpAddress = RemoteIpAddress;

            TR_INFO( ("Arp1394 - Non-Bridge update cache Mac Address %x %x %x %x %x %x\n",
                        RemoteDestKey.ENetAddress.addr[0], 
                        RemoteDestKey.ENetAddress.addr[1],
                        RemoteDestKey.ENetAddress.addr[2],
                        RemoteDestKey.ENetAddress.addr[3],
                        RemoteDestKey.ENetAddress.addr[4],
                        RemoteDestKey.ENetAddress.addr[5]));
        }
        // Lookup/Create Remote IP Address
        //
        Status = RmLookupObjectInGroup(
                        &pIF->RemoteIpGroup,
                        RemoteIpCreateFlags,
                        (PVOID) (&RemoteDestKey),
                        (PVOID) (&RemoteDestKey), // pCreateParams
                        (RM_OBJECT_HEADER**) &pRemoteIp,
                        &fCreated,  // pfCreated
                        pSR
                        );
        if (FAIL(Status))
        {
            OBJLOG1(
                pIF,
                "Couldn't add localIp entry with addr 0x%lx\n",
                RemoteIpAddress
                );
            break;
        }

        //
        // Update the RemoteIp Last time checked here. This will refresh the 
        // Arp Entry for this Remote Ip struct
        //
        pRemoteIp->sendinfo.TimeLastChecked = 0;

        UNLOCKOBJ(pIF, pSR);

        arpUpdateRemoteIpDest(
            pIF,
            pRemoteIp,
            pDestParams,
            pSR
            );

        // If there is a resolution task going on for pRemoteIp we abort it.
        //
        arpTryAbortResolutionTask(pRemoteIp, pSR);

        // Finally, remove the tmprefs added in the lookups.
        //
        RmTmpDereferenceObject(&pRemoteIp->Hdr, pSR);

        return;                                         // EARLY RETURN

    } while (FALSE);

    UNLOCKOBJ(pIF, pSR);
    EXIT()
}


VOID
arpTryAbortResolutionTask(
        PARPCB_REMOTE_IP pRemoteIp, // NOLOCKIN NOLOCKOUT
        PRM_STACK_RECORD pSR
        )
{
    ENTER("arpTryAbortResolutionTask", 0xf34f16f2)
    PTASK_RESOLVE_IP_ADDRESS pResolutionTask;
    UINT    TaskResumed;
    RM_ASSERT_NOLOCKS(pSR);

    LOCKOBJ(pRemoteIp, pSR);
    pResolutionTask = (PTASK_RESOLVE_IP_ADDRESS) pRemoteIp->pResolutionTask;
    if (pResolutionTask != NULL)
    {
        RmTmpReferenceObject(&pResolutionTask->TskHdr.Hdr, pSR);
    }
    UNLOCKOBJ(pRemoteIp, pSR);

    DBGMARK(0x5b93ad3e);

    if (pResolutionTask != NULL)
    {
        RmResumeDelayedTaskNow(
            &pResolutionTask->TskHdr,
            &pResolutionTask->Timer,
            &TaskResumed,
            pSR
            );

        RmTmpDereferenceObject(&pResolutionTask->TskHdr.Hdr, pSR);
    }

    EXIT()
}