Source code of Windows XP (NT5)
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

/*++
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)));
}