Leaked source code of windows server 2003
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.

875 lines
23 KiB

  1. /*++
  2. Copyright (c) 1998, Microsoft Corporation
  3. Module Name:
  4. dhcpauto.c
  5. Abstract:
  6. This module contains code for automatic selection of a client address
  7. from a given scope of addresses.
  8. It makes use of a hashing function which accounts for the client's
  9. hardware address.
  10. Author:
  11. Abolade Gbadegesin (aboladeg) 9-Mar-1998
  12. Revision History:
  13. Raghu Gatta (rgatta) 5-Jul-2001
  14. +Changed DhcpIsReservedAddress & DhcpQueryReservedAddress to
  15. handle variable length name strings.
  16. +Added DhcpConvertHostNametoUnicode (mimics DhcpServer effect)
  17. Raghu Gatta (rgatta) 17-Jul-2001
  18. +Added DhcpGetLocalMacAddr
  19. --*/
  20. #include "precomp.h"
  21. #pragma hdrstop
  22. ULONG
  23. DhcpAcquireUniqueAddress(
  24. PCHAR Name,
  25. ULONG NameLength,
  26. PUCHAR HardwareAddress,
  27. ULONG HardwareAddressLength
  28. )
  29. /*++
  30. Routine Description:
  31. This routine is invoked to acquire a unique address for a client
  32. using the given hardware address to decrease the likelihood of collision.
  33. Arguments:
  34. Name - the name of the host for whom the address is being requested.
  35. If this matches the name of a server in the shared-access server-list,
  36. the address reserved for the server is returned.
  37. NameLength - length of 'Name', excluding any terminating 'nul'.
  38. HardwareAddress - the hardware address to be used
  39. HardwareAddressLength - the length of the hardware address
  40. Return Value:
  41. ULONG - the generated IP address
  42. Environment:
  43. Invoked from an arbitrary context.
  44. --*/
  45. {
  46. ULONG AssignedAddress;
  47. ULONG i = 0;
  48. PLIST_ENTRY Link;
  49. ULONG ScopeMask;
  50. ULONG ScopeNetwork;
  51. ULONG Seed = GetTickCount();
  52. BOOLEAN bUnused;
  53. PROFILE("DhcpAcquireUniqueAddress");
  54. EnterCriticalSection(&DhcpGlobalInfoLock);
  55. if (Name &&
  56. (AssignedAddress = DhcpQueryReservedAddress(Name, NameLength))) {
  57. LeaveCriticalSection(&DhcpGlobalInfoLock);
  58. NhTrace(
  59. TRACE_FLAG_DHCP,
  60. "DhcpAcquireUniqueAddress: returning mapping to %s",
  61. INET_NTOA(AssignedAddress)
  62. );
  63. return AssignedAddress;
  64. }
  65. ScopeNetwork = DhcpGlobalInfo->ScopeNetwork;
  66. ScopeMask = DhcpGlobalInfo->ScopeMask;
  67. LeaveCriticalSection(&DhcpGlobalInfoLock);
  68. do {
  69. if (++i > 4) { AssignedAddress = 0; break; }
  70. //
  71. // Generate an address
  72. //
  73. do {
  74. AssignedAddress =
  75. DhcpGenerateAddress(
  76. &Seed,
  77. HardwareAddress,
  78. HardwareAddressLength,
  79. ScopeNetwork,
  80. ScopeMask
  81. );
  82. } while(
  83. (AssignedAddress & ~ScopeMask) == 0 ||
  84. (AssignedAddress & ~ScopeMask) == ~ScopeMask
  85. );
  86. } while(!DhcpIsUniqueAddress(AssignedAddress, &bUnused, NULL, NULL));
  87. return AssignedAddress;
  88. } // DhcpAcquireUniqueAddress
  89. ULONG
  90. DhcpGenerateAddress(
  91. PULONG Seed,
  92. PUCHAR HardwareAddress,
  93. ULONG HardwareAddressLength,
  94. ULONG ScopeNetwork,
  95. ULONG ScopeMask
  96. )
  97. /*++
  98. Routine Description:
  99. This routine is invoked to compute a randomized hash value
  100. for a client IP address using a hardware-address.
  101. Arguments:
  102. Seed - contains (and receives) the seed to 'RtlRandom'
  103. HardwareAddress - the hardware address to be used
  104. HardwareAddressLength - the length of the hardware address
  105. ScopeNetwork - the network into which the generated address
  106. will be constrained
  107. ScopeMask - the mask for the scope network
  108. Return Value:
  109. ULONG - the generated IP address
  110. Environment:
  111. Invoked from an arbitrary context.
  112. Revision History:
  113. Based on 'GrandHashing' from net\sockets\tcpcmd\dhcpm\client\dhcp
  114. by RameshV.
  115. --*/
  116. {
  117. ULONG Hash;
  118. ULONG Shift;
  119. #if 1
  120. Hash = RtlRandom(Seed) & 0xffff0000;
  121. Hash |= RtlRandom(Seed) >> 16;
  122. #else
  123. Seed = GetTickCount();
  124. Seed = Seed * 1103515245 + 12345;
  125. Hash = (Seed) >> 16;
  126. Hash <<= 16;
  127. Seed = Seed * 1103515245 + 12345;
  128. Hash += Seed >> 16;
  129. #endif
  130. Shift = Hash % sizeof(ULONG);
  131. while(HardwareAddressLength--) {
  132. Hash += (*HardwareAddress++) << (8 * Shift);
  133. Shift = (Shift + 1) % sizeof(ULONG);
  134. }
  135. return (Hash & ~ScopeMask) | ScopeNetwork;
  136. } // DnsGenerateAddress
  137. BOOLEAN
  138. DhcpIsReservedAddress(
  139. ULONG Address,
  140. PCHAR Name OPTIONAL,
  141. ULONG NameLength OPTIONAL
  142. )
  143. /*++
  144. Routine Description:
  145. This routine is invoked to determine whether the given IP address
  146. is reserved for another client.
  147. Arguments:
  148. Address - the IP address to be determined
  149. Name - optionally specifies the client on whose behalf the call is made
  150. NameLength - specifies the length of 'Name' excluding the terminating nul
  151. Return Value:
  152. BOOLEAN - TRUE if the address is reserved for another client,
  153. FALSE otherwise.
  154. Environment:
  155. Invoked with 'DhcpGlobalInfoLock' held by the caller.
  156. --*/
  157. {
  158. ULONG Error = NO_ERROR;
  159. PLIST_ENTRY Link;
  160. PNAT_DHCP_RESERVATION Reservation;
  161. PWCHAR pszUnicodeHostName = NULL;
  162. EnterCriticalSection(&NhLock);
  163. if (IsListEmpty(&NhDhcpReservationList)) {
  164. LeaveCriticalSection(&NhLock);
  165. return FALSE;
  166. }
  167. if (Name) {
  168. Error = DhcpConvertHostNametoUnicode(
  169. CP_OEMCP, // atleast Windows clients send it this way
  170. Name,
  171. NameLength,
  172. &pszUnicodeHostName
  173. );
  174. if (NO_ERROR != Error) {
  175. LeaveCriticalSection(&NhLock);
  176. if (pszUnicodeHostName) {
  177. NH_FREE(pszUnicodeHostName);
  178. }
  179. //
  180. // we can return true or false on failure
  181. // better we return false - otherwise the client will be in a continuous
  182. // loop trying to get another address when we NACK its request
  183. //
  184. return FALSE;
  185. }
  186. }
  187. for (Link = NhDhcpReservationList.Flink;
  188. Link != &NhDhcpReservationList; Link = Link->Flink) {
  189. Reservation = CONTAINING_RECORD(Link, NAT_DHCP_RESERVATION, Link);
  190. if (Address == Reservation->Address) {
  191. //
  192. // Address matches but Name does not (regular case)
  193. // we return TRUE since address reserved for someone else
  194. //
  195. if (!pszUnicodeHostName ||
  196. !Reservation->Name ||
  197. lstrcmpiW(pszUnicodeHostName, Reservation->Name)) {
  198. LeaveCriticalSection(&NhLock);
  199. if (pszUnicodeHostName) {
  200. NH_FREE(pszUnicodeHostName);
  201. }
  202. return TRUE;
  203. }
  204. //
  205. // Address and Name BOTH match
  206. // we fall through and return FALSE since we want this address to be selected
  207. //
  208. else
  209. break;
  210. } else {
  211. //
  212. // Name matches but Address does not (irregular case)
  213. // we return TRUE since another address has been reserved for this name
  214. //
  215. if (pszUnicodeHostName &&
  216. Reservation->Name &&
  217. lstrcmpiW(pszUnicodeHostName, Reservation->Name) == 0) {
  218. LeaveCriticalSection(&NhLock);
  219. if (pszUnicodeHostName) {
  220. NH_FREE(pszUnicodeHostName);
  221. }
  222. return TRUE;
  223. }
  224. //
  225. // Neither Address nor Name match
  226. // continue searching
  227. //
  228. }
  229. }
  230. LeaveCriticalSection(&NhLock);
  231. if (pszUnicodeHostName) {
  232. NH_FREE(pszUnicodeHostName);
  233. }
  234. return FALSE;
  235. } // DhcpIsReservedAddress
  236. BOOLEAN
  237. DhcpIsUniqueAddress(
  238. ULONG Address,
  239. PBOOLEAN IsLocal,
  240. PUCHAR ConflictAddress OPTIONAL,
  241. PULONG ConflictAddressLength OPTIONAL
  242. )
  243. /*++
  244. Routine Description:
  245. This routine is invoked to determine whether the given address
  246. is unique on the directly connected subnetworks.
  247. The determination accounts for any configured static addresses
  248. included in the global information.
  249. Arguments:
  250. Address - the address whose uniqueness is to be determined
  251. IsLocal - pointer to BOOLEAN which receives info about whether
  252. the requested address is one of the local interfaces' address
  253. ConflictAddress - optionally receives a copy of the conflicting
  254. hardware address if a conflict is found
  255. ConflictAddressLength - if 'ConflictAddress' is set, receives
  256. the length of the conflicting address.
  257. Return Value:
  258. BOOLEAN - TRUE if unique, FALSE otherwise.
  259. --*/
  260. {
  261. BOOLEAN ConflictFound = FALSE;
  262. ULONG Error;
  263. UCHAR ExistingAddress[MAX_HARDWARE_ADDRESS_LENGTH];
  264. ULONG ExistingAddressLength;
  265. ULONG i;
  266. PDHCP_INTERFACE Interfacep;
  267. BOOLEAN IsNatInterface;
  268. PLIST_ENTRY Link;
  269. ULONG SourceAddress;
  270. PROFILE("DhcpIsUniqueAddress");
  271. *IsLocal = FALSE;
  272. //
  273. // See if this is a static address
  274. //
  275. EnterCriticalSection(&DhcpGlobalInfoLock);
  276. if (DhcpGlobalInfo && DhcpGlobalInfo->ExclusionCount) {
  277. for (i = 0; i < DhcpGlobalInfo->ExclusionCount; i++) {
  278. if (Address == DhcpGlobalInfo->ExclusionArray[i]) {
  279. LeaveCriticalSection(&DhcpGlobalInfoLock);
  280. if (ConflictAddressLength) { *ConflictAddressLength = 0; }
  281. return FALSE;
  282. }
  283. }
  284. }
  285. LeaveCriticalSection(&DhcpGlobalInfoLock);
  286. //
  287. // Try to detect collisions
  288. //
  289. EnterCriticalSection(&DhcpInterfaceLock);
  290. for (Link = DhcpInterfaceList.Flink;
  291. Link != &DhcpInterfaceList;
  292. Link = Link->Flink
  293. ) {
  294. Interfacep = CONTAINING_RECORD(Link, DHCP_INTERFACE, Link);
  295. if (DHCP_INTERFACE_DELETED(Interfacep)) { continue; }
  296. ACQUIRE_LOCK(Interfacep);
  297. //
  298. // We send out an ARP request unless
  299. // (a) the interface is a boundary interface
  300. // (b) the interface is not NAT-enabled
  301. // (c) the allocator is not active on the interface
  302. // (d) the interface is not a LAN adapter
  303. // (e) the interface has no bindings.
  304. //
  305. if (!DHCP_INTERFACE_NAT_NONBOUNDARY(Interfacep) ||
  306. !DHCP_INTERFACE_ACTIVE(Interfacep) ||
  307. (Interfacep->Type != PERMANENT) ||
  308. !Interfacep->BindingCount) {
  309. RELEASE_LOCK(Interfacep);
  310. continue;
  311. }
  312. for (i = 0; i < Interfacep->BindingCount; i++) {
  313. SourceAddress = Interfacep->BindingArray[i].Address;
  314. ExistingAddressLength = sizeof(ExistingAddress);
  315. if (SourceAddress == Address)
  316. {
  317. //
  318. // check to see that requested address is not same as
  319. // one of the local addresses on the NAT box
  320. //
  321. NhTrace(
  322. TRACE_FLAG_DHCP,
  323. "DhcpIsUniqueAddress: %s is in use locally",
  324. INET_NTOA(Address)
  325. );
  326. if (ConflictAddress && ConflictAddressLength) {
  327. if (DhcpGetLocalMacAddr(
  328. Address,
  329. ExistingAddress,
  330. &ExistingAddressLength
  331. ))
  332. {
  333. if (ExistingAddressLength > MAX_HARDWARE_ADDRESS_LENGTH) {
  334. ExistingAddressLength = MAX_HARDWARE_ADDRESS_LENGTH;
  335. }
  336. CopyMemory(
  337. ConflictAddress,
  338. ExistingAddress,
  339. ExistingAddressLength
  340. );
  341. *ConflictAddressLength = ExistingAddressLength;
  342. }
  343. else
  344. {
  345. *ConflictAddressLength = 0;
  346. }
  347. }
  348. *IsLocal = TRUE;
  349. ConflictFound = TRUE;
  350. break;
  351. }
  352. RELEASE_LOCK(Interfacep);
  353. Error =
  354. SendARP(
  355. Address,
  356. SourceAddress,
  357. (PULONG)ExistingAddress,
  358. &ExistingAddressLength
  359. );
  360. ACQUIRE_LOCK(Interfacep);
  361. if (Error) {
  362. NhWarningLog(
  363. IP_AUTO_DHCP_LOG_SENDARP_FAILED,
  364. Error,
  365. "%I%I",
  366. Address,
  367. SourceAddress
  368. );
  369. } else if (ExistingAddressLength &&
  370. ExistingAddressLength <= sizeof(ExistingAddress)) {
  371. NhTrace(
  372. TRACE_FLAG_DHCP,
  373. "DhcpIsUniqueAddress: %s is in use",
  374. INET_NTOA(Address)
  375. );
  376. #if DBG
  377. NhDump(
  378. TRACE_FLAG_DHCP,
  379. ExistingAddress,
  380. ExistingAddressLength,
  381. 1
  382. );
  383. #endif
  384. if (ConflictAddress && ConflictAddressLength) {
  385. if (ExistingAddressLength > MAX_HARDWARE_ADDRESS_LENGTH) {
  386. ExistingAddressLength = MAX_HARDWARE_ADDRESS_LENGTH;
  387. }
  388. CopyMemory(
  389. ConflictAddress,
  390. ExistingAddress,
  391. ExistingAddressLength
  392. );
  393. *ConflictAddressLength = ExistingAddressLength;
  394. }
  395. ConflictFound = TRUE;
  396. break;
  397. }
  398. }
  399. RELEASE_LOCK(Interfacep);
  400. if (ConflictFound) { break; }
  401. }
  402. LeaveCriticalSection(&DhcpInterfaceLock);
  403. return ConflictFound ? FALSE : TRUE;
  404. } // DhcpIsUniqueAddress
  405. ULONG
  406. DhcpQueryReservedAddress(
  407. PCHAR Name,
  408. ULONG NameLength
  409. )
  410. /*++
  411. Routine Description:
  412. This routine is called to determine whether the given machine name
  413. corresponds to an entry in the list of reserved addresses.
  414. Arguments:
  415. Name - specifies the machine name, which might not be nul-terminated.
  416. NameLength - specifies the length of the given machine name,
  417. not including any terminating nul character.
  418. Return Value:
  419. ULONG - the IP address of the machine, if any.
  420. Environment:
  421. Invoked with 'DhcpGlobalInfoLock' held by the caller.
  422. --*/
  423. {
  424. ULONG Error = NO_ERROR;
  425. PLIST_ENTRY Link;
  426. ULONG ReservedAddress;
  427. PNAT_DHCP_RESERVATION Reservation;
  428. PWCHAR pszUnicodeHostName = NULL;
  429. EnterCriticalSection(&NhLock);
  430. if (IsListEmpty(&NhDhcpReservationList))
  431. {
  432. LeaveCriticalSection(&NhLock);
  433. return FALSE;
  434. }
  435. if (Name) {
  436. Error = DhcpConvertHostNametoUnicode(
  437. CP_OEMCP, // atleast Windows clients send it this way
  438. Name,
  439. NameLength,
  440. &pszUnicodeHostName
  441. );
  442. if (NO_ERROR != Error) {
  443. LeaveCriticalSection(&NhLock);
  444. if (pszUnicodeHostName) {
  445. NH_FREE(pszUnicodeHostName);
  446. }
  447. return FALSE;
  448. }
  449. }
  450. for (Link = NhDhcpReservationList.Flink;
  451. Link != &NhDhcpReservationList; Link = Link->Flink)
  452. {
  453. Reservation = CONTAINING_RECORD(Link, NAT_DHCP_RESERVATION, Link);
  454. if (!pszUnicodeHostName || !Reservation->Name) { continue; }
  455. if (lstrcmpiW(pszUnicodeHostName, Reservation->Name)) { continue; }
  456. ReservedAddress = Reservation->Address;
  457. LeaveCriticalSection(&NhLock);
  458. if (pszUnicodeHostName) {
  459. NH_FREE(pszUnicodeHostName);
  460. }
  461. return ReservedAddress;
  462. }
  463. LeaveCriticalSection(&NhLock);
  464. if (pszUnicodeHostName) {
  465. NH_FREE(pszUnicodeHostName);
  466. }
  467. return 0;
  468. } // DhcpQueryReservedAddress
  469. //
  470. // Utility routines
  471. //
  472. ULONG
  473. DhcpConvertHostNametoUnicode(
  474. UINT CodePage,
  475. CHAR *pHostName,
  476. ULONG HostNameLength,
  477. PWCHAR *ppszUnicode
  478. )
  479. {
  480. //
  481. // make sure to free the returned Unicode hostname
  482. //
  483. DWORD dwSize = 0;
  484. ULONG Error = NO_ERROR;
  485. PCHAR pszHostName = NULL;
  486. LPBYTE pszUtf8HostName = NULL; // copy of pszHostName in Utf8 format
  487. PWCHAR pszUnicodeHostName = NULL;
  488. if (ppszUnicode)
  489. {
  490. *ppszUnicode = NULL;
  491. }
  492. else
  493. {
  494. return ERROR_INVALID_PARAMETER;
  495. }
  496. do
  497. {
  498. //
  499. // create a null terminated copy
  500. //
  501. dwSize = HostNameLength + 4;
  502. pszHostName = reinterpret_cast<PCHAR>(NH_ALLOCATE(dwSize));
  503. if (!pszHostName)
  504. {
  505. NhTrace(
  506. TRACE_FLAG_DNS,
  507. "DhcpConvertHostNametoUnicode: allocation failed for "
  508. "hostname copy buffer"
  509. );
  510. Error = ERROR_NOT_ENOUGH_MEMORY;
  511. break;
  512. }
  513. ZeroMemory(pszHostName, dwSize);
  514. memcpy(pszHostName, pHostName, HostNameLength);
  515. pszHostName[HostNameLength] = '\0';
  516. //
  517. // convert the given hostname to a Unicode string
  518. //
  519. if (CP_UTF8 == CodePage)
  520. {
  521. pszUtf8HostName = (LPBYTE)pszHostName;
  522. }
  523. else
  524. {
  525. //
  526. // now convert this into UTF8 format
  527. //
  528. if (!ConvertToUtf8(
  529. CodePage,
  530. (LPSTR)pszHostName,
  531. (PCHAR *)&pszUtf8HostName,
  532. &dwSize))
  533. {
  534. Error = GetLastError();
  535. NhTrace(
  536. TRACE_FLAG_DNS,
  537. "DhcpConvertHostNametoUnicode: conversion from "
  538. "CodePage %d to UTF8 for hostname failed "
  539. "with error %ld (0x%08x)",
  540. CodePage,
  541. Error,
  542. Error
  543. );
  544. break;
  545. }
  546. }
  547. //
  548. // now convert UTF8 string into Unicode format
  549. //
  550. if (!ConvertUTF8ToUnicode(
  551. pszUtf8HostName,
  552. (LPWSTR *)&pszUnicodeHostName,
  553. &dwSize))
  554. {
  555. Error = GetLastError();
  556. NhTrace(
  557. TRACE_FLAG_DNS,
  558. "DhcpConvertHostNametoUnicode: conversion from "
  559. "UTF8 to Unicode for hostname failed "
  560. "with error %ld (0x%08x)",
  561. Error,
  562. Error
  563. );
  564. if (pszUnicodeHostName)
  565. {
  566. NH_FREE(pszUnicodeHostName);
  567. }
  568. break;
  569. }
  570. *ppszUnicode = pszUnicodeHostName;
  571. NhTrace(
  572. TRACE_FLAG_DNS,
  573. "DhcpConvertHostNametoUnicode: succeeded! %S",
  574. pszUnicodeHostName
  575. );
  576. } while (FALSE);
  577. if (pszHostName)
  578. {
  579. NH_FREE(pszHostName);
  580. }
  581. if ((CP_UTF8 != CodePage) && pszUtf8HostName)
  582. {
  583. NH_FREE(pszUtf8HostName);
  584. }
  585. return Error;
  586. } // DhcpConvertHostNametoUnicode
  587. BOOL
  588. DhcpGetLocalMacAddr(
  589. ULONG Address,
  590. PUCHAR MacAddr,
  591. PULONG MacAddrLength
  592. )
  593. /*++
  594. Routine Description:
  595. This routine is invoked to determine the local physical MAC address
  596. for the given local IP address.
  597. Arguments:
  598. Address - the local IP address
  599. MacAddr - buffer for holding the MAC addr (upto MAX_HARDWARE_ADDRESS_LENGTH)
  600. MacAddrLength - specifies the length of 'MacAddr'
  601. Return Value:
  602. BOOLEAN - TRUE if we are able to get the MAC address,
  603. FALSE otherwise.
  604. Environment:
  605. Invoked from DhcpIsUniqueAddress().
  606. --*/
  607. {
  608. BOOL bRet = FALSE;
  609. DWORD Error = NO_ERROR;
  610. PMIB_IPNETTABLE IpNetTable = NULL;
  611. PMIB_IPNETROW IpNetRow = NULL;
  612. DWORD dwPhysAddrLen = 0, i;
  613. ULONG dwSize = 0;
  614. do
  615. {
  616. //
  617. // retrieve size of address mapping table
  618. //
  619. Error = GetIpNetTable(
  620. IpNetTable,
  621. &dwSize,
  622. FALSE
  623. );
  624. if (!Error)
  625. {
  626. NhTrace(
  627. TRACE_FLAG_DHCP,
  628. "DhcpGetLocalMacAddr: should NOT have returned %d",
  629. Error
  630. );
  631. break;
  632. }
  633. else
  634. if (ERROR_INSUFFICIENT_BUFFER != Error)
  635. {
  636. NhTrace(
  637. TRACE_FLAG_DHCP,
  638. "DhcpGetLocalMacAddr: GetIpNetTable=%d",
  639. Error
  640. );
  641. break;
  642. }
  643. //
  644. // allocate a buffer
  645. //
  646. IpNetTable = (PMIB_IPNETTABLE)NH_ALLOCATE(dwSize);
  647. if (!IpNetTable)
  648. {
  649. NhTrace(
  650. TRACE_FLAG_DHCP,
  651. "DhcpGetLocalMacAddr: error allocating %d bytes",
  652. dwSize
  653. );
  654. break;
  655. }
  656. //
  657. // retrieve the address mapping table
  658. //
  659. Error = GetIpNetTable(
  660. IpNetTable,
  661. &dwSize,
  662. FALSE
  663. );
  664. if (NO_ERROR != Error)
  665. {
  666. NhTrace(
  667. TRACE_FLAG_DHCP,
  668. "DhcpGetLocalMacAddr: GetIpNetTable=%d size=%d",
  669. Error,
  670. dwSize
  671. );
  672. break;
  673. }
  674. for (i = 0; i < IpNetTable->dwNumEntries; i++)
  675. {
  676. IpNetRow = &IpNetTable->table[i];
  677. if (IpNetRow->dwAddr == Address)
  678. {
  679. dwPhysAddrLen = IpNetRow->dwPhysAddrLen;
  680. if (dwPhysAddrLen > MAX_HARDWARE_ADDRESS_LENGTH)
  681. {
  682. dwPhysAddrLen = MAX_HARDWARE_ADDRESS_LENGTH;
  683. }
  684. CopyMemory(
  685. MacAddr,
  686. IpNetRow->bPhysAddr,
  687. dwPhysAddrLen
  688. );
  689. *MacAddrLength = dwPhysAddrLen;
  690. bRet = TRUE;
  691. break;
  692. }
  693. }
  694. } while (FALSE);
  695. if (IpNetTable)
  696. {
  697. NH_FREE(IpNetTable);
  698. }
  699. return bRet;
  700. } // DhcpGetLocalMacAddr