mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1302 lines
26 KiB
1302 lines
26 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ipinip\ioctl.c
|
|
|
|
Abstract:
|
|
|
|
IOCTL handlers for IP in IP encapsulation driver
|
|
|
|
Author:
|
|
|
|
Amritansh Raghav
|
|
|
|
Revision History:
|
|
|
|
AmritanR Created
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#define __FILE_SIG__ IOCT_SIG
|
|
|
|
#include "inc.h"
|
|
|
|
|
|
NTSTATUS
|
|
AddTunnelInterface(
|
|
IN PIRP pIrp,
|
|
IN ULONG ulInLength,
|
|
IN ULONG ulOutLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the handler for IOCTL_IPINIP_CREATE_TUNNEL. We do the normal
|
|
buffer length checks.
|
|
|
|
Locks
|
|
|
|
None
|
|
|
|
Arguments
|
|
|
|
pIrp IRP
|
|
ulInLength The length of the Input Buffer
|
|
ulOutLength The length of the Output Buffer
|
|
|
|
Return Value
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
STATUS_INFO_LENGTH_MISMATCH
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvIoBuffer;
|
|
NTSTATUS nStatus;
|
|
PTUNNEL pTunnel;
|
|
KIRQL irql;
|
|
ULONG i, ulMaxLength;
|
|
BOOLEAN bTerminated;
|
|
DWORD dwNewIndex;
|
|
|
|
PIPINIP_CREATE_TUNNEL pCreateInfo;
|
|
|
|
TraceEnter(TUNN, "AddTunnelInterface");
|
|
|
|
//
|
|
// Get the user buffer
|
|
//
|
|
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
pCreateInfo = (PIPINIP_CREATE_TUNNEL)pvIoBuffer;
|
|
|
|
//
|
|
// Always clean out the information field
|
|
//
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if(ulInLength < sizeof(IPINIP_CREATE_TUNNEL))
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("AddTunnelInterface: In Length %d too small\n",
|
|
ulInLength));
|
|
|
|
TraceLeave(TUNN, "AddTunnelInterface");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if(ulOutLength < sizeof(IPINIP_CREATE_TUNNEL))
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("AddTunnelInterface: Out Length %d too small\n",
|
|
ulInLength));
|
|
|
|
TraceLeave(TUNN, "AddTunnelInterface");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
nStatus = IpIpCreateAdapter(pCreateInfo,
|
|
0,
|
|
&dwNewIndex);
|
|
|
|
|
|
if(nStatus is STATUS_SUCCESS)
|
|
{
|
|
pIrp->IoStatus.Information = sizeof(IPINIP_CREATE_TUNNEL);
|
|
|
|
pCreateInfo->dwIfIndex = dwNewIndex;
|
|
}
|
|
|
|
TraceLeave(TUNN, "AddTunnelInterface");
|
|
|
|
return nStatus;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DeleteTunnelInterface(
|
|
IN PIRP pIrp,
|
|
IN ULONG ulInLength,
|
|
IN ULONG ulOutLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the handler for IOCTL_IPINIP_DELETE_TUNNEL.
|
|
|
|
Locks
|
|
|
|
Takes the tunnel list lock as writer and the tunnel lock
|
|
|
|
Arguments
|
|
|
|
pIrp IRP
|
|
ulInLength The length of the Input Buffer
|
|
ulOutLength The length of the Output Buffer
|
|
|
|
Return Value
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
STATUS_OBJECT_NAME_NOT_FOUND
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvIoBuffer;
|
|
NTSTATUS nStatus;
|
|
PTUNNEL pTunnel;
|
|
KIRQL irql;
|
|
ULONG i;
|
|
LIST_ENTRY leTempList;
|
|
|
|
PIPINIP_DELETE_TUNNEL pDeleteInfo;
|
|
|
|
TraceEnter(TUNN, "DeleteTunnelInterface");
|
|
|
|
//
|
|
// Get the user buffer
|
|
//
|
|
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
pDeleteInfo = (PIPINIP_DELETE_TUNNEL)pvIoBuffer;
|
|
|
|
//
|
|
// Always clean out the information field
|
|
//
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if(ulInLength < sizeof(IPINIP_DELETE_TUNNEL))
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("DeleteTunnelInterface: In Length %d too small\n",
|
|
ulInLength));
|
|
|
|
TraceLeave(TUNN, "DeleteTunnelInterface");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
InitializeListHead(&leTempList);
|
|
|
|
EnterReader(&g_rwlTunnelLock,
|
|
&irql);
|
|
|
|
pTunnel = FindTunnelGivenIndex(pDeleteInfo->dwIfIndex);
|
|
|
|
if(pTunnel is NULL)
|
|
{
|
|
ExitReader(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
//
|
|
// Could not find the tunnel for the given index
|
|
//
|
|
|
|
Trace(TUNN, ERROR,
|
|
("DeleteTunnelInterface: Couldnt find tunnel for index %d\n",
|
|
pDeleteInfo->dwIfIndex));
|
|
|
|
TraceLeave(TUNN, "DeleteTunnelInterface");
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if(IsTunnelMapped(pTunnel))
|
|
{
|
|
//
|
|
// Remove it from the address blocks
|
|
//
|
|
|
|
RemoveEntryList(&(pTunnel->leAddressLink));
|
|
}
|
|
|
|
//
|
|
// Mark the tunnel as unmapped
|
|
//
|
|
|
|
MarkTunnelUnmapped(pTunnel);
|
|
|
|
//
|
|
// Remove the tunnel from the list
|
|
//
|
|
|
|
RemoveEntryList(&(pTunnel->leTunnelLink));
|
|
|
|
pTunnel->dwAdminState |= TS_DELETING;
|
|
|
|
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
|
|
|
|
//
|
|
// If there are queued packets, copy out the queue
|
|
//
|
|
|
|
if(!IsListEmpty(&(pTunnel->lePacketQueueHead)))
|
|
{
|
|
//
|
|
// Copy out Flink and Blink
|
|
//
|
|
|
|
leTempList = pTunnel->lePacketQueueHead;
|
|
|
|
//
|
|
// Set Flink's Blink
|
|
//
|
|
|
|
leTempList.Flink->Blink = &leTempList;
|
|
|
|
//
|
|
// Set Blink's Flink
|
|
//
|
|
|
|
leTempList.Blink->Flink = &leTempList;
|
|
}
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
|
|
|
|
|
|
//
|
|
// Deref the tunnel once for deleting it from the list
|
|
//
|
|
|
|
DereferenceTunnel(pTunnel);
|
|
|
|
//
|
|
// Protected by the tunnel lock
|
|
//
|
|
|
|
g_ulNumTunnels--;
|
|
|
|
//
|
|
// Let go of the lock
|
|
//
|
|
|
|
ExitReader(&g_rwlTunnelLock, irql);
|
|
|
|
//
|
|
// Before deleting from IP, free all the packets
|
|
//
|
|
|
|
while(!IsListEmpty(&leTempList))
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PQUEUE_NODE pQueueNode;
|
|
|
|
pleNode = RemoveHeadList(&leTempList);
|
|
|
|
pQueueNode = CONTAINING_RECORD(pleNode,
|
|
QUEUE_NODE,
|
|
leQueueItemLink);
|
|
|
|
for(i = 0; i < pQueueNode->uiNumPackets; i++)
|
|
{
|
|
PNDIS_PACKET pnpPacket;
|
|
|
|
pnpPacket = pQueueNode->ppPacketArray[i];
|
|
|
|
//
|
|
// ok to access pvIpContext since we have a reference
|
|
// and the tunnel is not going away
|
|
//
|
|
|
|
g_pfnIpSendComplete(pTunnel->pvIpContext,
|
|
pnpPacket,
|
|
NDIS_STATUS_ADAPTER_NOT_READY);
|
|
}
|
|
|
|
FreeQueueNode(pQueueNode);
|
|
}
|
|
|
|
//
|
|
// Now delete the interface
|
|
//
|
|
|
|
g_pfnIpDeleteInterface(pTunnel->pvIpContext,
|
|
TRUE);
|
|
|
|
//
|
|
// Dereference the tunnel for deleting it from IP
|
|
// and once more because FindTunnel... put a ref on it
|
|
//
|
|
|
|
DereferenceTunnel(pTunnel);
|
|
DereferenceTunnel(pTunnel);
|
|
|
|
TraceLeave(TUNN, "DeleteTunnelInterface");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetTunnelInfo(
|
|
IN PIRP pIrp,
|
|
IN ULONG ulInLength,
|
|
IN ULONG ulOutLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the handler for IOCTL_IPINIP_SET_TUNNEL. We do the normal
|
|
buffer length checks.
|
|
|
|
Locks
|
|
|
|
Takes the tunnel list lock as writer and the tunnel lock
|
|
|
|
Arguments
|
|
|
|
pIrp IRP
|
|
ulInLength The length of the Input Buffer
|
|
ulOutLength The length of the Output Buffer
|
|
|
|
Return Value
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
STATUS_OBJECT_NAME_NOT_FOUND
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvIoBuffer;
|
|
NTSTATUS nsStatus;
|
|
PTUNNEL pTunnel;
|
|
KIRQL irql;
|
|
LIST_ENTRY leTempList;
|
|
ULONG i;
|
|
|
|
PIPINIP_SET_TUNNEL_INFO pSet;
|
|
PTDI_ADDRESS_IP pTdiIp;
|
|
PADDRESS_BLOCK pAddrBlock;
|
|
|
|
TraceEnter(TUNN, "SetTunnelInfo");
|
|
|
|
//
|
|
// Get the user buffer
|
|
//
|
|
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
pSet = (PIPINIP_SET_TUNNEL_INFO)pvIoBuffer;
|
|
|
|
//
|
|
// Always clean out the information field
|
|
//
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if(ulInLength < sizeof(IPINIP_SET_TUNNEL_INFO))
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("SetTunnelInfo: In Length %d too small\n",
|
|
ulInLength));
|
|
|
|
TraceLeave(TUNN, "SetTunnelInfo");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Validate the parameters
|
|
//
|
|
|
|
if((pSet->dwLocalAddress is INVALID_IP_ADDRESS) or
|
|
(pSet->dwRemoteAddress is INVALID_IP_ADDRESS) or
|
|
((DWORD)(pSet->dwLocalAddress & 0x000000E0) >= (DWORD)0x000000E0) or
|
|
((DWORD)(pSet->dwRemoteAddress & 0x000000E0) >= (DWORD)0x000000E0) or
|
|
(pSet->byTtl is 0))
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("SetTunnelInfo: One of %d.%d.%d.%d %d.%d.%d.%d %d is invalid\n",
|
|
PRINT_IPADDR(pSet->dwLocalAddress),
|
|
PRINT_IPADDR(pSet->dwRemoteAddress),
|
|
pSet->byTtl));
|
|
|
|
TraceLeave(TUNN, "SetTunnelInfo");
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
InitializeListHead(&leTempList);
|
|
|
|
EnterWriter(&g_rwlTunnelLock,
|
|
&irql);
|
|
|
|
pTunnel = FindTunnelGivenIndex(pSet->dwIfIndex);
|
|
|
|
if(pTunnel is NULL)
|
|
{
|
|
ExitWriter(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
//
|
|
// Could not find the tunnel for the given index
|
|
//
|
|
|
|
Trace(TUNN, ERROR,
|
|
("SetTunnelInfo: Couldnt find tunnel for index %d\n",
|
|
pSet->dwIfIndex));
|
|
|
|
TraceLeave(TUNN, "SetTunnelInfo");
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if(IsTunnelMapped(pTunnel))
|
|
{
|
|
Trace(TUNN, TRACE,
|
|
("SetTunnelInfo: Tunnel already mapped\n"));
|
|
|
|
//
|
|
// if we are only changing the TTL, alles okay
|
|
//
|
|
|
|
if((pSet->dwRemoteAddress is pTunnel->REMADDR) and
|
|
(pSet->dwLocalAddress is pTunnel->LOCALADDR))
|
|
{
|
|
Trace(TUNN, TRACE,
|
|
("SetTunnelInfo: Only changing TTL on mapped tunnel\n"));
|
|
|
|
pTunnel->byTtl = pSet->byTtl;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
|
|
|
|
DereferenceTunnel(pTunnel);
|
|
|
|
ExitWriter(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
TraceLeave(TUNN, "SetTunnelInfo");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// So addresses are changing..
|
|
//
|
|
|
|
Trace(TUNN, TRACE,
|
|
("SetTunnelInfo: Changing address on mapped tunnel\n"));
|
|
|
|
//
|
|
// Remove it from the address blocks
|
|
//
|
|
|
|
RemoveEntryList(&(pTunnel->leAddressLink));
|
|
|
|
//
|
|
// This also marks it unmapped
|
|
//
|
|
|
|
pTunnel->dwAdminState = IF_ADMIN_STATUS_DOWN;
|
|
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
|
|
|
|
//
|
|
// Copy out the queued packets to delete later
|
|
//
|
|
|
|
if(!IsListEmpty(&(pTunnel->lePacketQueueHead)))
|
|
{
|
|
//
|
|
// Copy out Flink and Blink
|
|
//
|
|
|
|
leTempList = pTunnel->lePacketQueueHead;
|
|
|
|
//
|
|
// Set Flink's Blink
|
|
//
|
|
|
|
leTempList.Flink->Blink = &leTempList;
|
|
|
|
//
|
|
// Set Blink's Flink
|
|
//
|
|
|
|
leTempList.Blink->Flink = &leTempList;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtAssert(IsListEmpty(&(pTunnel->lePacketQueueHead)));
|
|
}
|
|
|
|
//
|
|
// Set the state down
|
|
//
|
|
|
|
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
|
|
pTunnel->dwAdminState = IF_ADMIN_STATUS_UP;
|
|
|
|
//
|
|
// See if we have the address block for this
|
|
//
|
|
|
|
pAddrBlock = GetAddressBlock(pSet->dwLocalAddress);
|
|
|
|
if(pAddrBlock)
|
|
{
|
|
RtAssert(pAddrBlock->dwAddress is pSet->dwLocalAddress);
|
|
|
|
if(pAddrBlock->bAddressPresent)
|
|
{
|
|
pTunnel->dwAdminState |= TS_ADDRESS_PRESENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Create one
|
|
//
|
|
|
|
pAddrBlock = RtAllocate(NonPagedPool,
|
|
sizeof(ADDRESS_BLOCK),
|
|
TUNNEL_TAG);
|
|
|
|
if(pAddrBlock is NULL)
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
|
|
|
|
ExitWriter(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
DereferenceTunnel(pTunnel);
|
|
|
|
Trace(TDI, ERROR,
|
|
("TdixAddressArrival: Unable to allocate address block\n"));
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pAddrBlock->dwAddress = pSet->dwLocalAddress;
|
|
pAddrBlock->bAddressPresent = FALSE;
|
|
|
|
InitializeListHead(&(pAddrBlock->leTunnelList));
|
|
|
|
InsertHeadList(&g_leAddressList,
|
|
&(pAddrBlock->leAddressLink));
|
|
}
|
|
|
|
//
|
|
// Link this onto the address
|
|
//
|
|
|
|
InsertHeadList(&(pAddrBlock->leTunnelList),
|
|
&(pTunnel->leAddressLink));
|
|
|
|
pTunnel->REMADDR = pSet->dwRemoteAddress;
|
|
pTunnel->LOCALADDR = pSet->dwLocalAddress;
|
|
pTunnel->byTtl = pSet->byTtl;
|
|
|
|
MarkTunnelMapped(pTunnel);
|
|
|
|
//
|
|
// Initialize the TDI structure for this
|
|
//
|
|
|
|
pTdiIp = &(pTunnel->tiaIpAddr.Address[0].Address[0]);
|
|
|
|
pTdiIp->sin_port = 0;
|
|
pTdiIp->in_addr = pTunnel->REMADDR;
|
|
|
|
if(pTunnel->dwAdminState & TS_ADDRESS_PRESENT)
|
|
{
|
|
UpdateMtuAndReachability(pTunnel);
|
|
}
|
|
|
|
//
|
|
// Return the current operational state to the user
|
|
//
|
|
|
|
pSet->dwOperationalState = pTunnel->dwOperState;
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pTunnel->rlLock));
|
|
|
|
ExitWriter(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
|
|
//
|
|
// Before dereferencing
|
|
//
|
|
|
|
while(!IsListEmpty(&leTempList))
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PQUEUE_NODE pQueueNode;
|
|
|
|
pleNode = RemoveHeadList(&leTempList);
|
|
|
|
pQueueNode = CONTAINING_RECORD(pleNode,
|
|
QUEUE_NODE,
|
|
leQueueItemLink);
|
|
|
|
for(i = 0; i < pQueueNode->uiNumPackets; i++)
|
|
{
|
|
PNDIS_PACKET pnpPacket;
|
|
|
|
pnpPacket = pQueueNode->ppPacketArray[i];
|
|
|
|
//
|
|
// ok to access pvIpContext since we have a reference
|
|
// and the tunnel is not going away
|
|
//
|
|
|
|
g_pfnIpSendComplete(pTunnel->pvIpContext,
|
|
pnpPacket,
|
|
NDIS_STATUS_ADAPTER_NOT_READY);
|
|
}
|
|
|
|
FreeQueueNode(pQueueNode);
|
|
}
|
|
|
|
DereferenceTunnel(pTunnel);
|
|
|
|
TraceLeave(TUNN, "SetTunnelInfo");
|
|
|
|
pIrp->IoStatus.Information = sizeof(IPINIP_SET_TUNNEL_INFO);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetTunnelTable(
|
|
IN PIRP pIrp,
|
|
IN ULONG ulInLength,
|
|
IN ULONG ulOutLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the handler for IOCTL_IPINIP_GET_TUNNEL. We do the normal
|
|
buffer length checks.
|
|
|
|
Locks
|
|
|
|
Takes the tunnel list lock as Reader
|
|
|
|
Arguments
|
|
|
|
pIrp IRP
|
|
ulInLength The length of the Input Buffer
|
|
ulOutLength The length of the Output Buffer
|
|
|
|
Return Value
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvIoBuffer;
|
|
ULONG i;
|
|
NTSTATUS nsStatus;
|
|
KIRQL irql;
|
|
PLIST_ENTRY pleNode;
|
|
PTUNNEL pTunnel;
|
|
|
|
PIPINIP_TUNNEL_TABLE pTunnelTable;
|
|
|
|
|
|
TraceEnter(TUNN, "GetTunnels");
|
|
|
|
//
|
|
// Get the user buffer
|
|
//
|
|
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
pTunnelTable = (PIPINIP_TUNNEL_TABLE)pvIoBuffer;
|
|
|
|
//
|
|
// Always clean out the information field
|
|
//
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if(ulOutLength < SIZEOF_BASIC_TUNNEL_TABLE)
|
|
{
|
|
Trace(TUNN, ERROR,
|
|
("GetTunnels: In Length %d too smaller than smallest table %d\n",
|
|
ulInLength,
|
|
SIZEOF_BASIC_TUNNEL_TABLE));
|
|
|
|
TraceLeave(TUNN, "GetTunnels");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
EnterReader(&g_rwlTunnelLock,
|
|
&irql);
|
|
|
|
if(ulOutLength < SIZEOF_TUNNEL_TABLE(g_ulNumTunnels))
|
|
{
|
|
ExitReader(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
Trace(TUNN, ERROR,
|
|
("GetTunnels: Len %d is less than required (%d) for %d i/f\n",
|
|
ulOutLength,
|
|
SIZEOF_TUNNEL_TABLE(g_ulNumTunnels),
|
|
g_ulNumTunnels));
|
|
|
|
pTunnelTable->ulNumTunnels = g_ulNumTunnels;
|
|
|
|
pIrp->IoStatus.Information = SIZEOF_BASIC_TUNNEL_TABLE;
|
|
|
|
TraceLeave(TUNN, "GetTunnels");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pTunnelTable->ulNumTunnels = g_ulNumTunnels;
|
|
|
|
//
|
|
// So we have enough space to fill the tunnel
|
|
//
|
|
|
|
for(pleNode = g_leTunnelList.Flink, i = 0;
|
|
pleNode isnot &g_leTunnelList;
|
|
pleNode = pleNode->Flink, i++)
|
|
{
|
|
pTunnel = CONTAINING_RECORD(pleNode,
|
|
TUNNEL,
|
|
leTunnelLink);
|
|
|
|
pTunnelTable->rgTable[i].dwIfIndex = pTunnel->dwIfIndex;
|
|
pTunnelTable->rgTable[i].dwRemoteAddress = pTunnel->REMADDR;
|
|
pTunnelTable->rgTable[i].dwLocalAddress = pTunnel->LOCALADDR;
|
|
pTunnelTable->rgTable[i].fMapped = IsTunnelMapped(pTunnel);
|
|
pTunnelTable->rgTable[i].byTtl = pTunnel->byTtl;
|
|
}
|
|
|
|
RtAssert(i is g_ulNumTunnels);
|
|
|
|
ExitReader(&g_rwlTunnelLock,
|
|
irql);
|
|
|
|
pIrp->IoStatus.Information = SIZEOF_TUNNEL_TABLE(i);
|
|
|
|
TraceLeave(TUNN, "GetTunnels");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ProcessNotification(
|
|
PIRP pIrp,
|
|
ULONG ulInLength,
|
|
ULONG ulOutLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The handler for IOCTL_IPINIP_NOTIFICATION. We see if we have some info
|
|
we wish to return to the caller and if we do, we return it. Otherwise,
|
|
we pend the IRP and use it later when we need to report an event to
|
|
the user mode
|
|
|
|
Locks:
|
|
|
|
Acquires the IoCancelSpinLock
|
|
|
|
Arguments:
|
|
|
|
pIrp IRP
|
|
ulInLength The length of the Input Buffer
|
|
ulOutLength The length of the Output Buffer
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL kiIrql;
|
|
PLIST_ENTRY pleNode;
|
|
PVOID pvIoBuffer;
|
|
|
|
PPENDING_MESSAGE pMessage;
|
|
|
|
TraceEnter(GLOBAL, "ProcessNotification");
|
|
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if((ulInLength < sizeof(IPINIP_NOTIFICATION)) or
|
|
(ulOutLength < sizeof(IPINIP_NOTIFICATION)))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// use cancel spin lock to prevent irp being cancelled during this call.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&kiIrql);
|
|
|
|
//
|
|
// If we have a pending notification then complete it - else
|
|
// queue the notification IRP
|
|
//
|
|
|
|
if(!IsListEmpty(&g_lePendingMessageList))
|
|
{
|
|
//
|
|
// We have some old info
|
|
//
|
|
|
|
Trace(GLOBAL, TRACE,
|
|
("ProcNotification: Pending message being completed\n"));
|
|
|
|
//
|
|
// Remove it off the pending list
|
|
//
|
|
|
|
pleNode = RemoveHeadList(&g_lePendingMessageList);
|
|
|
|
//
|
|
// Get a pointer to the structure
|
|
//
|
|
|
|
pMessage = CONTAINING_RECORD(pleNode,
|
|
PENDING_MESSAGE,
|
|
leMessageLink);
|
|
|
|
//
|
|
// Copy out the event to the user mode buffer
|
|
//
|
|
|
|
RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer,
|
|
&pMessage->inMsg,
|
|
sizeof(IPINIP_NOTIFICATION));
|
|
|
|
//
|
|
// Mark the IRP as non pending (and hence non cancelable)
|
|
//
|
|
|
|
IoSetCancelRoutine(pIrp,
|
|
NULL);
|
|
|
|
//
|
|
// Fill the irp info
|
|
//
|
|
|
|
pIrp->IoStatus.Information = sizeof(IPINIP_NOTIFICATION);
|
|
|
|
IoReleaseCancelSpinLock(kiIrql);
|
|
|
|
//
|
|
// Free the allocated message
|
|
//
|
|
|
|
FreeMessage(pMessage);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
Trace(GLOBAL, TRACE,
|
|
("ProcNotification: Notification being queued\n"));
|
|
|
|
|
|
//
|
|
// Queue this IRP to use for later
|
|
// First, mark the irp as pending
|
|
//
|
|
|
|
IoMarkIrpPending(pIrp);
|
|
|
|
//
|
|
// Queue up the irp at the end
|
|
//
|
|
|
|
InsertTailList(&g_lePendingIrpList,
|
|
&(pIrp->Tail.Overlay.ListEntry));
|
|
|
|
//
|
|
// Set the cancel routine
|
|
//
|
|
|
|
IoSetCancelRoutine(pIrp,
|
|
CancelNotificationIrp);
|
|
|
|
IoReleaseCancelSpinLock(kiIrql);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
VOID
|
|
CancelNotificationIrp(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to cancel a queued irp
|
|
|
|
Locks:
|
|
|
|
Called with the IoCancelSpinLock acquired
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject
|
|
pIrp
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
TraceEnter(GLOBAL, "CancelNotificationIrp");
|
|
|
|
//
|
|
// Mark this Irp as cancelled
|
|
//
|
|
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Take off our own list
|
|
//
|
|
|
|
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Release cancel spin lock which the IO system acquired
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
|
|
|
IoCompleteRequest(pIrp,
|
|
IO_NETWORK_INCREMENT);
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteNotificationIrp(
|
|
PPENDING_MESSAGE pMessage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to send a message to user mode
|
|
|
|
Locks:
|
|
|
|
Acquires the IoCancelSpinLock
|
|
|
|
Arguments:
|
|
|
|
pEvent
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL kiIrql;
|
|
|
|
TraceEnter(GLOBAL, "CompleteNotificationIrp");
|
|
|
|
//
|
|
// grab cancel spin lock
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&kiIrql);
|
|
|
|
if(!IsListEmpty(&g_lePendingIrpList))
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// We have a pending IRP. Use it to return info to router manager
|
|
//
|
|
|
|
pleNode = RemoveHeadList(&g_lePendingIrpList) ;
|
|
|
|
pIrp = CONTAINING_RECORD(pleNode,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer,
|
|
&(pMessage->inMsg),
|
|
sizeof(IPINIP_NOTIFICATION));
|
|
|
|
Trace(GLOBAL, TRACE,
|
|
("CompleteNotificationIrp: Returning Irp with event code of %d\n",
|
|
((PIPINIP_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer)->ieEvent));
|
|
|
|
IoSetCancelRoutine(pIrp,
|
|
NULL);
|
|
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
pIrp->IoStatus.Information = sizeof(IPINIP_NOTIFICATION);
|
|
|
|
//
|
|
// release lock
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(kiIrql);
|
|
|
|
IoCompleteRequest(pIrp,
|
|
IO_NETWORK_INCREMENT);
|
|
|
|
//
|
|
// Free the allocated Message
|
|
//
|
|
|
|
FreeMessage(pMessage);
|
|
|
|
}
|
|
else
|
|
{
|
|
Trace(GLOBAL, TRACE,
|
|
("CompleteNotificationIrp: Found no pending Irp so queuing message\n"));
|
|
|
|
|
|
InsertTailList(&g_lePendingMessageList,
|
|
&(pMessage->leMessageLink));
|
|
|
|
//
|
|
// release lock
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(kiIrql);
|
|
}
|
|
}
|
|
|
|
PADDRESS_BLOCK
|
|
GetAddressBlock(
|
|
DWORD dwAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Looks up the address block for the given address
|
|
|
|
Locks
|
|
|
|
Must be called with the g_rwlTunnelLock held
|
|
|
|
Arguments
|
|
|
|
dwAddress
|
|
|
|
Return Value
|
|
|
|
Pointer to the address block
|
|
NULL if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
|
|
for(pleNode = g_leAddressList.Flink;
|
|
pleNode isnot &g_leAddressList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
PADDRESS_BLOCK pAddrBlock;
|
|
|
|
pAddrBlock = CONTAINING_RECORD(pleNode,
|
|
ADDRESS_BLOCK,
|
|
leAddressLink);
|
|
|
|
if(pAddrBlock->dwAddress is dwAddress)
|
|
{
|
|
return pAddrBlock;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
UpdateMtuAndReachability(
|
|
PTUNNEL pTunnel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Updates the MTU and reachability info for a tunnel
|
|
|
|
Locks
|
|
|
|
Must be called with the Tunnel locked and referenced
|
|
|
|
Arguments
|
|
|
|
pTunnel
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwLocalNet;
|
|
RouteCacheEntry *pDummyRce;
|
|
BYTE byType;
|
|
USHORT usMtu;
|
|
IPOptInfo OptInfo;
|
|
BOOLEAN bChange;
|
|
ULONG ulNewMtu;
|
|
|
|
PPENDING_MESSAGE pMessage;
|
|
|
|
bChange = FALSE;
|
|
|
|
RtAssert(pTunnel->dwAdminState & TS_ADDRESS_PRESENT);
|
|
RtAssert(IsTunnelMapped(pTunnel));
|
|
|
|
RtlZeroMemory(&OptInfo,
|
|
sizeof(OptInfo));
|
|
|
|
//
|
|
// See if the remote address is reachable and what the MTU is.
|
|
//
|
|
|
|
dwLocalNet = g_pfnOpenRce(pTunnel->REMADDR,
|
|
pTunnel->LOCALADDR,
|
|
&pDummyRce,
|
|
&byType,
|
|
&usMtu,
|
|
&OptInfo);
|
|
|
|
if(dwLocalNet isnot NULL_IP_ADDR)
|
|
{
|
|
LLIPMTUChange mtuChangeInfo;
|
|
|
|
pTunnel->dwAdminState |= TS_ADDRESS_REACHABLE;
|
|
|
|
//
|
|
// Clear out any error bits
|
|
//
|
|
|
|
ClearErrorBits(pTunnel);
|
|
|
|
//
|
|
// Set the MTU if its changed
|
|
//
|
|
|
|
RtAssert(usMtu > MAX_IP_HEADER_LENGTH);
|
|
|
|
ulNewMtu = usMtu - MAX_IP_HEADER_LENGTH;
|
|
|
|
if(pTunnel->ulMtu isnot ulNewMtu)
|
|
{
|
|
bChange = TRUE;
|
|
|
|
pTunnel->ulMtu = ulNewMtu;
|
|
|
|
mtuChangeInfo.lmc_mtu = pTunnel->ulMtu;
|
|
|
|
g_pfnIpStatus(pTunnel->pvIpContext,
|
|
LLIP_STATUS_MTU_CHANGE,
|
|
&mtuChangeInfo,
|
|
sizeof(LLIPMTUChange),
|
|
NULL);
|
|
}
|
|
|
|
if(pTunnel->dwOperState isnot IF_OPER_STATUS_OPERATIONAL)
|
|
{
|
|
bChange = TRUE;
|
|
|
|
pTunnel->dwOperState = IF_OPER_STATUS_OPERATIONAL;
|
|
}
|
|
|
|
//
|
|
// Close the RCE
|
|
//
|
|
|
|
g_pfnCloseRce(pDummyRce);
|
|
}
|
|
else
|
|
{
|
|
pTunnel->dwAdminState &= ~TS_ADDRESS_REACHABLE;
|
|
|
|
if(pTunnel->dwOperState isnot IF_OPER_STATUS_NON_OPERATIONAL)
|
|
{
|
|
bChange = TRUE;
|
|
|
|
pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL;
|
|
}
|
|
}
|
|
|
|
if(bChange)
|
|
{
|
|
pMessage = AllocateMessage();
|
|
|
|
if(pMessage isnot NULL)
|
|
{
|
|
if(pTunnel->dwOperState is IF_OPER_STATUS_OPERATIONAL)
|
|
{
|
|
pMessage->inMsg.ieEvent = IE_INTERFACE_UP;
|
|
}
|
|
else
|
|
{
|
|
pMessage->inMsg.ieEvent = IE_INTERFACE_DOWN;
|
|
}
|
|
|
|
pMessage->inMsg.iseSubEvent = 0xFFFFFFFF;
|
|
pMessage->inMsg.dwIfIndex = pTunnel->dwIfIndex;
|
|
|
|
CompleteNotificationIrp(pMessage);
|
|
}
|
|
}
|
|
|
|
KeQueryTickCount((PLARGE_INTEGER)&((pTunnel->ullLastChange)));
|
|
}
|