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.
1374 lines
33 KiB
1374 lines
33 KiB
/*++
|
|
|
|
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
|
|
|