1375 lines
32 KiB
C
1375 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1997, Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ticket.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code for the NAT's ticket-management.
|
||
|
||
Author:
|
||
|
||
Abolade Gbadegesin (t-abolag) 22-Aug-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
ULONG DynamicTicketCount;
|
||
LIST_ENTRY DynamicTicketList;
|
||
KSPIN_LOCK DynamicTicketLock;
|
||
ULONG TicketCount;
|
||
|
||
|
||
NTSTATUS
|
||
NatCreateDynamicTicket(
|
||
PIP_NAT_CREATE_DYNAMIC_TICKET CreateTicket,
|
||
ULONG InputBufferLength,
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to create a dynamically-activated ticket
|
||
in response to an 'IOCTL_IP_NAT_CREATE_DYNAMIC_TICKET' request.
|
||
|
||
Arguments:
|
||
|
||
CreateTicket - describes the dynamic ticket to be created
|
||
|
||
InputBufferLength - the length of the buffer specified by 'CreateTicket'
|
||
|
||
FileObject - file-object with which to associate the dynamic ticket
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY InsertionPoint;
|
||
ULONG i;
|
||
KIRQL Irql;
|
||
ULONG Key;
|
||
ULONG ResponseArrayLength;
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
CALLTRACE(("NatCreateDynamicTicket\n"));
|
||
|
||
//
|
||
// Validate the parameters.
|
||
//
|
||
|
||
if ((CreateTicket->Protocol != NAT_PROTOCOL_TCP &&
|
||
CreateTicket->Protocol != NAT_PROTOCOL_UDP) || !CreateTicket->Port) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
} else if (CreateTicket->ResponseCount >
|
||
MAXLONG / sizeof(CreateTicket->ResponseArray[0])) {
|
||
return STATUS_INVALID_BUFFER_SIZE;
|
||
}
|
||
|
||
ResponseArrayLength =
|
||
CreateTicket->ResponseCount *
|
||
sizeof(CreateTicket->ResponseArray[0]) +
|
||
FIELD_OFFSET(IP_NAT_CREATE_DYNAMIC_TICKET, ResponseArray);
|
||
if (InputBufferLength < ResponseArrayLength) {
|
||
return STATUS_INVALID_BUFFER_SIZE;
|
||
}
|
||
|
||
for (i = 0; i < CreateTicket->ResponseCount; i++) {
|
||
if ((CreateTicket->ResponseArray[i].Protocol != NAT_PROTOCOL_TCP &&
|
||
CreateTicket->ResponseArray[i].Protocol != NAT_PROTOCOL_UDP) ||
|
||
!CreateTicket->ResponseArray[i].StartPort ||
|
||
!CreateTicket->ResponseArray[i].EndPort ||
|
||
NTOHS(CreateTicket->ResponseArray[i].StartPort) >
|
||
NTOHS(CreateTicket->ResponseArray[i].EndPort)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Construct a key and search for a duplicate
|
||
//
|
||
|
||
Key = MAKE_DYNAMIC_TICKET_KEY(CreateTicket->Protocol, CreateTicket->Port);
|
||
KeAcquireSpinLock(&DynamicTicketLock, &Irql);
|
||
if (NatLookupDynamicTicket(Key, &InsertionPoint)) {
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
TRACE(TICKET, ("NatCreateDynamicTicket: collision %08X\n", Key));
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize the new dynamic ticket
|
||
//
|
||
|
||
Ticket =
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(NAT_DYNAMIC_TICKET) + ResponseArrayLength,
|
||
NAT_TAG_DYNAMIC_TICKET
|
||
);
|
||
if (!Ticket) {
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
ERROR(("NatCreateTicket: ticket could not be allocated\n"));
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
Ticket->Key = Key;
|
||
Ticket->FileObject = FileObject;
|
||
Ticket->ResponseCount = CreateTicket->ResponseCount;
|
||
Ticket->ResponseArray = (PVOID)(Ticket + 1);
|
||
for (i = 0; i < CreateTicket->ResponseCount; i++) {
|
||
Ticket->ResponseArray[i].Protocol =
|
||
CreateTicket->ResponseArray[i].Protocol;
|
||
Ticket->ResponseArray[i].StartPort =
|
||
CreateTicket->ResponseArray[i].StartPort;
|
||
Ticket->ResponseArray[i].EndPort =
|
||
CreateTicket->ResponseArray[i].EndPort;
|
||
}
|
||
InsertTailList(InsertionPoint, &Ticket->Link);
|
||
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
InterlockedIncrement(&DynamicTicketCount);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatCreateDynamicTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatCreateTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
UCHAR Protocol,
|
||
ULONG PrivateAddress,
|
||
USHORT PrivateOrEndPort,
|
||
ULONG RemoteAddress OPTIONAL,
|
||
ULONG RemotePort OPTIONAL,
|
||
ULONG Flags,
|
||
PNAT_USED_ADDRESS AddressToUse OPTIONAL,
|
||
USHORT PortToUse OPTIONAL,
|
||
PULONG PublicAddress,
|
||
PUSHORT PublicPort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates and initializes a NAT ticket to allow a single
|
||
inbound session to be established using 'Protocol'. The routine acquires
|
||
an address and port to be advertised as the publicly-visible endpoint
|
||
of the session, and sets the ticket to expire in 'TimeoutSeconds'.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which the ticket is to be created
|
||
|
||
Protocol - the protocol of the inbound session to be allowed
|
||
|
||
PrivateAddress - the private address to which the inbound session
|
||
should be directed when the ticket is used.
|
||
|
||
PrivateOrEndPort - contains either
|
||
(a) the private port to which the inbound session should be
|
||
directed when the ticket is used, or
|
||
(b) the end of a range of public ports which starts with 'PortToUse',
|
||
if 'Flags' has 'NAT_TICKET_FLAG_IS_RANGE' set,
|
||
in which case the *private* port to which the inbound session
|
||
should be directed is determined when the ticket is used.
|
||
|
||
Flags - the initial flags for the ticket;
|
||
NAT_TICKET_FLAG_PERSISTENT - the ticket is reusable
|
||
NAT_TICKET_FLAG_PORT_MAPPING - the ticket is for a port-mapping
|
||
NAT_TICKET_FLAG_IS_RANGE - the ticket is for a range of ports
|
||
|
||
AddressToUse - optionally supplies the public address for the ticket
|
||
|
||
PortToUse - if 'AddressToUse' is set, must supply the public-port
|
||
|
||
PublicAddress - receives the public address assigned to the ticket.
|
||
|
||
PublicPort - receives the public port assigned to the ticket.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY InsertionPoint;
|
||
ULONG64 Key;
|
||
ULONG64 RemoteKey;
|
||
NTSTATUS status;
|
||
PNAT_TICKET Ticket;
|
||
|
||
TRACE(TICKET, ("NatCreateTicket\n"));
|
||
|
||
if (AddressToUse) {
|
||
if (!NatReferenceAddressPoolEntry(AddressToUse)) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
*PublicAddress = AddressToUse->PublicAddress;
|
||
*PublicPort = PortToUse;
|
||
} else {
|
||
|
||
//
|
||
// Acquire a public address
|
||
//
|
||
|
||
status =
|
||
NatAcquireFromAddressPool(
|
||
Interfacep,
|
||
PrivateAddress,
|
||
0,
|
||
&AddressToUse
|
||
);
|
||
if (!NT_SUCCESS(status)) { return status; }
|
||
|
||
//
|
||
// Acquire a unique public port
|
||
//
|
||
|
||
status =
|
||
NatAcquireFromPortPool(
|
||
Interfacep,
|
||
AddressToUse,
|
||
Protocol,
|
||
PrivateOrEndPort,
|
||
&PortToUse
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
|
||
return status;
|
||
}
|
||
|
||
*PublicAddress = AddressToUse->PublicAddress;
|
||
*PublicPort = PortToUse;
|
||
}
|
||
|
||
//
|
||
// Look for a duplicate of the key
|
||
//
|
||
|
||
Key = MAKE_TICKET_KEY(Protocol, *PublicAddress, *PublicPort);
|
||
RemoteKey = MAKE_TICKET_KEY(Protocol, RemoteAddress, RemotePort);
|
||
if (NatLookupTicket(Interfacep, Key, RemoteKey, &InsertionPoint)) {
|
||
//
|
||
// Collision; fail
|
||
//
|
||
TRACE(TICKET, ("NatCreateTicket: collision %016I64X:%016I64X\n",
|
||
Key, RemoteKey));
|
||
NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize the ticket
|
||
//
|
||
|
||
Ticket = ALLOCATE_TICKET_BLOCK();
|
||
if (!Ticket) {
|
||
ERROR(("NatCreateTicket: ticket could not be allocated\n"));
|
||
NatDereferenceAddressPoolEntry(Interfacep, AddressToUse);
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
Ticket->Key = Key;
|
||
Ticket->RemoteKey = RemoteKey;
|
||
Ticket->Flags = Flags;
|
||
Ticket->UsedAddress = AddressToUse;
|
||
Ticket->PrivateAddress = PrivateAddress;
|
||
if (NAT_TICKET_IS_RANGE(Ticket)) {
|
||
Ticket->PrivateOrHostOrderEndPort = NTOHS(PrivateOrEndPort);
|
||
} else {
|
||
Ticket->PrivateOrHostOrderEndPort = PrivateOrEndPort;
|
||
}
|
||
InsertTailList(InsertionPoint, &Ticket->Link);
|
||
KeQueryTickCount((PLARGE_INTEGER)&Ticket->LastAccessTime);
|
||
|
||
InterlockedIncrement(&TicketCount);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatCreateTicket
|
||
|
||
|
||
VOID
|
||
NatDeleteAnyAssociatedDynamicTicket(
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked when cleanup is in progress for a file-object
|
||
opened for \Device\IpNat. It deletes any dynamic tickets associated with
|
||
the file-object.
|
||
|
||
Arguments:
|
||
|
||
FileObject - the file-object being cleaned up
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
PLIST_ENTRY Link;
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
CALLTRACE(("NatDeleteAnyAssociatedDynamicTicket\n"));
|
||
KeAcquireSpinLock(&DynamicTicketLock, &Irql);
|
||
for (Link = DynamicTicketList.Flink; Link != &DynamicTicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_DYNAMIC_TICKET, Link);
|
||
if (Ticket->FileObject != FileObject) { continue; }
|
||
Link = Link->Blink;
|
||
RemoveEntryList(&Ticket->Link);
|
||
ExFreePool(Ticket);
|
||
InterlockedDecrement(&DynamicTicketCount);
|
||
}
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
} // NatDeleteAnyAssociatedDynamicTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatDeleteDynamicTicket(
|
||
PIP_NAT_DELETE_DYNAMIC_TICKET DeleteTicket,
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked when an 'IOCTL_IP_DELETE_DYNAMIC_TICKET' request
|
||
is issued to delete a dynamic ticket.
|
||
|
||
Arguments:
|
||
|
||
DeleteTicket - specifies the ticket to be deleted
|
||
|
||
FileObject - specifies the file-object in which the request was issued
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY InsertionPoint;
|
||
KIRQL Irql;
|
||
ULONG Key;
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
CALLTRACE(("NatDeleteDynamicTicket\n"));
|
||
|
||
Key = MAKE_DYNAMIC_TICKET_KEY(DeleteTicket->Protocol, DeleteTicket->Port);
|
||
KeAcquireSpinLock(&DynamicTicketLock, &Irql);
|
||
if (!(Ticket = NatLookupDynamicTicket(Key, &InsertionPoint))) {
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
return STATUS_UNSUCCESSFUL;
|
||
} else if (Ticket->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
RemoveEntryList(&Ticket->Link);
|
||
ExFreePool(Ticket);
|
||
KeReleaseSpinLock(&DynamicTicketLock, Irql);
|
||
InterlockedDecrement(&DynamicTicketCount);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // NatDeleteDynamicTicket
|
||
|
||
|
||
VOID
|
||
NatDeleteTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
PNAT_TICKET Ticket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to delete a NAT ticket.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the ticket's interface
|
||
|
||
Ticket - the ticket to be deleted
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
InterlockedDecrement(&TicketCount);
|
||
RemoveEntryList(&Ticket->Link);
|
||
NatDereferenceAddressPoolEntry(Interfacep, Ticket->UsedAddress);
|
||
FREE_TICKET_BLOCK(Ticket);
|
||
|
||
} // NatDeleteTicket
|
||
|
||
|
||
VOID
|
||
NatInitializeDynamicTicketManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize state used for managing
|
||
dynamic tickets.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
CALLTRACE(("NatInitializeDynamicTicketManagement\n"));
|
||
InitializeListHead(&DynamicTicketList);
|
||
KeInitializeSpinLock(&DynamicTicketLock);
|
||
DynamicTicketCount = 0;
|
||
} // NatInitializeDynamicTicketManagement
|
||
|
||
|
||
BOOLEAN
|
||
NatIsPortUsedByTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
UCHAR Protocol,
|
||
USHORT PublicPort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the interface's ticket-list to see if the given port
|
||
is in use as the public port of any ticket.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface whose ticket list is to be searched
|
||
|
||
Protocol - indicates either TCP or UDP
|
||
|
||
PublicPort - the port for which to search
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the port is in use, FALSE otherwise
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
USHORT HostOrderPort;
|
||
ULONG64 Key;
|
||
PNAT_TICKET Ticket;
|
||
|
||
TRACE(TICKET, ("NatIsPortUsedByTicket\n"));
|
||
|
||
HostOrderPort = NTOHS(PublicPort);
|
||
Key = MAKE_TICKET_KEY(Protocol, 0, PublicPort);
|
||
for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
|
||
if (NAT_TICKET_IS_RANGE(Ticket)) {
|
||
if (HostOrderPort > Ticket->PrivateOrHostOrderEndPort) {
|
||
continue;
|
||
} else if (HostOrderPort < NTOHS(TICKET_PORT(Ticket->Key))) {
|
||
continue;
|
||
}
|
||
} else if (Key != (Ticket->Key & MAKE_TICKET_KEY(~0,0,~0))) {
|
||
continue;
|
||
}
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
} // NatIsPortUsedByTicket
|
||
|
||
|
||
VOID
|
||
NatLookupAndApplyDynamicTicket(
|
||
UCHAR Protocol,
|
||
USHORT DestinationPort,
|
||
PNAT_INTERFACE Interfacep,
|
||
ULONG PublicAddress,
|
||
ULONG PrivateAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to determine whether there is a dynamic ticket
|
||
which should be activated for the given outbound session.
|
||
|
||
Arguments:
|
||
|
||
Protocol - the protocol of the outbound session
|
||
|
||
DestinationPort - the destination port of the outbound session
|
||
|
||
Interfacep - the interface across which the outbound session
|
||
will be translated
|
||
|
||
PublicAddress - the public address used by the outbound session's mapping
|
||
|
||
PrivateAddress - the private address of the outbound session's mapping
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
Environment:
|
||
|
||
Invoked at dispatch level with neither 'Interfacep->Lock' nor
|
||
'DynamicTicketLock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_USED_ADDRESS AddressToUse;
|
||
ULONG i;
|
||
ULONG Key;
|
||
USHORT PublicPort;
|
||
NTSTATUS status;
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
|
||
Key = MAKE_DYNAMIC_TICKET_KEY(Protocol, DestinationPort);
|
||
KeAcquireSpinLockAtDpcLevel(&DynamicTicketLock);
|
||
if (!(Ticket = NatLookupDynamicTicket(Key, NULL))) {
|
||
KeReleaseSpinLockFromDpcLevel(&DynamicTicketLock);
|
||
return;
|
||
}
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
for (i = 0; i < Ticket->ResponseCount; i++) {
|
||
status =
|
||
NatAcquireFromAddressPool(
|
||
Interfacep,
|
||
PrivateAddress,
|
||
0,
|
||
&AddressToUse
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
NatCreateTicket(
|
||
Interfacep,
|
||
Ticket->ResponseArray[i].Protocol,
|
||
PrivateAddress,
|
||
Ticket->ResponseArray[i].EndPort,
|
||
0,
|
||
0,
|
||
NAT_TICKET_FLAG_IS_RANGE,
|
||
AddressToUse,
|
||
Ticket->ResponseArray[i].StartPort,
|
||
&PublicAddress,
|
||
&PublicPort
|
||
);
|
||
}
|
||
}
|
||
KeReleaseSpinLockFromDpcLevel(&Interfacep->Lock);
|
||
KeReleaseSpinLockFromDpcLevel(&DynamicTicketLock);
|
||
} // NatLookupAndApplyDynamicTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatLookupAndDeleteTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
ULONG64 Key,
|
||
ULONG64 RemoteKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks for a ticket with the specified key and, if found,
|
||
removes and deallocates the ticket after releasing its address and port.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to look for the ticket
|
||
|
||
Key - the ticket to look for
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_TICKET Ticket;
|
||
|
||
TRACE(TICKET, ("NatLookupAndDeleteTicket\n"));
|
||
|
||
//
|
||
// Look for the ticket
|
||
//
|
||
|
||
Ticket = NatLookupTicket(Interfacep, Key, RemoteKey, NULL);
|
||
if (Ticket) {
|
||
NatDeleteTicket(Interfacep, Ticket);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
|
||
} // NatLookupAndDeleteTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatLookupAndRemoveTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
ULONG64 Key,
|
||
ULONG64 RemoteKey,
|
||
PNAT_USED_ADDRESS* UsedAddress,
|
||
PULONG PrivateAddress,
|
||
PUSHORT PrivatePort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks for a ticket with the specified key and, if found,
|
||
removes and deallocates the ticket after storing the private address/port
|
||
for the ticket in the caller's arguments.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to look for the ticket
|
||
|
||
Key - the public key of the ticket to look for
|
||
|
||
UsedAddress - receives a pointer to the public-address used by the ticket
|
||
|
||
PrivateAddress - receives the address to which the ticket grants access
|
||
|
||
PrivatePort - receives the port to which the ticket grants access
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
USHORT HostOrderPort;
|
||
PNAT_TICKET Ticket;
|
||
ULONG RemoteAddress;
|
||
USHORT RemotePort;
|
||
|
||
TRACE(PER_PACKET, ("NatLookupAndRemoveTicket\n"));
|
||
|
||
//
|
||
// Look for the ticket.
|
||
//
|
||
|
||
HostOrderPort = NTOHS(TICKET_PORT(Key));
|
||
for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
|
||
if (NAT_TICKET_IS_RANGE(Ticket)) {
|
||
if (HostOrderPort > Ticket->PrivateOrHostOrderEndPort) {
|
||
continue;
|
||
} else if (HostOrderPort < NTOHS(TICKET_PORT(Ticket->Key))) {
|
||
continue;
|
||
}
|
||
} else if (Key != Ticket->Key) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Primary key matches, also need to check remote key.
|
||
//
|
||
|
||
if (RemoteKey != Ticket->RemoteKey) {
|
||
|
||
//
|
||
// Handle cases where remote key wasn't specified
|
||
//
|
||
|
||
RemoteAddress = TICKET_ADDRESS(Ticket->RemoteKey);
|
||
if (RemoteAddress != 0
|
||
&& RemoteAddress != TICKET_ADDRESS(RemoteKey)) {
|
||
continue;
|
||
}
|
||
|
||
RemotePort = TICKET_PORT(Ticket->RemoteKey);
|
||
if (RemotePort != 0
|
||
&& RemotePort != TICKET_PORT(RemoteKey)) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This is the ticket
|
||
//
|
||
|
||
*UsedAddress = Ticket->UsedAddress;
|
||
*PrivateAddress = Ticket->PrivateAddress;
|
||
if (NAT_TICKET_IS_RANGE(Ticket)) {
|
||
*PrivatePort = TICKET_PORT(Key);
|
||
} else {
|
||
*PrivatePort = Ticket->PrivateOrHostOrderEndPort;
|
||
}
|
||
|
||
if (!NAT_TICKET_PERSISTENT(Ticket)) {
|
||
InterlockedDecrement(&TicketCount);
|
||
RemoveEntryList(&Ticket->Link);
|
||
FREE_TICKET_BLOCK(Ticket);
|
||
} else {
|
||
|
||
//
|
||
// Reference the ticket's address again for the next use
|
||
//
|
||
|
||
NatReferenceAddressPoolEntry(Ticket->UsedAddress);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
|
||
} // NatLookupAndRemoveTicket
|
||
|
||
|
||
PNAT_DYNAMIC_TICKET
|
||
NatLookupDynamicTicket(
|
||
ULONG Key,
|
||
PLIST_ENTRY *InsertionPoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to look for a dynamic ticket with the given key.
|
||
|
||
Arguments:
|
||
|
||
Key - the key for the dynamic ticket to be found
|
||
|
||
InsertionPoint - if the ticket is not found, receives the insertion point
|
||
|
||
Return Value:
|
||
|
||
PNAT_DYNAMIC_TICKET - the dynamic ticket, if found
|
||
|
||
Environment:
|
||
|
||
Invoked with 'DynamicTicketLock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
TRACE(TICKET, ("NatLookupDynamicTicket\n"));
|
||
|
||
for (Link = DynamicTicketList.Flink; Link != &DynamicTicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_DYNAMIC_TICKET, Link);
|
||
if (Key > Ticket->Key) {
|
||
continue;
|
||
} else if (Key < Ticket->Key) {
|
||
break;
|
||
}
|
||
return Ticket;
|
||
}
|
||
|
||
if (InsertionPoint) { *InsertionPoint = Link; }
|
||
return NULL;
|
||
|
||
} // NatLookupDynamicTicket
|
||
|
||
|
||
PNAT_TICKET
|
||
NatLookupFirewallTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
UCHAR Protocol,
|
||
USHORT Port
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to look for a firewall ticket with the given
|
||
protocol and port. A firewall ticket must:
|
||
* have the same public and private address
|
||
* have the same public and private port
|
||
* be marked persistent
|
||
* not be a range
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to search for a ticket
|
||
|
||
Protocol - the protocl for the ticket to be found
|
||
|
||
Port - the port for the ticket to be found
|
||
|
||
Return Value:
|
||
|
||
PNAT_TICKET - the ticket, if found
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
PNAT_TICKET Ticket;
|
||
TRACE(TICKET, ("NatLookupFirewallTicket\n"));
|
||
|
||
for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
|
||
|
||
if (Protocol == TICKET_PROTOCOL(Ticket->Key)
|
||
&& Port == TICKET_PORT(Ticket->Key)
|
||
&& Port == Ticket->PrivateOrHostOrderEndPort
|
||
&& Ticket->PrivateAddress == TICKET_ADDRESS(Ticket->Key)
|
||
&& NAT_TICKET_PERSISTENT(Ticket)
|
||
&& !NAT_TICKET_IS_RANGE(Ticket)) {
|
||
|
||
return Ticket;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
} // NatLookupFirewallTicket
|
||
|
||
|
||
PNAT_TICKET
|
||
NatLookupTicket(
|
||
PNAT_INTERFACE Interfacep,
|
||
ULONG64 Key,
|
||
ULONG64 RemoteKey,
|
||
PLIST_ENTRY *InsertionPoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to look for a ticket with the given key.
|
||
|
||
Arguments:
|
||
|
||
Interfacep - the interface on which to search for a ticket
|
||
|
||
Key - the key for the ticket to be found
|
||
|
||
InsertionPoint - if the ticket is not found, receives the insertion point
|
||
|
||
Return Value:
|
||
|
||
PNAT_TICKET - the ticket, if found
|
||
|
||
Environment:
|
||
|
||
Invoked with 'Interfacep->Lock' held by the caller.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
PNAT_TICKET Ticket;
|
||
TRACE(TICKET, ("NatLookupTicket\n"));
|
||
|
||
for (Link = Interfacep->TicketList.Flink; Link != &Interfacep->TicketList;
|
||
Link = Link->Flink) {
|
||
Ticket = CONTAINING_RECORD(Link, NAT_TICKET, Link);
|
||
if (Key > Ticket->Key) {
|
||
continue;
|
||
} else if (Key < Ticket->Key) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Primary keys match, check secondary.
|
||
//
|
||
|
||
if (RemoteKey > Ticket->RemoteKey) {
|
||
continue;
|
||
} else if (RemoteKey < Ticket->RemoteKey) {
|
||
break;
|
||
}
|
||
|
||
return Ticket;
|
||
}
|
||
|
||
if (InsertionPoint) { *InsertionPoint = Link; }
|
||
return NULL;
|
||
|
||
} // NatLookupTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatProcessCreateTicket(
|
||
PIP_NAT_CREATE_TICKET CreateTicket,
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to create a ticket in response to an
|
||
'IOCTL_IP_NAT_CREATE_TICKET' request.
|
||
|
||
Arguments:
|
||
|
||
CreateTicket - describes the ticket to be created
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
NTSTATUS Status;
|
||
|
||
TRACE(TICKET, ("NatProcessCreateTicket\n"));
|
||
|
||
//
|
||
// Validate the parameters
|
||
//
|
||
|
||
if (0 == CreateTicket->InterfaceIndex
|
||
|| INVALID_IF_INDEX == CreateTicket->InterfaceIndex
|
||
|| (NAT_PROTOCOL_TCP != CreateTicket->PortMapping.Protocol
|
||
&& NAT_PROTOCOL_UDP != CreateTicket->PortMapping.Protocol)
|
||
|| 0 == CreateTicket->PortMapping.PublicPort
|
||
|| 0 == CreateTicket->PortMapping.PrivatePort
|
||
|| 0 == CreateTicket->PortMapping.PrivateAddress) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Lookup and reference the interface
|
||
//
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
|
||
Interfacep = NatLookupInterface(CreateTicket->InterfaceIndex, NULL);
|
||
|
||
if (NULL == Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (Interfacep->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
if (!NatReferenceInterface(Interfacep)) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
|
||
//
|
||
// Create the actual ticket
|
||
//
|
||
|
||
Status =
|
||
NatCreateStaticPortMapping(
|
||
Interfacep,
|
||
&CreateTicket->PortMapping
|
||
);
|
||
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
NatDereferenceInterface(Interfacep);
|
||
|
||
return Status;
|
||
} // NatProcessCreateTicket
|
||
|
||
|
||
NTSTATUS
|
||
NatProcessDeleteTicket(
|
||
PIP_NAT_CREATE_TICKET DeleteTicket,
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to delete a ticket in response to an
|
||
'IOCTL_IP_NAT_DELETE_TICKET' request.
|
||
|
||
Arguments:
|
||
|
||
DeleteTicket - describes the ticket to be created
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
ULONG64 Key;
|
||
ULONG64 RemoteKey;
|
||
NTSTATUS Status;
|
||
PNAT_TICKET Ticketp;
|
||
PNAT_USED_ADDRESS Usedp;
|
||
|
||
TRACE(TICKET, ("NatProcessDeleteTicket\n"));
|
||
|
||
//
|
||
// Validate the parameters
|
||
//
|
||
|
||
if (0 == DeleteTicket->InterfaceIndex
|
||
|| INVALID_IF_INDEX == DeleteTicket->InterfaceIndex
|
||
|| (NAT_PROTOCOL_TCP != DeleteTicket->PortMapping.Protocol
|
||
&& NAT_PROTOCOL_UDP != DeleteTicket->PortMapping.Protocol)
|
||
|| 0 == DeleteTicket->PortMapping.PublicPort) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
RemoteKey =
|
||
MAKE_TICKET_KEY(
|
||
DeleteTicket->PortMapping.Protocol,
|
||
0,
|
||
0
|
||
);
|
||
|
||
//
|
||
// Lookup and reference the interface
|
||
//
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
|
||
Interfacep = NatLookupInterface(DeleteTicket->InterfaceIndex, NULL);
|
||
|
||
if (NULL == Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (Interfacep->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
if (!NatReferenceInterface(Interfacep)) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
|
||
//
|
||
// If the caller didn't specify a public address we need
|
||
// to use the address of the interface itself
|
||
//
|
||
|
||
if (!DeleteTicket->PortMapping.PublicAddress) {
|
||
Status =
|
||
NatAcquireFromAddressPool(
|
||
Interfacep,
|
||
DeleteTicket->PortMapping.PrivateAddress,
|
||
0,
|
||
&Usedp
|
||
);
|
||
if (NT_SUCCESS(Status)) {
|
||
DeleteTicket->PortMapping.PublicAddress = Usedp->PublicAddress;
|
||
NatDereferenceAddressPoolEntry(Interfacep, Usedp);
|
||
}
|
||
}
|
||
|
||
Key =
|
||
MAKE_TICKET_KEY(
|
||
DeleteTicket->PortMapping.Protocol,
|
||
DeleteTicket->PortMapping.PublicAddress,
|
||
DeleteTicket->PortMapping.PublicPort
|
||
);
|
||
|
||
//
|
||
// Search for the ticket on the interface, and delete
|
||
// it if found.
|
||
//
|
||
|
||
Ticketp =
|
||
NatLookupTicket(
|
||
Interfacep,
|
||
Key,
|
||
RemoteKey,
|
||
NULL
|
||
);
|
||
|
||
if (NULL != Ticketp) {
|
||
NatDeleteTicket(Interfacep, Ticketp);
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_NOT_FOUND;
|
||
}
|
||
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
NatDereferenceInterface(Interfacep);
|
||
|
||
return Status;
|
||
} // NatProcessDeleteTicket
|
||
|
||
NTSTATUS
|
||
NatProcessLookupTicket(
|
||
PIP_NAT_CREATE_TICKET LookupTicket,
|
||
PIP_NAT_PORT_MAPPING Ticket,
|
||
PFILE_OBJECT FileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to lookup a ticket in response to an
|
||
'IOCTL_IP_NAT_LOOKUP_TICKET' request.
|
||
|
||
Arguments:
|
||
|
||
LookupTicket - describes the ticket to search for
|
||
|
||
Ticket - Receives the information about the ticket, if found
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_INTERFACE Interfacep;
|
||
KIRQL Irql;
|
||
ULONG64 Key;
|
||
IP_NAT_PORT_MAPPING PortMapping;
|
||
ULONG64 RemoteKey;
|
||
NTSTATUS Status;
|
||
PNAT_TICKET Ticketp;
|
||
|
||
TRACE(TICKET, ("NatProcessLookupTicket\n"));
|
||
|
||
//
|
||
// Validate the parameters
|
||
//
|
||
|
||
if (0 == LookupTicket->InterfaceIndex
|
||
|| INVALID_IF_INDEX == LookupTicket->InterfaceIndex
|
||
|| (NAT_PROTOCOL_TCP != LookupTicket->PortMapping.Protocol
|
||
&& NAT_PROTOCOL_UDP != LookupTicket->PortMapping.Protocol)
|
||
|| 0 == LookupTicket->PortMapping.PublicPort) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
RemoteKey =
|
||
MAKE_TICKET_KEY(
|
||
LookupTicket->PortMapping.Protocol,
|
||
0,
|
||
0
|
||
);
|
||
|
||
//
|
||
// Lookup and reference the interface
|
||
//
|
||
|
||
KeAcquireSpinLock(&InterfaceLock, &Irql);
|
||
|
||
Interfacep = NatLookupInterface(LookupTicket->InterfaceIndex, NULL);
|
||
|
||
if (NULL == Interfacep) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (Interfacep->FileObject != FileObject) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
if (!NatReferenceInterface(Interfacep)) {
|
||
KeReleaseSpinLock(&InterfaceLock, Irql);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&InterfaceLock);
|
||
KeAcquireSpinLockAtDpcLevel(&Interfacep->Lock);
|
||
|
||
//
|
||
// If the caller didn't specify a public address we need
|
||
// to use the address of the interface itself
|
||
//
|
||
|
||
if (!LookupTicket->PortMapping.PublicAddress
|
||
&& Interfacep->AddressCount > 0) {
|
||
|
||
LookupTicket->PortMapping.PublicAddress =
|
||
Interfacep->AddressArray[0].Address;
|
||
}
|
||
|
||
Key =
|
||
MAKE_TICKET_KEY(
|
||
LookupTicket->PortMapping.Protocol,
|
||
LookupTicket->PortMapping.PublicAddress,
|
||
LookupTicket->PortMapping.PublicPort
|
||
);
|
||
|
||
//
|
||
// Search for the ticket on the interface.
|
||
//
|
||
|
||
Ticketp =
|
||
NatLookupTicket(
|
||
Interfacep,
|
||
Key,
|
||
RemoteKey,
|
||
NULL
|
||
);
|
||
|
||
if (NULL != Ticketp) {
|
||
|
||
//
|
||
// We can't write into the output buffer while holding any
|
||
// locks, since that buffer may be paged out. Copy the
|
||
// information from the ticket into a local port mapping
|
||
// structure.
|
||
//
|
||
|
||
PortMapping.Protocol = TICKET_PROTOCOL(Ticketp->Key);
|
||
PortMapping.PublicAddress = TICKET_ADDRESS(Ticketp->Key);
|
||
PortMapping.PublicPort = TICKET_PORT(Ticketp->Key);
|
||
PortMapping.PrivateAddress = Ticketp->PrivateAddress;
|
||
PortMapping.PrivatePort = Ticketp->PrivateOrHostOrderEndPort;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
Status = STATUS_NOT_FOUND;
|
||
}
|
||
|
||
KeReleaseSpinLock(&Interfacep->Lock, Irql);
|
||
NatDereferenceInterface(Interfacep);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Copy the port mapping information into the
|
||
// output buffer.
|
||
//
|
||
|
||
__try {
|
||
RtlCopyMemory(Ticket, &PortMapping, sizeof(*Ticket));
|
||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
} // NatProcessLookupTicket
|
||
|
||
|
||
VOID
|
||
NatShutdownDynamicTicketManagement(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to cleanup resources used for managing
|
||
dynamic tickets.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNAT_DYNAMIC_TICKET Ticket;
|
||
CALLTRACE(("NatShutdownDynamicTicketManagement\n"));
|
||
while (!IsListEmpty(&DynamicTicketList)) {
|
||
Ticket =
|
||
CONTAINING_RECORD(
|
||
DynamicTicketList.Flink, NAT_DYNAMIC_TICKET, Link
|
||
);
|
||
RemoveEntryList(&Ticket->Link);
|
||
ExFreePool(Ticket);
|
||
}
|
||
DynamicTicketCount = 0;
|
||
} // NatShutdownDynamicTicketManagement
|
||
|