Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1532 lines
38 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. iislbdr.cxx
  5. Abstract:
  6. This module implements NAT entry points
  7. Author:
  8. Philippe Choquier ( phillich )
  9. --*/
  10. extern "C" {
  11. //#include <nt.h>
  12. //#include <ntrtl.h>
  13. //#include <nturtl.h>
  14. //#include <ntsecapi.h>
  15. //#include <ntddnetd.h>
  16. //#include <ntverp.h>
  17. #define NOGDICAPMASKS
  18. #define NOVIRTUALKEYCODES
  19. #define NOWINMESSAGES
  20. #define NOWINSTYLES
  21. #define NOSYSMETRICS
  22. #define NOMENUS
  23. #define NOICONS
  24. #define NOKEYSTATES
  25. #define NOSYSCOMMANDS
  26. #define NORASTEROPS
  27. #define NOSHOWWINDOW
  28. #define OEMRESOURCE
  29. #define NOATOM
  30. #define NOCLIPBOARD
  31. #define NOCOLOR
  32. #define NOCTLMGR
  33. #define NODRAWTEXT
  34. #define NOGDI
  35. #define NOKERNEL
  36. #if defined(KERNEL_MODE)
  37. #define NOUSER
  38. #endif
  39. #define NONLS
  40. #define NOMB
  41. #define NOMEMMGR
  42. #define NOMETAFILE
  43. #define NOMINMAX
  44. #define NOMSG
  45. #define NOOPENFILE
  46. #define NOSCROLL
  47. #define NOSERVICE
  48. #define NOSOUND
  49. #define NOTEXTMETRIC
  50. #define NOWH
  51. #define NOWINOFFSETS
  52. #define NOCOMM
  53. #define NOKANJI
  54. #define NOHELP
  55. #define NOPROFILER
  56. #define NODEFERWINDOWPOS
  57. #if defined(KERNEL_MODE)
  58. #include <strmini.h>
  59. #else
  60. #include <windows.h>
  61. #include <wdbgexts.h>
  62. #endif
  63. #include <stdlib.h>
  64. #include <ipnat.h>
  65. #include "iisnatio.h"
  66. #include <bootexp.hxx>
  67. }
  68. #pragma intrinsic(memcmp)
  69. #include <iislbh.hxx>
  70. #if DBG
  71. #define NOISY_DBG 1
  72. #else
  73. #define NOISY_DBG 0
  74. #endif
  75. //
  76. // Private strutures
  77. //
  78. #pragma pack(1)
  79. typedef struct _HASH_BUCKET
  80. {
  81. ULONG m_HashEntry;
  82. ULONG m_CheckExpiry;
  83. } HASH_BUCKET ;
  84. typedef struct _HASH_ENTRY
  85. {
  86. ULONG m_RemoteAddress;
  87. USHORT m_PublicRef;
  88. USHORT m_PrivateRef;
  89. ULONG m_NextHashEntry;
  90. ULONG m_Expiry;
  91. } HASH_ENTRY;
  92. #pragma pack()
  93. #define NB_BUCKETS 251
  94. #define HASH_ALLOC_GRAIN 64
  95. #define EOCHAIN 0xffff
  96. #define CHECK_EXPIRY 30 // in seconds
  97. //
  98. // Define macros allowing common source code for user & kernel mode
  99. // This allows the mapping to be tested in user mode
  100. //
  101. #if defined(KERNEL_MODE)
  102. #define DECLARE_LOCK_OBJECT(a) KSPIN_LOCK a
  103. #define INIT_LOCK_OBJECT(a) KeInitializeSpinLock(a)
  104. #define TERMINATE_LOCK_OBJECT(a)
  105. #define LockIpMap() KeAcquireSpinLock(&IpMapLock, &oldIrql )
  106. #define UnlockIpMap() KeReleaseSpinLock(&IpMapLock, oldIrql )
  107. #define LockIpMapAtDpcLevel() KeAcquireSpinLock(&IpMapLock, &oldIrql)
  108. #define UnlockIpMapFromDpcLevel() KeReleaseSpinLock(&IpMapLock, oldIrql)
  109. //#define LockIpMapAtDpcLevel() KeAcquireSpinLockAtDpcLevel(&IpMapLock)
  110. //#define UnlockIpMapFromDpcLevel() KeReleaseSpinLockFromDpcLevel(&IpMapLock)
  111. #define AllocBuffer(a) ExAllocatePool( NonPagedPool, a )
  112. #define FreeBuffer(a) ExFreePool( a )
  113. #define Copy_Memory(a,b,c) RtlCopyBytes(a,b,c)
  114. #define Equal_Memory(a,b,c) RtlEqualMemory(a,b,c)
  115. #define DeclareTimeObject(a) ULONGLONG a
  116. #define GetTime(a,b) KeQuerySystemTime( (PLARGE_INTEGER)&a ); b = (ULONG)(a / (ULONGLONG)(10*1000*1000))
  117. #else
  118. #include <time.h>
  119. #define DECLARE_LOCK_OBJECT(a) CRITICAL_SECTION a
  120. #define INIT_LOCK_OBJECT(a)
  121. #define TERMINATE_LOCK_OBJECT(a)
  122. #define LockIpMap()
  123. #define UnlockIpMap()
  124. #define LockIpMapAtDpcLevel()
  125. #define UnlockIpMapFromDpcLevel()
  126. #define AllocBuffer(a) LocalAlloc( LMEM_FIXED, a)
  127. #define FreeBuffer(a) LocalFree( a )
  128. #define Copy_Memory(a,b,c) memcpy(a,b,c)
  129. #define Equal_Memory(a,b,c) 0 // !memcmp(a,b,c)
  130. #define DeclareTimeObject(a)
  131. #define GetTime(a,b) b = time(NULL)
  132. #undef IoCompleteRequest
  133. #define IoCompleteRequest(a,b)
  134. #undef ProbeForRead
  135. #define ProbeForRead(a,b,c)
  136. #define IoDeleteSymbolicLink(a)
  137. #define IoDeleteDevice(a)
  138. #define IoCreateSymbolicLink(a,b) STATUS_SUCCESS
  139. #define IoCreateDevice(a,b,c,d,e,f,g) STATUS_SUCCESS
  140. #define IoGetDeviceObjectPointer(a,b,c,d) (*c=NULL,*d=NULL,STATUS_SUCCESS)
  141. #define IoBuildDeviceIoControlRequest(a,b,c,d,e,f,g,h,i) (PIRP)1
  142. #define ObfDereferenceObject(a)
  143. #include <stdio.h>
  144. #include <stdarg.h>
  145. ULONG
  146. _cdecl
  147. DbgPrint(
  148. PCH Format,
  149. ...
  150. )
  151. {
  152. va_list marker;
  153. CHAR achD[128];
  154. va_start( marker, Format );
  155. wvsprintf( achD, Format, marker );
  156. OutputDebugString( achD );
  157. va_end( marker );
  158. return 0;
  159. }
  160. #endif
  161. //
  162. // Private prototypes
  163. //
  164. NTSTATUS
  165. NotifyPort(
  166. CKernelIpEndpointEx* pEndp
  167. );
  168. NTSTATUS
  169. UnnotifyPort(
  170. CKernelIpEndpointEx* pEndp
  171. );
  172. //
  173. // Globals
  174. //
  175. HASH_BUCKET HashBuckets[NB_BUCKETS];
  176. HASH_ENTRY* HashEntries;
  177. unsigned int cHashEntries;
  178. unsigned int iFirstFreeHashEntry;
  179. CKernelIpMapMinHelper IpMap;
  180. ULONG iCurrentServer;
  181. PNAT_HELPER_QUERY_INFO_SESSION pQueryInfoSession;
  182. PNAT_HELPER_DEREGISTER_DIRECTOR pDeregisterNat;
  183. PDEVICE_OBJECT NatDeviceObject;
  184. PFILE_OBJECT NatFileObject;
  185. DECLARE_LOCK_OBJECT(IpMapLock);
  186. //
  187. // NAT functions
  188. //
  189. extern "C"
  190. NTSTATUS
  191. NatRegisterSessionControl(
  192. IN ULONG Version
  193. )
  194. /*++
  195. NatRegisterSessionControl
  196. Routine Description:
  197. This routine is exported by the session-control module.
  198. It is invoked by the NAT to determine whether the module will be
  199. directing incoming sessions.
  200. Arguments:
  201. Version - the version of the NAT which is running.
  202. Return Value:
  203. STATUS_SUCCESS if the module wishes to direct incoming sessions,
  204. error code otherwise.
  205. --*/
  206. {
  207. DeclareTimeObject( RegSes );
  208. ULONG Now;
  209. ULONG i;
  210. GetTime( RegSes, Now );
  211. HashEntries = NULL;
  212. IpMap.SetBuffer( NULL );
  213. iFirstFreeHashEntry = EOCHAIN;
  214. cHashEntries = 0;
  215. iCurrentServer = 0;
  216. for ( i = 0 ; i < NB_BUCKETS ; ++i )
  217. {
  218. HashBuckets[i].m_HashEntry = EOCHAIN;
  219. HashBuckets[i].m_CheckExpiry = Now + CHECK_EXPIRY;
  220. }
  221. INIT_LOCK_OBJECT(&IpMapLock);
  222. pQueryInfoSession = NULL;
  223. pDeregisterNat = NULL;
  224. #if NOISY_DBG
  225. DbgPrint( "NatRegisterSessionControl:\n" );
  226. #endif
  227. return STATUS_SUCCESS;
  228. }
  229. extern "C"
  230. NTSTATUS
  231. NatIoctlSessionControl(
  232. IN PIRP Irp
  233. )
  234. /*++
  235. NatIoctlSessionControl
  236. Routine Description:
  237. It handles IOCTL_IISNATIO_SET_CONFIG IRP, dispatched from IISNATDispatchIoctl()
  238. It is also exported to be called directly when called from user mode
  239. The module should treat the IRP as if it were received in an actual
  240. dispatch routine; the NAT does no pre-processing or post-processing,
  241. so the module must update the IRP on its own.
  242. Arguments:
  243. Irp - the IRP received by the NAT from the I/O manager.
  244. Return Value:
  245. The status code to be returned from the NAT's dispatch routine.
  246. --*/
  247. {
  248. ULONG cNewIpMapSize;
  249. LPVOID NewIpMap;
  250. HASH_ENTRY* pTopHash;
  251. HASH_ENTRY* pHash;
  252. CKernelIpMapMinHelper NewIpMapHelper;
  253. UINT iOld;
  254. UINT iNew;
  255. UINT iHash;
  256. UINT iF;
  257. UINT iPrevF;
  258. UINT iNext;
  259. DWORD iNewPub;
  260. DWORD iNewPrv;
  261. BOOL fUpdate = FALSE;
  262. PIO_STACK_LOCATION IrpSp;
  263. NTSTATUS status = STATUS_SUCCESS;
  264. KIRQL oldIrql;
  265. CKernelIpEndpointEx* pOld;
  266. CKernelIpEndpointEx* pNew;
  267. UINT cPublicIp;
  268. UINT cPrivateIp;
  269. IPREF* pIp;
  270. IPREF* pTopIp;
  271. UINT cSize;
  272. UINT iIpRef;
  273. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  274. cNewIpMapSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
  275. NewIpMap = AllocBuffer( cNewIpMapSize );
  276. if ( NewIpMap == NULL )
  277. {
  278. status = STATUS_NO_MEMORY;
  279. goto complete;
  280. }
  281. NewIpMapHelper.SetBuffer( NewIpMap );
  282. //
  283. // Copy over the user's buffer into our information structure.
  284. //
  285. Copy_Memory( NewIpMap,
  286. Irp->AssociatedIrp.SystemBuffer,
  287. cNewIpMapSize );
  288. //
  289. // Configuration validation
  290. //
  291. if ( NewIpMapHelper.GetSize() != cNewIpMapSize )
  292. {
  293. #if NOISY_DBG
  294. DbgPrint( "NatIoctlSessionControl: size mismatch: %u %u\n", NewIpMapHelper.GetSize(), cNewIpMapSize );
  295. #endif
  296. FreeBuffer( NewIpMap );
  297. status = STATUS_INVALID_PARAMETER;
  298. goto complete;
  299. }
  300. cSize = NewIpMapHelper.GetKernelServerDescriptionSize();
  301. pIp = NewIpMapHelper.GetPrivateIpRef( NewIpMapHelper.GetServerPtr( 0 ), 0 );
  302. pTopIp = (IPREF *)((LPBYTE)pIp + cSize * NewIpMapHelper.ServerCount());
  303. cPublicIp = NewIpMapHelper.PublicIpCount();
  304. cPrivateIp = NewIpMapHelper.PrivateIpCount();
  305. //
  306. // Check that private IP references are in range
  307. //
  308. for ( ;
  309. pIp < pTopIp ;
  310. pIp = (IPREF*)(((LPBYTE)pIp) + cSize) )
  311. {
  312. for ( iIpRef = 0 ; iIpRef < cPublicIp ; ++iIpRef )
  313. {
  314. if ( pIp[iIpRef] >= cPrivateIp &&
  315. pIp[iIpRef] != -1 )
  316. {
  317. #if NOISY_DBG
  318. DbgPrint( "NatIoctlSessionControl: invalid private IP ref: %u max is %u\n",
  319. pIp[iIpRef],
  320. cPrivateIp );
  321. #endif
  322. FreeBuffer( NewIpMap );
  323. status = STATUS_INVALID_PARAMETER;
  324. goto complete;
  325. }
  326. }
  327. }
  328. //
  329. // check if existing configuration not empty. If empty then no cache update
  330. // necessary : there is no mapping to update
  331. //
  332. if ( IpMap.GetBuffer() )
  333. {
  334. //
  335. // Check if each unique port in old cnfg is present in new cnfg
  336. // if present in both then nothing to do
  337. // if present in old but not in new then unnotify NAT
  338. // if present in new but not in old then notify NAT
  339. //
  340. for ( iOld = 0 ;
  341. iOld < IpMap.PublicIpCount() ;
  342. ++iOld )
  343. {
  344. pOld = IpMap.GetPublicIpPtr( iOld );
  345. //
  346. // need to reset m_dwNotifyPort : might have been set to 0 during previous
  347. // IOCTL
  348. // Only consider if != 0 : 0 means non unique port ( i.e already present in a
  349. // previous entry )
  350. //
  351. if ( pOld->m_dwNotifyPort = pOld->m_usUniquePort )
  352. {
  353. //
  354. // Check if present in new. If yes mark as no notification
  355. // in both old & new configuration
  356. //
  357. for ( iNew = 0 ;
  358. iNew < NewIpMapHelper.PublicIpCount() ;
  359. ++iNew )
  360. {
  361. pNew = NewIpMapHelper.GetPublicIpPtr( iNew );
  362. if ( pNew->m_usUniquePort == pOld->m_usUniquePort )
  363. {
  364. pNew->m_dwNotifyPort = 0;
  365. pOld->m_dwNotifyPort = 0;
  366. pNew->m_pvDirectorHandle = pOld->m_pvDirectorHandle;
  367. }
  368. }
  369. //
  370. // If still !=0 then was not found in new configuration,
  371. // so we have to unnotify NAT
  372. //
  373. if ( pOld->m_dwNotifyPort )
  374. {
  375. if ( (status = UnnotifyPort( pOld )) != STATUS_SUCCESS )
  376. {
  377. FreeBuffer( NewIpMap );
  378. goto complete;
  379. }
  380. }
  381. }
  382. }
  383. //
  384. // build old public IP -> new public IP, old prv IP -> new private IP map
  385. // if no match then -1
  386. //
  387. for ( iOld = 0 ; iOld < IpMap.PublicIpCount() ; ++iOld )
  388. {
  389. IpMap.GetPublicIpPtr( iOld )->m_dwIndex = (DWORD)-1;
  390. for ( iNew = 0 ; iNew < NewIpMapHelper.PublicIpCount() ; ++iNew )
  391. {
  392. if ( Equal_Memory( IpMap.GetPublicIpPtr( iOld ),
  393. NewIpMapHelper.GetPublicIpPtr( iNew ),
  394. sizeof(CKernelIpEndpoint) ) )
  395. {
  396. IpMap.GetPublicIpPtr( iOld )->m_dwIndex = iNew;
  397. break;
  398. }
  399. }
  400. //
  401. // If new index not equal to old, then must update cache entries
  402. //
  403. if ( iNew != iOld ||
  404. iNew == NewIpMapHelper.PublicIpCount() )
  405. {
  406. #if NOISY_DBG
  407. DbgPrint( "NatIoctlSessionControl: public IP mismatch: %u/%u %08x:%04x -> %u/%u\n",
  408. iOld, IpMap.PublicIpCount(),
  409. IpMap.GetPublicIpPtr( iOld )->m_dwIpAddress,
  410. IpMap.GetPublicIpPtr( iOld )->m_usPort,
  411. iNew, NewIpMapHelper.PublicIpCount() );
  412. #endif
  413. fUpdate = TRUE;
  414. }
  415. }
  416. for ( iOld = 0 ; iOld < IpMap.PrivateIpCount() ; ++iOld )
  417. {
  418. IpMap.GetPrivateIpEndpoint( iOld )->m_dwIndex = (DWORD)-1;
  419. for ( iNew = 0 ; iNew < NewIpMapHelper.PrivateIpCount() ; ++iNew )
  420. {
  421. //
  422. // Check for identity between old and new private IP address
  423. // We also check reference count on new private IP > 0
  424. // ( or was already 0 for old private IP )
  425. //
  426. if ( Equal_Memory( IpMap.GetPrivateIpEndpoint( iOld ),
  427. NewIpMapHelper.GetPrivateIpEndpoint( iNew ),
  428. sizeof(CKernelIpEndpoint) ) &&
  429. ( NewIpMapHelper.GetPrivateIpEndpoint( iNew )->m_dwRefCount ||
  430. !IpMap.GetPrivateIpEndpoint( iOld )->m_dwRefCount) )
  431. {
  432. IpMap.GetPrivateIpEndpoint( iOld )->m_dwIndex = iNew;
  433. break;
  434. }
  435. }
  436. //
  437. // If new index not equal to old, then must update cache entries
  438. //
  439. if ( iNew != iOld ||
  440. iNew == NewIpMapHelper.PrivateIpCount() )
  441. {
  442. #if NOISY_DBG
  443. DbgPrint( "NatIoctlSessionControl: private IP mismatch: %u/%u %08x:%04x -> %u/%u\n",
  444. iOld, IpMap.PrivateIpCount(),
  445. IpMap.GetPrivateIpEndpoint( iOld )->m_dwIpAddress,
  446. IpMap.GetPrivateIpEndpoint( iOld )->m_usPort,
  447. iNew, NewIpMapHelper.PrivateIpCount() );
  448. #endif
  449. fUpdate = TRUE;
  450. }
  451. }
  452. LockIpMap();
  453. if ( fUpdate )
  454. {
  455. #if NOISY_DBG
  456. DbgPrint( "NatIoctlSessionControl: update cache\n" );
  457. #endif
  458. // update cache, if no map for either public or private ref then delete entry
  459. // scan all cache entries for all hash buckets
  460. for ( iHash = 0 ; iHash < NB_BUCKETS ; ++iHash )
  461. {
  462. for ( iF = HashBuckets[iHash].m_HashEntry, iPrevF = EOCHAIN ;
  463. iF != EOCHAIN ;
  464. iF = iNext )
  465. {
  466. iNext = HashEntries[iF].m_NextHashEntry;
  467. iNewPrv = IpMap.GetPrivateIpEndpoint( HashEntries[iF].m_PrivateRef )->m_dwIndex;
  468. iNewPub = IpMap.GetPublicIpPtr( HashEntries[iF].m_PublicRef )->m_dwIndex;
  469. if ( iNewPrv == (DWORD)-1 ||
  470. iNewPub == (DWORD)-1 )
  471. {
  472. // remove it from hash chain
  473. if ( iPrevF != EOCHAIN )
  474. {
  475. HashEntries[iPrevF].m_NextHashEntry = iNext;
  476. }
  477. else
  478. {
  479. HashBuckets[iHash].m_HashEntry = iNext;
  480. }
  481. // insert it in free list
  482. HashEntries[iF].m_NextHashEntry = iFirstFreeHashEntry;
  483. iFirstFreeHashEntry = iF;
  484. }
  485. else
  486. {
  487. HashEntries[iF].m_PrivateRef = (USHORT)iNewPrv;
  488. HashEntries[iF].m_PublicRef = (USHORT)iNewPub;
  489. iPrevF = iF;
  490. }
  491. }
  492. }
  493. }
  494. FreeBuffer( IpMap.GetBuffer() );
  495. IpMap.SetBuffer( NewIpMap );
  496. UnlockIpMap();
  497. }
  498. else
  499. {
  500. LockIpMap();
  501. IpMap.SetBuffer( NewIpMap );
  502. UnlockIpMap();
  503. }
  504. //
  505. // scan new configuration for all m_dwNotifyPort != 0
  506. // which means unique ports not found in old cnfg
  507. // so we have to notify NAT
  508. //
  509. for ( iNew = 0 ;
  510. iNew < NewIpMapHelper.PublicIpCount() ;
  511. ++iNew )
  512. {
  513. pNew = NewIpMapHelper.GetPublicIpPtr( iNew );
  514. if ( pNew->m_dwNotifyPort )
  515. {
  516. if ( (status = NotifyPort( pNew )) != STATUS_SUCCESS )
  517. {
  518. goto complete;
  519. }
  520. }
  521. }
  522. iCurrentServer = 0;
  523. complete:
  524. Irp->IoStatus.Information = 0; // nothing to send back to user mode
  525. Irp->IoStatus.Status = status;
  526. IoCompleteRequest( Irp, 0 );
  527. #if NOISY_DBG
  528. DbgPrint( "NatIoctlSessionControl: status=%u\n", status );
  529. #endif
  530. return status;
  531. }
  532. extern "C"
  533. NTSTATUS
  534. NatQuerySessionControl(
  535. IN PVOID DirectorContext,
  536. IN UCHAR Protocol,
  537. IN ULONG PublicAddress,
  538. IN USHORT PublicPort,
  539. IN ULONG RemoteAddress,
  540. IN USHORT RemotePort,
  541. OUT PULONG PrivateAddress,
  542. OUT PUSHORT PrivatePort,
  543. OUT PVOID* DirectorSessionContextp OPTIONAL
  544. )
  545. /*++
  546. NatQuerySessionControl
  547. Routine Description:
  548. This routine is exported by the session-control module.
  549. It is invoked by the NAT to obtain the IP address and port to which
  550. a given incoming session should be directed.
  551. Arguments:
  552. Protocol - NAT_PROTOCOL_TCP or NAT_PROTOCOL_UDP (See IPNAT.H).
  553. PublicAddress - the public address on which the session was received
  554. PublicPort - the public port on which the session was received
  555. RemoteAddress - the remote address from which the session originated
  556. RemotePort - the remote port from which the session originated
  557. PrivateAddress - receives the private address for the session
  558. PrivatePort - receives the private port for the session
  559. Return Value:
  560. STATUS_SUCCESS if the session-control module has stored directions
  561. in 'PrivateAddress' and 'PrivatePort'; failure code otherwise.
  562. --*/
  563. {
  564. ULONG iHash;
  565. ULONG iF;
  566. ULONG iPrevF;
  567. ULONG iNext;
  568. DeclareTimeObject( SesCtl );
  569. ULONG Now;
  570. ULONG cGrain;
  571. int fAtLeastOneServer;
  572. int fAnyServerAvailable;
  573. CKernelIpEndpointEx* pIp;
  574. IPREF BestIpSoFar = (IPREF)-1;
  575. IPREF* pTopIp;
  576. IPREF* pEndIp;
  577. IPREF* pCurrentIp;
  578. CKernelServerDescription* pServer;
  579. CKernelServerDescription* BestServerSoFar = NULL;
  580. ULONG iP;
  581. ULONG cPublicIp;
  582. ULONG cServer;
  583. ULONG iS;
  584. ULONG cSize;
  585. HASH_ENTRY* NewHashEntries;
  586. HASH_ENTRY* pHash;
  587. int BestLoadSoFar = 0;
  588. KIRQL oldIrql;
  589. DWORD dwSticky;
  590. if ( IpMap.GetBuffer() == NULL )
  591. {
  592. return STATUS_ADDRESS_NOT_ASSOCIATED;
  593. }
  594. *DirectorSessionContextp = NULL;
  595. GetTime( SesCtl, Now );
  596. #if NOISY_DBG
  597. DbgPrint( "NatQuerySessionControl: protocol %02x remote %08x:%04x to %08x:%04x time %u\n",
  598. Protocol,
  599. RemoteAddress,RemotePort,
  600. PublicAddress,PublicPort,
  601. Now );
  602. #endif
  603. // hash bucket determined by XORing all bytes of public & remote address
  604. iHash = (PublicAddress ^ RemoteAddress) % NB_BUCKETS;
  605. LockIpMapAtDpcLevel();
  606. // check entries for expiration
  607. if ( HashBuckets[iHash].m_CheckExpiry < Now )
  608. {
  609. for ( iF = HashBuckets[iHash].m_HashEntry, iPrevF = EOCHAIN ;
  610. iF != EOCHAIN ;
  611. iF = iNext )
  612. {
  613. iNext = HashEntries[iF].m_NextHashEntry;
  614. if ( HashEntries[iF].m_Expiry < Now )
  615. {
  616. #if NOISY_DBG
  617. DbgPrint( "NatQuerySessionControl: remove entry %u, expire %u prev link %u\n",
  618. iF,
  619. HashEntries[iF].m_Expiry, iPrevF );
  620. #endif
  621. // remove it from hash chain
  622. if ( iPrevF != EOCHAIN )
  623. {
  624. HashEntries[iPrevF].m_NextHashEntry = iNext;
  625. }
  626. else
  627. {
  628. HashBuckets[iHash].m_HashEntry = iNext;
  629. }
  630. // insert it in free list
  631. HashEntries[iF].m_NextHashEntry = iFirstFreeHashEntry;
  632. iFirstFreeHashEntry = iF;
  633. }
  634. else
  635. {
  636. iPrevF = iF;
  637. }
  638. }
  639. HashBuckets[iHash].m_CheckExpiry = Now + CHECK_EXPIRY;
  640. }
  641. cPublicIp = IpMap.PublicIpCount();
  642. cServer = IpMap.ServerCount();
  643. // get index of public IP/port
  644. for ( iP = 0 ;
  645. iP < cPublicIp ;
  646. ++iP )
  647. {
  648. pIp = IpMap.GetPublicIpPtr( iP );
  649. if ( pIp->m_dwIpAddress == PublicAddress &&
  650. pIp->m_usPort == PublicPort )
  651. {
  652. dwSticky = pIp->m_dwSticky;
  653. break;
  654. }
  655. }
  656. #if NOISY_DBG
  657. DbgPrint( "NatQuerySessionControl: %d public, %d servers, index public %d\n",
  658. cPublicIp, cServer, iP );
  659. #endif
  660. if ( iP == cPublicIp )
  661. {
  662. // public IP not found in configuration : fail request
  663. UnlockIpMapFromDpcLevel();
  664. #if NOISY_DBG
  665. DbgPrint( "NatQuerySessionControl: public IP not found, fail request\n" );
  666. #endif
  667. return STATUS_ADDRESS_NOT_ASSOCIATED;
  668. }
  669. // check if RemoteAddress in sticky cache ( also check expiration for this cache line )
  670. // if yes update expiration time, return addr
  671. for ( iF = HashBuckets[iHash].m_HashEntry ;
  672. iF != EOCHAIN ;
  673. iF = pHash->m_NextHashEntry )
  674. {
  675. pHash = HashEntries + iF;
  676. if ( pHash->m_RemoteAddress == RemoteAddress &&
  677. pHash->m_PublicRef == iP )
  678. {
  679. // we have a match : return cached address
  680. pIp = IpMap.GetPrivateIpEndpoint( pHash->m_PrivateRef );
  681. *PrivateAddress = pIp->m_dwIpAddress;
  682. *PrivatePort = pIp->m_usPort;
  683. // update mapping expiration time
  684. pHash->m_Expiry = Now + dwSticky;
  685. #if NOISY_DBG
  686. DbgPrint( "NatQuerySessionControl: found in cache %08x:%04x, sticky %d new expire %u\n",
  687. *PrivateAddress,*PrivatePort,
  688. dwSticky, pHash->m_Expiry );
  689. #endif
  690. UnlockIpMapFromDpcLevel();
  691. return STATUS_SUCCESS;
  692. }
  693. }
  694. // get private IP : find entry with non null private IP, if > 0 then decrease by
  695. // LOADBAL_NORMALIZED_TOTAL / cServer / 10.
  696. // if no valid entry return public IP/port if no load avail entry reset all avail
  697. fAtLeastOneServer = FALSE;
  698. fAnyServerAvailable = FALSE;
  699. //
  700. // If no server then fail request, as at this point we know that the requested
  701. // public IP address is in our list, so access is denied by configuration
  702. //
  703. if ( cServer == 0 )
  704. {
  705. UnlockIpMapFromDpcLevel();
  706. #if NOISY_DBG
  707. DbgPrint( "NatQuerySessionControl: no server avail, fail\n" );
  708. #endif
  709. return STATUS_ADDRESS_NOT_ASSOCIATED;
  710. }
  711. //
  712. // load balancing granularity : based on # servers.
  713. // note that if cGrain > all base load availability for all servers
  714. // then load availability adjustment will loop several times, decreasing
  715. // efficiency.
  716. //
  717. cGrain = LOADBAL_NORMALIZED_TOTAL / cServer / 10;
  718. again:
  719. pServer = IpMap.GetServerPtr( iCurrentServer );
  720. pEndIp = pCurrentIp = IpMap.GetPrivateIpRef( pServer, iP );
  721. pTopIp = IpMap.GetPrivateIpRef( IpMap.GetServerPtr( cServer ), iP );
  722. cSize = IpMap.GetKernelServerDescriptionSize();
  723. // locate entry with highest load available
  724. for ( ;
  725. ;
  726. )
  727. {
  728. if ( *pCurrentIp != (IPREF)-1 )
  729. {
  730. // mapping exist from current server to public IP/port
  731. if ( pServer->m_dwLoadAvail )
  732. {
  733. fAtLeastOneServer = TRUE;
  734. // check if available
  735. if ( pServer->m_LoadbalancedLoadAvail > BestLoadSoFar )
  736. {
  737. BestServerSoFar = pServer;
  738. BestIpSoFar = *pCurrentIp;
  739. BestLoadSoFar = pServer->m_LoadbalancedLoadAvail;
  740. fAnyServerAvailable = TRUE;
  741. }
  742. }
  743. }
  744. // advance to next server
  745. pServer = (CKernelServerDescription*)(((LPBYTE)pServer) + cSize);
  746. if ( (pCurrentIp = (IPREF *)(((LPBYTE)pCurrentIp) + cSize )) == pTopIp )
  747. {
  748. pServer = IpMap.GetServerPtr( 0 );
  749. pCurrentIp = IpMap.GetPrivateIpRef( pServer, iP );
  750. }
  751. // check if looked at all servers
  752. if ( pCurrentIp == pEndIp )
  753. {
  754. if ( ++iCurrentServer == cServer )
  755. {
  756. iCurrentServer = 0;
  757. }
  758. break;
  759. }
  760. }
  761. if ( !fAnyServerAvailable )
  762. {
  763. // check if at least one server can map the public IP/port
  764. if ( fAtLeastOneServer )
  765. {
  766. // adjust load balanced availability
  767. for ( iS = 0 ; iS < cServer ; ++iS )
  768. {
  769. pServer = IpMap.GetServerPtr( iS );
  770. pServer->m_LoadbalancedLoadAvail += pServer->m_dwLoadAvail;
  771. }
  772. goto again;
  773. }
  774. // no server for this public IP/port : fail request
  775. UnlockIpMapFromDpcLevel();
  776. #if NOISY_DBG
  777. DbgPrint( "NatQuerySessionControl: no server for this addr avail, fail\n" );
  778. #endif
  779. return STATUS_ADDRESS_NOT_ASSOCIATED;
  780. }
  781. else
  782. {
  783. BestServerSoFar->m_LoadbalancedLoadAvail -= cGrain;
  784. pIp = IpMap.GetPrivateIpEndpoint( BestIpSoFar );
  785. *PrivateAddress = pIp->m_dwIpAddress;
  786. *PrivatePort = pIp->m_usPort;
  787. #if NOISY_DBG
  788. DbgPrint( "NatQuerySessionControl: found %08x:%04x, sticky %d new avail %d grain %d\n",
  789. *PrivateAddress,*PrivatePort,
  790. dwSticky, BestServerSoFar->m_LoadbalancedLoadAvail, cGrain );
  791. #endif
  792. }
  793. //
  794. // insert in sticky cache if sticky duration not null,
  795. // don't check if already there ( dups are OK )
  796. //
  797. if ( dwSticky != 0 )
  798. {
  799. //
  800. // if no free entry need to extend hash table, insert in iFirstFreeHashEntry
  801. //
  802. if ( iFirstFreeHashEntry == EOCHAIN )
  803. {
  804. NewHashEntries = (HASH_ENTRY*)AllocBuffer( sizeof(HASH_ENTRY) * (cHashEntries + HASH_ALLOC_GRAIN) );
  805. if ( NewHashEntries == NULL )
  806. {
  807. UnlockIpMapFromDpcLevel();
  808. #if NOISY_DBG
  809. DbgPrint( "NatQuerySessionControl: out of memory for %u hash entries\n",
  810. cHashEntries );
  811. #endif
  812. return STATUS_NO_MEMORY;
  813. }
  814. if ( HashEntries )
  815. {
  816. Copy_Memory( NewHashEntries, HashEntries, sizeof(HASH_ENTRY) * cHashEntries );
  817. FreeBuffer( HashEntries );
  818. }
  819. HashEntries = NewHashEntries;
  820. //
  821. // insert new entries in free chain
  822. //
  823. iFirstFreeHashEntry = cHashEntries;
  824. for ( iF = cHashEntries, cHashEntries += HASH_ALLOC_GRAIN - 1;
  825. iF < cHashEntries;
  826. ++iF
  827. )
  828. {
  829. HashEntries[iF].m_NextHashEntry = iF + 1;
  830. }
  831. HashEntries[cHashEntries++].m_NextHashEntry = EOCHAIN;
  832. }
  833. //
  834. // add new entry to cache
  835. //
  836. iF = iFirstFreeHashEntry;
  837. pHash = HashEntries + iF;
  838. iFirstFreeHashEntry = pHash->m_NextHashEntry;
  839. pHash->m_RemoteAddress = RemoteAddress;
  840. pHash->m_PrivateRef = (USHORT)BestIpSoFar;
  841. pHash->m_PublicRef = (USHORT)iP;
  842. pHash->m_NextHashEntry = HashBuckets[iHash].m_HashEntry;
  843. pHash->m_Expiry = Now + dwSticky;
  844. HashBuckets[iHash].m_HashEntry = iF;
  845. }
  846. UnlockIpMapFromDpcLevel();
  847. return STATUS_SUCCESS;
  848. }
  849. extern "C"
  850. NTSTATUS
  851. IISNATDispatchIoctl(
  852. IN PDEVICE_OBJECT DeviceObject,
  853. IN PIRP Irp
  854. )
  855. /*++
  856. Routine Description:
  857. This function handled IOCTL to the IIS LB device driver.
  858. Arguments:
  859. DeviceObject - Pointer to device object created by the system.
  860. Irp - I/O request packet
  861. Return Value:
  862. NT status
  863. --*/
  864. {
  865. PIO_STACK_LOCATION IrpSp;
  866. NTSTATUS status = STATUS_SUCCESS;
  867. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  868. switch ( IrpSp->Parameters.DeviceIoControl.IoControlCode )
  869. {
  870. case IOCTL_IISNATIO_SET_CONFIG:
  871. status = NatIoctlSessionControl( Irp );
  872. break;
  873. default:
  874. status = STATUS_INVALID_PARAMETER;
  875. }
  876. return status;
  877. }
  878. extern "C"
  879. NTSTATUS
  880. IISNATDispatchCreate(
  881. IN PDEVICE_OBJECT DeviceObject,
  882. IN PIRP Irp
  883. )
  884. /*++
  885. Routine Description:
  886. This function Create for IIS LB device driver.
  887. Arguments:
  888. DeviceObject - Pointer to device object created by the system.
  889. Irp - I/O request packet
  890. Return Value:
  891. NT status
  892. --*/
  893. {
  894. NTSTATUS status = STATUS_SUCCESS;
  895. Irp->IoStatus.Information = 0; // nothing to send back to user mode
  896. Irp->IoStatus.Status = status;
  897. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  898. #if NOISY_DBG
  899. DbgPrint( "IISNATDispatchCreate: status=%u\n", status );
  900. #endif
  901. return status;
  902. }
  903. extern "C"
  904. NTSTATUS
  905. IISNATDispatchClose(
  906. IN PDEVICE_OBJECT DeviceObject,
  907. IN PIRP Irp
  908. )
  909. /*++
  910. Routine Description:
  911. This function Close for IIS LB device driver.
  912. Arguments:
  913. DeviceObject - Pointer to device object created by the system.
  914. Irp - I/O request packet
  915. Return Value:
  916. NT status
  917. --*/
  918. {
  919. NTSTATUS status = STATUS_SUCCESS;
  920. Irp->IoStatus.Information = 0; // nothing to send back to user mode
  921. Irp->IoStatus.Status = status;
  922. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  923. #if NOISY_DBG
  924. DbgPrint( "IISNATDispatchClose: status=%u\n", status );
  925. #endif
  926. return status;
  927. }
  928. extern "C"
  929. VOID
  930. IISNATUnloadDriver(
  931. IN PDRIVER_OBJECT DriverObject
  932. )
  933. /*++
  934. Routine Description:
  935. This function is called when driver is unloaded
  936. Arguments:
  937. DriverObject - Pointer to driver object created by the system.
  938. Return Value:
  939. None
  940. --*/
  941. {
  942. UNICODE_STRING uszDeviceString;
  943. UINT iOld;
  944. CKernelIpEndpointEx* pOld;
  945. IoDeleteDevice( DriverObject->DeviceObject );
  946. RtlInitUnicodeString( &uszDeviceString, L"\\DosDevices\\" LSZDRIVERNAME );
  947. IoDeleteSymbolicLink( &uszDeviceString );
  948. if ( IpMap.GetBuffer() )
  949. {
  950. //
  951. // Unnotify all ports
  952. //
  953. for ( iOld = 0 ;
  954. iOld < IpMap.PublicIpCount() ;
  955. ++iOld )
  956. {
  957. pOld = IpMap.GetPublicIpPtr( iOld );
  958. if ( pOld->m_usUniquePort )
  959. {
  960. //
  961. // Ignore error : nothing we can do at this point
  962. //
  963. UnnotifyPort( pOld );
  964. }
  965. }
  966. FreeBuffer( IpMap.GetBuffer() );
  967. }
  968. ObDereferenceObject((PVOID)NatFileObject);
  969. ObDereferenceObject(NatDeviceObject);
  970. if ( HashEntries )
  971. {
  972. FreeBuffer( HashEntries );
  973. }
  974. #if NOISY_DBG
  975. DbgPrint( "IISNATUnloadDriver: unloaded\n" );
  976. #endif
  977. }
  978. extern "C"
  979. NTSTATUS
  980. DriverEntry (
  981. IN PDRIVER_OBJECT DriverObject,
  982. IN PUNICODE_STRING RegistryPath
  983. )
  984. /*++
  985. Routine Description:
  986. This is the initialization routine for the IIS LB device driver.
  987. Arguments:
  988. DriverObject - Pointer to driver object created by the system.
  989. Return Value:
  990. The function value is the final status from the initialization operation.
  991. --*/
  992. {
  993. NTSTATUS ntStatus = STATUS_SUCCESS;
  994. UNICODE_STRING uszDriverString;
  995. UNICODE_STRING uszDeviceString;
  996. UNICODE_STRING NatDeviceString;
  997. PDEVICE_OBJECT pDeviceObject;
  998. //
  999. // Get reference to NAT driver, necessary to allow safe calls to deregister port
  1000. //
  1001. RtlInitUnicodeString(&NatDeviceString, DD_IP_NAT_DEVICE_NAME);
  1002. ntStatus =
  1003. IoGetDeviceObjectPointer(
  1004. &NatDeviceString,
  1005. SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
  1006. &NatFileObject,
  1007. &NatDeviceObject
  1008. );
  1009. if (!NT_SUCCESS(ntStatus))
  1010. {
  1011. NatFileObject = NULL;
  1012. NatDeviceObject = NULL;
  1013. #if NOISY_DBG
  1014. DbgPrint( "DriverEntry: IoGetDeviceObjectPointer for NAT failed %08x\n",
  1015. ntStatus );
  1016. #endif
  1017. return ntStatus;
  1018. }
  1019. ObReferenceObject( NatDeviceObject );
  1020. RtlInitUnicodeString( &uszDriverString, L"\\Device\\" LSZDRIVERNAME );
  1021. ntStatus = IoCreateDevice( DriverObject,
  1022. 0,
  1023. &uszDriverString,
  1024. FILE_DEVICE_UNKNOWN,
  1025. 0,
  1026. FALSE,
  1027. &pDeviceObject );
  1028. #if NOISY_DBG
  1029. DbgPrint( "DriverEntry: IoCreateDevice=%x\n", ntStatus );
  1030. #endif
  1031. if ( ntStatus != STATUS_SUCCESS )
  1032. {
  1033. goto cleanup;
  1034. }
  1035. RtlInitUnicodeString( &uszDeviceString, L"\\DosDevices\\" LSZDRIVERNAME);
  1036. ntStatus = IoCreateSymbolicLink( &uszDeviceString, &uszDriverString );
  1037. #if NOISY_DBG
  1038. DbgPrint( "DriverEntry: IoCreateSymbolicLink=%x\n", ntStatus );
  1039. #endif
  1040. if ( ntStatus != STATUS_SUCCESS )
  1041. {
  1042. IoDeleteDevice( pDeviceObject );
  1043. goto cleanup;
  1044. }
  1045. NatRegisterSessionControl( 0 );
  1046. DriverObject->DriverUnload = IISNATUnloadDriver;
  1047. DriverObject->MajorFunction[IRP_MJ_CREATE] = IISNATDispatchCreate;
  1048. DriverObject->MajorFunction[IRP_MJ_CLOSE] = IISNATDispatchClose;
  1049. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IISNATDispatchIoctl;
  1050. cleanup:
  1051. if ( ntStatus != STATUS_SUCCESS )
  1052. {
  1053. ObDereferenceObject((PVOID)NatFileObject);
  1054. ObDereferenceObject(NatDeviceObject);
  1055. }
  1056. return ntStatus;
  1057. }
  1058. extern "C"
  1059. NTSTATUS
  1060. NatDeleteSession(
  1061. IN PVOID SessionHandle,
  1062. IN PVOID DirectorContext,
  1063. IN PVOID DirectorSessionContext
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. This function is called by NAT for session deletion notification
  1068. Arguments:
  1069. SessionHandle - NAT session handle
  1070. DirectorContext - IIS LB per port context
  1071. DirectorSessionContext - IIS LB per session context
  1072. Return Value:
  1073. NT status
  1074. --*/
  1075. {
  1076. #if NOISY_DBG
  1077. DbgPrint( "NatDeleteSession: session %08x\n",
  1078. SessionHandle );
  1079. #endif
  1080. return STATUS_SUCCESS;
  1081. }
  1082. NTSTATUS
  1083. NotifyPort(
  1084. CKernelIpEndpointEx* pEndp
  1085. )
  1086. /*++
  1087. Routine Description:
  1088. This function notify NAT that load balancing driver wants to handle
  1089. specified port
  1090. Arguments:
  1091. pEndp - endpoint descriptor
  1092. Return Value:
  1093. NT status
  1094. --*/
  1095. {
  1096. IO_STATUS_BLOCK IoStatusBlock;
  1097. PIRP Irp;
  1098. NTSTATUS status;
  1099. IP_NAT_REGISTER_DIRECTOR RegisterDirector;
  1100. RtlZeroMemory(&RegisterDirector, sizeof(RegisterDirector));
  1101. RegisterDirector.Version = IP_NAT_VERSION;
  1102. RegisterDirector.Flags = 0;
  1103. RegisterDirector.Protocol = NAT_PROTOCOL_TCP;
  1104. RegisterDirector.Port = pEndp->m_usUniquePort;
  1105. RegisterDirector.DirectorContext = NULL;
  1106. RegisterDirector.QueryHandler = NatQuerySessionControl;
  1107. RegisterDirector.DeleteHandler = NatDeleteSession;
  1108. #if defined(KERNEL_MODE)
  1109. Irp =
  1110. IoBuildDeviceIoControlRequest(
  1111. IOCTL_IP_NAT_REGISTER_DIRECTOR,
  1112. NatDeviceObject,
  1113. (PVOID)&RegisterDirector,
  1114. sizeof(RegisterDirector),
  1115. (PVOID)&RegisterDirector,
  1116. sizeof(RegisterDirector),
  1117. FALSE,
  1118. NULL,
  1119. &IoStatusBlock
  1120. );
  1121. if (!Irp)
  1122. {
  1123. status = STATUS_UNSUCCESSFUL;
  1124. }
  1125. else
  1126. {
  1127. status = IoCallDriver(NatDeviceObject, Irp);
  1128. #if NOISY_DBG
  1129. if ( !NT_SUCCESS(status) )
  1130. {
  1131. DbgPrint( "NotifyPort: IoCallDriver failed %08x\n",
  1132. status );
  1133. }
  1134. #endif
  1135. }
  1136. if ( NT_SUCCESS(status) )
  1137. {
  1138. pEndp->m_pvDirectorHandle = RegisterDirector.DirectorHandle;
  1139. pQueryInfoSession = RegisterDirector.QueryInfoSession;
  1140. pDeregisterNat = RegisterDirector.Deregister;
  1141. }
  1142. #else
  1143. pEndp->m_pvDirectorHandle = (LPVOID)0x12345678;
  1144. status = STATUS_SUCCESS;
  1145. #endif
  1146. #if NOISY_DBG
  1147. DbgPrint( "NotifyPort: %u, status %x handle %08x\n", pEndp->m_usUniquePort, status, pEndp->m_pvDirectorHandle );
  1148. #endif
  1149. return status;
  1150. }
  1151. NTSTATUS
  1152. UnnotifyPort(
  1153. CKernelIpEndpointEx* pEndp
  1154. )
  1155. /*++
  1156. Routine Description:
  1157. This function notify NAT that load balancing driver does not want to handle
  1158. specified port anymore
  1159. Arguments:
  1160. pEndp - endpoint descriptor
  1161. Return Value:
  1162. NT status
  1163. --*/
  1164. {
  1165. NTSTATUS status;
  1166. if ( pDeregisterNat )
  1167. {
  1168. status = pDeregisterNat( pEndp->m_pvDirectorHandle );
  1169. }
  1170. else
  1171. {
  1172. //
  1173. // nothing to unregister in this case, so report success
  1174. //
  1175. status = STATUS_SUCCESS; // STATUS_INVALID_PARAMETER;
  1176. }
  1177. #if NOISY_DBG
  1178. DbgPrint( "UnnotifyPort: %u, handle %08x Deregister %08x status %x\n",
  1179. pEndp->m_usUniquePort,
  1180. pEndp->m_pvDirectorHandle,
  1181. pDeregisterNat,
  1182. status );
  1183. #endif
  1184. return status;
  1185. }