/*++ 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