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.

1414 lines
35 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: work.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin August 31, 1995 Created
  8. //
  9. // Worker function implementation
  10. //============================================================================
  11. #include "pchbootp.h"
  12. //----------------------------------------------------------------------------
  13. // Function: CallbackFunctionNetworkEvents
  14. //
  15. // This function runs in the context of the ntdll wait thread. Using
  16. // QueueBootpWorker ensures that the bootp dll is running
  17. //----------------------------------------------------------------------------
  18. VOID
  19. CallbackFunctionNetworkEvents(
  20. PVOID pvContext,
  21. BOOLEAN NotUsed
  22. ) {
  23. HANDLE WaitHandle;
  24. if (!ENTER_BOOTP_API()) { return; }
  25. //
  26. // set the handle to NULL, so that Unregister wont be called
  27. //
  28. WaitHandle = InterlockedExchangePointer(&ig.IG_InputEventHandle, NULL);
  29. if (WaitHandle) {
  30. UnregisterWaitEx( WaitHandle, NULL ) ;
  31. }
  32. QueueBootpWorker(WorkerFunctionNetworkEvents, pvContext);
  33. LEAVE_BOOTP_API();
  34. return;
  35. }
  36. //----------------------------------------------------------------------------
  37. // Function: WorkerFunctionNetworkEvents
  38. //
  39. // This function enumerates the input events on each interface and processes
  40. // any incoming input packets. Queued by CallbackFunctionNetworkEvents
  41. //----------------------------------------------------------------------------
  42. VOID
  43. WorkerFunctionNetworkEvents(
  44. PVOID pvContextNotused
  45. ) {
  46. DWORD i, dwErr;
  47. PIF_TABLE pTable;
  48. PIPBOOTP_IF_CONFIG pic;
  49. PIPBOOTP_IF_BINDING pib;
  50. PIPBOOTP_IP_ADDRESS paddr;
  51. PIF_TABLE_ENTRY pite;
  52. PLIST_ENTRY ple, phead;
  53. WSANETWORKEVENTS wsane;
  54. if (!ENTER_BOOTP_WORKER()) { return; }
  55. pTable = ig.IG_IfTable;
  56. ACQUIRE_READ_LOCK(&pTable->IT_RWL);
  57. //
  58. // go through the list of active interfaces
  59. // processing sockets which are read-ready
  60. //
  61. phead = &pTable->IT_ListByAddress;
  62. for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
  63. pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
  64. pic = pite->ITE_Config;
  65. pib = pite->ITE_Binding;
  66. paddr = IPBOOTP_IF_ADDRESS_TABLE(pib);
  67. for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
  68. if (pite->ITE_Sockets[i] == INVALID_SOCKET) { continue; }
  69. //
  70. // enumerate network events to see whether
  71. // any packets have arrived on this interface
  72. //
  73. dwErr = WSAEnumNetworkEvents(pite->ITE_Sockets[i], NULL, &wsane);
  74. if (dwErr != NO_ERROR) {
  75. LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
  76. TRACE3(
  77. RECEIVE, "error %d checking for input on interface %d (%s)",
  78. dwErr, pite->ITE_Index, lpszAddr
  79. );
  80. LOGWARN1(ENUM_NETWORK_EVENTS_FAILED, lpszAddr, dwErr);
  81. continue;
  82. }
  83. //
  84. // see if the input bit is set
  85. //
  86. if (!(wsane.lNetworkEvents & FD_READ)) { continue; }
  87. //
  88. // the input flag is set, now see if there was an error
  89. //
  90. if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR) {
  91. LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
  92. TRACE3(
  93. RECEIVE, "error %d in input record for interface %d (%s)",
  94. wsane.iErrorCode[FD_READ_BIT], pite->ITE_Index, lpszAddr
  95. );
  96. LOGWARN1(INPUT_RECORD_ERROR, lpszAddr, dwErr);
  97. continue;
  98. }
  99. //
  100. // there is no error, so process the socket
  101. //
  102. ProcessSocket(pite, i, pTable);
  103. }
  104. }
  105. RELEASE_READ_LOCK(&pTable->IT_RWL);
  106. //
  107. // register the InputEvent with the NtdllWait Thread again (only if the
  108. // dll is not stopping). I use this model of registering the event with
  109. // ntdll every time, to prevent the worker function from being called for
  110. // every packet received (when packets are received at the same time).
  111. //
  112. if (ig.IG_Status != IPBOOTP_STATUS_STOPPING) {
  113. if (! RegisterWaitForSingleObject(
  114. &ig.IG_InputEventHandle,
  115. ig.IG_InputEvent,
  116. CallbackFunctionNetworkEvents,
  117. NULL, //null context
  118. INFINITE, //no timeout
  119. (WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE)
  120. )) {
  121. dwErr = GetLastError();
  122. TRACE1(
  123. START, "error %d returned by RegisterWaitForSingleObjectEx",
  124. dwErr
  125. );
  126. LOGERR0(REGISTER_WAIT_FAILED, dwErr);
  127. }
  128. }
  129. LEAVE_BOOTP_WORKER();
  130. return;
  131. }
  132. //----------------------------------------------------------------------------
  133. // Function: ProcessSocket
  134. //
  135. // This function processes a packet on an interface, queueing
  136. // the packet for processing by a worker function after doing some
  137. // basic validation.
  138. //----------------------------------------------------------------------------
  139. DWORD
  140. ProcessSocket(
  141. PIF_TABLE_ENTRY pite,
  142. DWORD dwAddrIndex,
  143. PIF_TABLE pTable
  144. ) {
  145. BOOL bFreePacket;
  146. WORKERFUNCTION pwf;
  147. PINPUT_CONTEXT pwc;
  148. PIPBOOTP_IF_STATS pis;
  149. PIPBOOTP_IF_CONFIG pic;
  150. PIPBOOTP_IP_ADDRESS paddr;
  151. PIPBOOTP_PACKET pibp;
  152. PLIST_ENTRY ple, phead;
  153. DWORD dwErr, dwInputSource;
  154. SOCKADDR_IN sinInputSource;
  155. PIPBOOTP_GLOBAL_CONFIG pigc;
  156. INT iInputLength, iAddrLength;
  157. PBYTE pInputPacket;
  158. pigc = ig.IG_Config;
  159. pis = &pite->ITE_Stats;
  160. paddr = IPBOOTP_IF_ADDRESS_TABLE(pite->ITE_Binding) + dwAddrIndex;
  161. //
  162. // the descriptor for this interface is set,
  163. // so allocate space for the packet
  164. //
  165. pwc = BOOTP_ALLOC(sizeof(INPUT_CONTEXT));
  166. if (pwc == NULL) {
  167. dwErr = GetLastError();
  168. TRACE2(
  169. RECEIVE, "error %d allocating %d bytes for incoming packet",
  170. dwErr, sizeof(INPUT_CONTEXT)
  171. );
  172. LOGERR0(HEAP_ALLOC_FAILED, dwErr);
  173. return dwErr;
  174. }
  175. pInputPacket = pwc->IC_InputPacket;
  176. dwErr = NO_ERROR;
  177. bFreePacket = TRUE;
  178. do {
  179. CHAR szSource[20];
  180. //
  181. // receive the packet
  182. //
  183. iAddrLength = sizeof(SOCKADDR_IN);
  184. iInputLength = recvfrom(
  185. pite->ITE_Sockets[dwAddrIndex], pInputPacket,
  186. MAX_PACKET_SIZE, 0, (PSOCKADDR)&sinInputSource,
  187. &iAddrLength
  188. );
  189. if (iInputLength == 0 || iInputLength == SOCKET_ERROR) {
  190. LPSTR lpszAddr;
  191. dwErr = WSAGetLastError();
  192. lpszAddr = INET_NTOA(paddr->IA_Address);
  193. TRACE3(
  194. RECEIVE, "error %d receiving on interface %d (%s)",
  195. dwErr, pite->ITE_Index, lpszAddr
  196. );
  197. LOGERR1(RECVFROM_FAILED, lpszAddr, dwErr);
  198. InterlockedIncrement(&pis->IS_ReceiveFailures);
  199. break;
  200. }
  201. dwInputSource = sinInputSource.sin_addr.s_addr;
  202. //
  203. // filter out packets we sent ourselves
  204. //
  205. if (GetIfByAddress(pTable, dwInputSource, NULL)) {
  206. break;
  207. }
  208. {
  209. PCHAR pStr1, pStr2;
  210. pStr1 = INET_NTOA(dwInputSource);
  211. if (pStr1)
  212. lstrcpy(szSource, pStr1);
  213. pStr2 = INET_NTOA(paddr->IA_Address);
  214. if (pStr1 && pStr2) {
  215. TRACE4(
  216. RECEIVE, "received %d-byte packet from %s on interface %d (%s)",
  217. iInputLength, szSource, pite->ITE_Index,
  218. pStr2
  219. );
  220. }
  221. }
  222. //
  223. // cast packet as a BOOTP message
  224. //
  225. pibp = (PIPBOOTP_PACKET)pInputPacket;
  226. //
  227. // consistency check 1: length of packet must exceed the length
  228. // of the BOOTP header
  229. //
  230. if (iInputLength < sizeof(IPBOOTP_PACKET)) {
  231. LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
  232. TRACE3(
  233. RECEIVE,
  234. "minimum BOOTP data is %d bytes, dropping %d byte packet from %s",
  235. sizeof(IPBOOTP_PACKET), iInputLength, szSource
  236. );
  237. LOGWARN2(PACKET_TOO_SMALL, lpszAddr, szSource, 0);
  238. break;
  239. }
  240. //
  241. // consistency check 2: op field must be either BOOTP_REQUEST
  242. // or BOOTP_REPLY
  243. //
  244. if (pibp->IP_Operation != IPBOOTP_OPERATION_REQUEST &&
  245. pibp->IP_Operation != IPBOOTP_OPERATION_REPLY) {
  246. LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
  247. TRACE2(
  248. RECEIVE,
  249. "dropping packet from %s due to unknown operation field %d",
  250. szSource, pibp->IP_Operation
  251. );
  252. LOGWARN2(PACKET_OPCODE_INVALID, lpszAddr, szSource, 0);
  253. break;
  254. }
  255. //
  256. // update statistics on incoming packets
  257. //
  258. switch (pibp->IP_Operation) {
  259. case IPBOOTP_OPERATION_REQUEST:
  260. InterlockedIncrement(&pis->IS_RequestsReceived);
  261. break;
  262. case IPBOOTP_OPERATION_REPLY:
  263. InterlockedIncrement(&pis->IS_RepliesReceived);
  264. break;
  265. }
  266. //
  267. // finish initializing the work context
  268. //
  269. pwc->IC_InterfaceIndex = pite->ITE_Index;
  270. pwc->IC_AddrIndex = dwAddrIndex;
  271. pwc->IC_InputSource = sinInputSource;
  272. pwc->IC_InputLength = iInputLength;
  273. //
  274. // place the packet on the receive queue
  275. //
  276. ACQUIRE_READ_LOCK(&ig.IG_RWL);
  277. ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
  278. dwErr = EnqueueRecvEntry(
  279. ig.IG_RecvQueue, (DWORD)pibp->IP_Operation, (PBYTE)pwc
  280. );
  281. RELEASE_LIST_LOCK(ig.IG_RecvQueue);
  282. RELEASE_READ_LOCK(&ig.IG_RWL);
  283. if (dwErr != NO_ERROR) {
  284. LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
  285. TRACE4(
  286. RECEIVE, "error %d queueing packet from %s on interface %d (%s)",
  287. dwErr, szSource, pite->ITE_Index, lpszAddr
  288. );
  289. LOGERR2(QUEUE_PACKET_FAILED, lpszAddr, szSource, dwErr);
  290. break;
  291. }
  292. //
  293. // queue the function to handle the packet
  294. //
  295. dwErr = QueueBootpWorker(WorkerFunctionProcessInput, NULL);
  296. if (dwErr != NO_ERROR) {
  297. PLIST_ENTRY phead;
  298. TRACE2(
  299. RECEIVE, "error %d queueing packet from %s for processing",
  300. dwErr, szSource
  301. );
  302. LOGERR0(QUEUE_WORKER_FAILED, dwErr);
  303. ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
  304. phead = &ig.IG_RecvQueue->LL_Head;
  305. RemoveTailList(phead);
  306. ig.IG_RecvQueueSize -= sizeof(RECV_QUEUE_ENTRY);
  307. RELEASE_LIST_LOCK(ig.IG_RecvQueue);
  308. break;
  309. }
  310. //
  311. // all went well, so we let the input processor free the packet
  312. //
  313. bFreePacket = FALSE;
  314. } while(FALSE);
  315. if (bFreePacket) { BOOTP_FREE(pwc); }
  316. return dwErr;
  317. }
  318. //----------------------------------------------------------------------------
  319. // Function: WorkerFunctionProcessInput
  320. //
  321. // This function processes an incoming packet.
  322. //----------------------------------------------------------------------------
  323. VOID
  324. WorkerFunctionProcessInput(
  325. PVOID pContext
  326. ) {
  327. PINPUT_CONTEXT pwc;
  328. DWORD dwErr, dwCommand;
  329. if (!ENTER_BOOTP_WORKER()) { return; }
  330. TRACE0(ENTER, "entering WorkerFunctionProcessInput");
  331. do {
  332. ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
  333. dwErr = DequeueRecvEntry(ig.IG_RecvQueue, &dwCommand, (PBYTE *)&pwc);
  334. RELEASE_LIST_LOCK(ig.IG_RecvQueue);
  335. if (dwErr != NO_ERROR) {
  336. TRACE1(
  337. RECEIVE, "error %d dequeueing packet from receive queue", dwErr
  338. );
  339. break;
  340. }
  341. switch (dwCommand) {
  342. case IPBOOTP_OPERATION_REQUEST:
  343. ProcessRequest(pwc);
  344. break;
  345. case IPBOOTP_OPERATION_REPLY:
  346. ProcessReply(pwc);
  347. break;
  348. }
  349. } while(FALSE);
  350. TRACE0(LEAVE, "leaving WorkerFunctionProcessInput");
  351. LEAVE_BOOTP_WORKER();
  352. }
  353. //----------------------------------------------------------------------------
  354. // Function: ProcessRequest
  355. //
  356. // This function handles the processing of BOOT_REQUEST messages
  357. //----------------------------------------------------------------------------
  358. VOID
  359. ProcessRequest(
  360. PVOID pContext
  361. ) {
  362. INT iErr;
  363. PIF_TABLE pTable;
  364. PINPUT_CONTEXT pwc;
  365. SOCKADDR_IN sinsrv;
  366. PIPBOOTP_PACKET pibp;
  367. PIF_TABLE_ENTRY pite;
  368. PIPBOOTP_IF_STATS pis;
  369. PIPBOOTP_IF_CONFIG pic;
  370. PIPBOOTP_IP_ADDRESS paddr;
  371. PIPBOOTP_GLOBAL_CONFIG pigc;
  372. DWORD dwErr, dwIndex, dwDhcpInformServer;
  373. PDWORD pdwAddr, pdwEnd;
  374. PDHCP_PACKET pdp;
  375. TRACE0(ENTER, "entering ProcessRequest");
  376. pwc = (PINPUT_CONTEXT)pContext;
  377. pTable = ig.IG_IfTable;
  378. ACQUIRE_READ_LOCK(&pTable->IT_RWL);
  379. do { // error breakout loop
  380. //
  381. // find the interface on which the input arrived
  382. //
  383. dwIndex = pwc->IC_InterfaceIndex;
  384. pite = GetIfByIndex(pTable, dwIndex);
  385. if (pite == NULL) {
  386. TRACE1(
  387. REQUEST, "processing request: interface %d not found", dwIndex
  388. );
  389. break;
  390. }
  391. pis = &pite->ITE_Stats;
  392. pic = pite->ITE_Config;
  393. //
  394. // Check if interface still bound to an IP address
  395. //
  396. if (pite->ITE_Binding == NULL) {
  397. TRACE1(
  398. REQUEST, "processing request: interface %d not bound",
  399. dwIndex
  400. );
  401. break;
  402. }
  403. paddr = IPBOOTP_IF_ADDRESS_TABLE(pite->ITE_Binding) + pwc->IC_AddrIndex;
  404. //
  405. // if we are not configured to relay, do nothing
  406. //
  407. if (pic->IC_RelayMode == IPBOOTP_RELAY_DISABLED) { break; }
  408. pibp = (PIPBOOTP_PACKET)pwc->IC_InputPacket;
  409. //
  410. // check the hop-count field to see if it is over the max hop-count
  411. // configured for this interface
  412. //
  413. if (pibp->IP_HopCount > IPBOOTP_MAX_HOP_COUNT ||
  414. pibp->IP_HopCount > pic->IC_MaxHopCount) {
  415. //
  416. // discard and log
  417. //
  418. CHAR szHops[12], *lpszAddr = INET_NTOA(paddr->IA_Address);
  419. _ltoa(pibp->IP_HopCount, szHops, 10);
  420. TRACE4(
  421. REQUEST,
  422. "dropping REQUEST with hop-count %d: max hop-count is %d on interface %d (%s)",
  423. pibp->IP_HopCount, pic->IC_MaxHopCount, dwIndex, lpszAddr
  424. );
  425. LOGWARN2(HOP_COUNT_TOO_HIGH, lpszAddr, szHops, 0);
  426. InterlockedIncrement(&pis->IS_RequestsDiscarded);
  427. break;
  428. }
  429. //
  430. // check the seconds threshold to make sure it is up to the minimum
  431. //
  432. if (pibp->IP_SecondsSinceBoot < pic->IC_MinSecondsSinceBoot) {
  433. //
  434. // discard and log
  435. //
  436. CHAR szSecs[12], *lpszAddr = INET_NTOA(paddr->IA_Address);
  437. _ltoa(pibp->IP_SecondsSinceBoot, szSecs, 10);
  438. TRACE3(
  439. REQUEST,
  440. "dropping REQUEST with secs-since-boot %d on interface %d (%s)",
  441. pibp->IP_SecondsSinceBoot, dwIndex, lpszAddr
  442. );
  443. LOGINFO2(SECS_SINCE_BOOT_TOO_LOW, lpszAddr, szSecs, 0);
  444. InterlockedIncrement(&pis->IS_RequestsDiscarded);
  445. break;
  446. }
  447. //
  448. // increment the hop-count
  449. //
  450. ++pibp->IP_HopCount;
  451. //
  452. // fill in relay agent IP address if it is empty
  453. //
  454. if (pibp->IP_AgentAddress == 0) {
  455. pibp->IP_AgentAddress = paddr->IA_Address;
  456. }
  457. //
  458. // if a dhcp-inform server has been set,
  459. // and this packet is a dhcp inform packet,
  460. // we will forward it to the dhcp-inform server.
  461. //
  462. pdp = (PDHCP_PACKET)(pibp + 1);
  463. if (!(dwDhcpInformServer = ig.IG_DhcpInformServer) ||
  464. pwc->IC_InputLength <
  465. sizeof(IPBOOTP_PACKET) + sizeof(DHCP_PACKET) + 1 ||
  466. *(DWORD UNALIGNED *)pdp->Cookie != DHCP_MAGIC_COOKIE ||
  467. pdp->Tag != DHCP_TAG_MESSAGE_TYPE ||
  468. pdp->Length != 1 ||
  469. pdp->Option[0] != DHCP_MESSAGE_INFORM
  470. ) {
  471. dwDhcpInformServer = 0;
  472. }
  473. //
  474. // relay the request to all configured BOOTP servers
  475. //
  476. ACQUIRE_READ_LOCK(&ig.IG_RWL);
  477. pigc = ig.IG_Config;
  478. if (dwDhcpInformServer) {
  479. pdwAddr = &dwDhcpInformServer;
  480. pdwEnd = pdwAddr + 1;
  481. }
  482. else {
  483. pdwAddr = (PDWORD)((PBYTE)pigc + sizeof(IPBOOTP_GLOBAL_CONFIG));
  484. pdwEnd = pdwAddr + pigc->GC_ServerCount;
  485. }
  486. for ( ; pdwAddr < pdwEnd; pdwAddr++) {
  487. sinsrv.sin_family = AF_INET;
  488. sinsrv.sin_port = htons(IPBOOTP_SERVER_PORT);
  489. sinsrv.sin_addr.s_addr = *pdwAddr;
  490. iErr = sendto(
  491. pite->ITE_Sockets[pwc->IC_AddrIndex],
  492. pwc->IC_InputPacket, pwc->IC_InputLength, 0,
  493. (PSOCKADDR)&sinsrv, sizeof(SOCKADDR_IN)
  494. );
  495. if (iErr == SOCKET_ERROR || iErr < (INT)pwc->IC_InputLength) {
  496. CHAR szSrv[20], *lpszAddr;
  497. dwErr = WSAGetLastError();
  498. if ((lpszAddr = INET_NTOA(*pdwAddr)) != NULL) {
  499. lstrcpy(szSrv, lpszAddr);
  500. lpszAddr = INET_NTOA(paddr->IA_Address);
  501. if (lpszAddr != NULL) {
  502. TRACE4(
  503. REQUEST,
  504. "error %d relaying REQUEST to server %s on interface %d (%s)",
  505. dwErr, szSrv, dwIndex, lpszAddr
  506. );
  507. LOGERR2(RELAY_REQUEST_FAILED, lpszAddr, szSrv, dwErr);
  508. }
  509. }
  510. InterlockedIncrement(&pis->IS_SendFailures);
  511. }
  512. }
  513. RELEASE_READ_LOCK(&ig.IG_RWL);
  514. } while(FALSE);
  515. RELEASE_READ_LOCK(&pTable->IT_RWL);
  516. BOOTP_FREE(pwc);
  517. TRACE0(LEAVE, "leaving ProcessRequest");
  518. return;
  519. }
  520. //----------------------------------------------------------------------------
  521. // Function: ProcessReply
  522. //
  523. // This function handles the relaying of BOOT_REPLY packets
  524. //----------------------------------------------------------------------------
  525. VOID
  526. ProcessReply(
  527. PVOID pContext
  528. ) {
  529. INT iErr;
  530. PIF_TABLE pTable;
  531. BOOL bArpUpdated;
  532. SOCKADDR_IN sincli;
  533. PINPUT_CONTEXT pwc;
  534. PIPBOOTP_PACKET pibp;
  535. PIPBOOTP_IP_ADDRESS paddrin, paddrout;
  536. PIPBOOTP_IF_STATS pisin, pisout;
  537. PIF_TABLE_ENTRY pitein, piteout;
  538. DWORD dwErr, dwIndex, dwAddress, dwAddrIndexOut;
  539. TRACE0(ENTER, "entering ProcessReply");
  540. pwc = (PINPUT_CONTEXT)pContext;
  541. pTable = ig.IG_IfTable;
  542. ACQUIRE_READ_LOCK(&pTable->IT_RWL);
  543. do { // error breakout loop
  544. //
  545. // get the interface on which the packet was received
  546. //
  547. dwIndex = pwc->IC_InterfaceIndex;
  548. pitein = GetIfByIndex(pTable, dwIndex);
  549. if (pitein == NULL) {
  550. TRACE1(REPLY, "processing REPLY: interface %d not found", dwIndex);
  551. break;
  552. }
  553. if (pitein->ITE_Binding == NULL) {
  554. TRACE1(REPLY, "processing REPLY: interface %d not bound", dwIndex);
  555. break;
  556. }
  557. paddrin = IPBOOTP_IF_ADDRESS_TABLE(pitein->ITE_Binding) +
  558. pwc->IC_AddrIndex;
  559. //
  560. // if we are not configured t relay on this interface, do nothing
  561. //
  562. if (pitein->ITE_Config->IC_RelayMode == IPBOOTP_RELAY_DISABLED) {
  563. TRACE2(
  564. REPLY,
  565. "dropping REPLY: relaying on interface %d (%s) is disabled",
  566. pitein->ITE_Index, INET_NTOA(paddrin->IA_Address)
  567. );
  568. break;
  569. }
  570. pisin = &pitein->ITE_Stats;
  571. //
  572. // place a template over the packet, and retrieve
  573. // the AgentAddress field; this contains the address
  574. // of the relay agent responsible for relaying this REPLY
  575. //
  576. pibp = (PIPBOOTP_PACKET)pwc->IC_InputPacket;
  577. dwAddress = pibp->IP_AgentAddress;
  578. //
  579. // see if the address in the reply matches any local interface
  580. //
  581. piteout = GetIfByAddress(pTable, dwAddress, &dwAddrIndexOut);
  582. if (piteout == NULL) {
  583. CHAR szAddress[20];
  584. PCHAR pStr1, pStr2;
  585. pStr1 = INET_NTOA(dwAddress);
  586. if (pStr1)
  587. lstrcpy(szAddress, pStr1);
  588. pStr2 = INET_NTOA(paddrin->IA_Address);
  589. if (pStr1 && pStr2) {
  590. TRACE3(
  591. REPLY,
  592. "dropping REPLY packet on interface %d (%s); no interfaces have address %s",
  593. pitein->ITE_Index, pStr2, szAddress
  594. );
  595. }
  596. InterlockedIncrement(&pisin->IS_RepliesDiscarded);
  597. break;
  598. }
  599. if (piteout->ITE_Binding == NULL) {
  600. TRACE1(REPLY, "processing REPLY: outgoing interface %d is not bound", dwIndex);
  601. break;
  602. }
  603. paddrout = IPBOOTP_IF_ADDRESS_TABLE(piteout->ITE_Binding) +
  604. dwAddrIndexOut;
  605. //
  606. // only relay if relay is enabled on the outgoing interface
  607. //
  608. if (piteout->ITE_Config->IC_RelayMode == IPBOOTP_RELAY_DISABLED) {
  609. TRACE2(
  610. REPLY,
  611. "dropping REPLY: relaying on interface %d (%s) is disabled",
  612. piteout->ITE_Index, INET_NTOA(paddrout->IA_Address)
  613. );
  614. break;
  615. }
  616. pisout = &piteout->ITE_Stats;
  617. //
  618. // the message must be relayed on the interface whose address
  619. // was specifed in the packet;
  620. //
  621. // if the broadcast bit is not set and the clients IP address
  622. // is in the packet, add an entry to the ARP cache for the client
  623. // and then relay the packet by unicast
  624. //
  625. sincli.sin_family = AF_INET;
  626. sincli.sin_port = htons(IPBOOTP_CLIENT_PORT);
  627. if ((pibp->IP_Flags & htons(IPBOOTP_FLAG_BROADCAST)) != 0 ||
  628. pibp->IP_OfferedAddress == 0) {
  629. //
  630. // the broadcast bit is set of the offered address is 0,
  631. // which is not an address we can add to the ARP cache;
  632. // in this case, send by broadcast
  633. //
  634. bArpUpdated = FALSE;
  635. sincli.sin_addr.s_addr = INADDR_BROADCAST;
  636. }
  637. else {
  638. //
  639. // attempt to seed the ARP cache with the address
  640. // offered to the client in the packet we are about
  641. // to send to the client.
  642. //
  643. dwErr = UpdateArpCache(
  644. piteout->ITE_Index, pibp->IP_OfferedAddress,
  645. (PBYTE)pibp->IP_MacAddr, pibp->IP_MacAddrLength,
  646. TRUE, ig.IG_FunctionTable
  647. );
  648. if (dwErr == NO_ERROR) {
  649. bArpUpdated = TRUE;
  650. sincli.sin_addr.s_addr = pibp->IP_OfferedAddress;
  651. }
  652. else {
  653. //
  654. // okay, that didn't work,
  655. // so fall back on broadcasting the packet
  656. //
  657. TRACE3(
  658. REPLY,
  659. "error %d adding entry to ARP cache on interface %d (%s)",
  660. dwErr, piteout->ITE_Index, INET_NTOA(paddrout->IA_Address)
  661. );
  662. bArpUpdated = FALSE;
  663. sincli.sin_addr.s_addr = INADDR_BROADCAST;
  664. InterlockedIncrement(&pisout->IS_ArpUpdateFailures);
  665. }
  666. }
  667. //
  668. // relay the packet
  669. //
  670. iErr = sendto(
  671. piteout->ITE_Sockets[dwAddrIndexOut], pwc->IC_InputPacket,
  672. pwc->IC_InputLength, 0,
  673. (PSOCKADDR)&sincli, sizeof(SOCKADDR_IN)
  674. );
  675. if (iErr == SOCKET_ERROR || iErr < (INT)pwc->IC_InputLength) {
  676. INT i;
  677. BYTE *pb;
  678. CHAR szCli[64], *psz, *lpszAddr, szDigits[] = "0123456789ABCDEF";
  679. dwErr = WSAGetLastError();
  680. lpszAddr = INET_NTOA(paddrout->IA_Address);
  681. //
  682. // format the client's hardware address
  683. //
  684. for (i = 0, psz = szCli, pb = pibp->IP_MacAddr;
  685. i < 16 && i < pibp->IP_MacAddrLength;
  686. i++, pb++) {
  687. *psz++ = szDigits[*pb / 16];
  688. *psz++ = szDigits[*pb % 16];
  689. *psz++ = ':';
  690. }
  691. (psz == szCli) ? (*psz = '\0') : (*(psz - 1) = '\0');
  692. TRACE4(
  693. REPLY,
  694. "error %d relaying REPLY to client %s on interface %d (%s)",
  695. dwErr, szCli, dwIndex, lpszAddr
  696. );
  697. LOGERR2(RELAY_REPLY_FAILED, lpszAddr, szCli, dwErr);
  698. InterlockedIncrement(&pisout->IS_SendFailures);
  699. }
  700. //
  701. // remove the ARP entry if one was added
  702. //
  703. if (bArpUpdated) {
  704. dwErr = UpdateArpCache(
  705. piteout->ITE_Index, pibp->IP_OfferedAddress,
  706. (PBYTE)pibp->IP_MacAddr, pibp->IP_MacAddrLength,
  707. FALSE, ig.IG_FunctionTable
  708. );
  709. if (dwErr != NO_ERROR) {
  710. InterlockedIncrement(&pisout->IS_ArpUpdateFailures);
  711. }
  712. }
  713. } while(FALSE);
  714. RELEASE_READ_LOCK(&pTable->IT_RWL);
  715. BOOTP_FREE(pwc);
  716. TRACE0(LEAVE, "leaving ProcessReply");
  717. return;
  718. }
  719. #define ClearScreen(h) { \
  720. DWORD _dwin,_dwout; \
  721. COORD _c = {0, 0}; \
  722. CONSOLE_SCREEN_BUFFER_INFO _csbi; \
  723. GetConsoleScreenBufferInfo(h,&_csbi); \
  724. _dwin = _csbi.dwSize.X * _csbi.dwSize.Y; \
  725. FillConsoleOutputCharacter(h,' ',_dwin,_c,&_dwout); \
  726. }
  727. VOID
  728. PrintGlobalConfig(
  729. HANDLE hConsole,
  730. PCOORD pc,
  731. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  732. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  733. );
  734. VOID
  735. PrintIfConfig(
  736. HANDLE hConsole,
  737. PCOORD pc,
  738. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  739. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  740. );
  741. VOID
  742. PrintIfBinding(
  743. HANDLE hConsole,
  744. PCOORD pc,
  745. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  746. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  747. );
  748. VOID
  749. PrintIfStats(
  750. HANDLE hConsole,
  751. PCOORD pc,
  752. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  753. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  754. );
  755. #if DBG
  756. VOID
  757. CallbackFunctionMibDisplay(
  758. PVOID pContext,
  759. BOOLEAN NotUsed
  760. ) {
  761. // enter/leaveBootpWorker not required as timer queue is persistent
  762. QueueBootpWorker(WorkerFunctionMibDisplay, pContext);
  763. return;
  764. }
  765. VOID
  766. WorkerFunctionMibDisplay(
  767. PVOID pContext
  768. ) {
  769. COORD c;
  770. INT iErr;
  771. FD_SET fdsRead;
  772. HANDLE hConsole;
  773. TIMEVAL tvTimeout;
  774. DWORD dwErr, dwTraceID;
  775. DWORD dwExactSize, dwInSize, dwOutSize;
  776. IPBOOTP_MIB_GET_INPUT_DATA imgid;
  777. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod;
  778. if (!ENTER_BOOTP_WORKER()) { return; }
  779. TraceGetConsole(ig.IG_MibTraceID, &hConsole);
  780. if (hConsole == NULL) {
  781. LEAVE_BOOTP_WORKER();
  782. return;
  783. }
  784. ClearScreen(hConsole);
  785. c.X = c.Y = 0;
  786. dwInSize = sizeof(imgid);
  787. imgid.IMGID_TypeID = IPBOOTP_GLOBAL_CONFIG_ID;
  788. dwOutSize = 0;
  789. pimgod = NULL;
  790. dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod);
  791. if (dwErr == ERROR_INSUFFICIENT_BUFFER) {
  792. pimgod = BOOTP_ALLOC(dwOutSize);
  793. if (pimgod) {
  794. dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod);
  795. }
  796. }
  797. while (dwErr == NO_ERROR) {
  798. switch(pimgod->IMGOD_TypeID) {
  799. case IPBOOTP_GLOBAL_CONFIG_ID:
  800. PrintGlobalConfig(hConsole, &c, &imgid, pimgod);
  801. break;
  802. case IPBOOTP_IF_CONFIG_ID:
  803. PrintIfConfig(hConsole, &c, &imgid, pimgod);
  804. break;
  805. case IPBOOTP_IF_BINDING_ID:
  806. PrintIfBinding(hConsole, &c, &imgid, pimgod);
  807. break;
  808. case IPBOOTP_IF_STATS_ID:
  809. PrintIfStats(hConsole, &c, &imgid, pimgod);
  810. break;
  811. default:
  812. break;
  813. }
  814. //
  815. // move to next line
  816. //
  817. ++c.Y;
  818. dwOutSize = 0;
  819. if (pimgod) { BOOTP_FREE(pimgod); pimgod = NULL; }
  820. dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod);
  821. if (dwErr == ERROR_INSUFFICIENT_BUFFER) {
  822. pimgod = BOOTP_ALLOC(dwOutSize);
  823. if (pimgod) {
  824. dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod);
  825. }
  826. }
  827. }
  828. if (pimgod != NULL) { BOOTP_FREE(pimgod); }
  829. LEAVE_BOOTP_WORKER();
  830. return;
  831. }
  832. #endif //if DBG
  833. #define WriteLine(h,c,fmt,arg) { \
  834. DWORD _dw; \
  835. CHAR _sz[200], _fmt[200]; \
  836. wsprintf(_fmt,"%-100s",fmt); \
  837. wsprintf(_sz,_fmt,arg); \
  838. WriteConsoleOutputCharacter(h,_sz,lstrlen(_sz),c,&_dw); \
  839. ++(c).Y; \
  840. }
  841. VOID
  842. PrintGlobalConfig(
  843. HANDLE hConsole,
  844. PCOORD pc,
  845. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  846. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  847. ) {
  848. PDWORD pdwsrv, pdwsrvend;
  849. PIPBOOTP_GLOBAL_CONFIG pgc;
  850. pgc = (PIPBOOTP_GLOBAL_CONFIG)pimgod->IMGOD_Buffer;
  851. WriteLine(
  852. hConsole,
  853. *pc,
  854. "Logging Level: %d",
  855. pgc->GC_LoggingLevel
  856. );
  857. WriteLine(
  858. hConsole,
  859. *pc,
  860. "Max Receive Queue Size: %d",
  861. pgc->GC_MaxRecvQueueSize
  862. );
  863. WriteLine(
  864. hConsole,
  865. *pc,
  866. "BOOTP Server Count: %d",
  867. pgc->GC_ServerCount
  868. );
  869. pdwsrv = (PDWORD)(pgc + 1);
  870. pdwsrvend = pdwsrv + pgc->GC_ServerCount;
  871. for ( ; pdwsrv < pdwsrvend; pdwsrv++) {
  872. WriteLine(
  873. hConsole,
  874. *pc,
  875. "BOOTP Server: %s",
  876. INET_NTOA(*pdwsrv)
  877. );
  878. }
  879. pimgid->IMGID_TypeID = IPBOOTP_GLOBAL_CONFIG_ID;
  880. }
  881. VOID
  882. PrintIfConfig(
  883. HANDLE hConsole,
  884. PCOORD pc,
  885. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  886. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  887. ) {
  888. CHAR szMode[20];
  889. PIPBOOTP_IF_CONFIG pic;
  890. pic = (PIPBOOTP_IF_CONFIG)pimgod->IMGOD_Buffer;
  891. switch (pic->IC_RelayMode) {
  892. case IPBOOTP_RELAY_ENABLED:
  893. strcpy(szMode, "enabled"); break;
  894. case IPBOOTP_RELAY_DISABLED:
  895. strcpy(szMode, "disabled"); break;
  896. default:
  897. break;
  898. }
  899. WriteLine(
  900. hConsole,
  901. *pc,
  902. "Interface Index: %d",
  903. pimgod->IMGOD_IfIndex
  904. );
  905. WriteLine(
  906. hConsole,
  907. *pc,
  908. "Relay Mode: %s",
  909. szMode
  910. );
  911. WriteLine(
  912. hConsole,
  913. *pc,
  914. "Max Hop Count: %d",
  915. pic->IC_MaxHopCount
  916. );
  917. WriteLine(
  918. hConsole,
  919. *pc,
  920. "Min Seconds Since Boot: %d",
  921. pic->IC_MinSecondsSinceBoot
  922. );
  923. pimgid->IMGID_TypeID = IPBOOTP_IF_CONFIG_ID;
  924. pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
  925. }
  926. VOID
  927. PrintIfBinding(
  928. HANDLE hConsole,
  929. PCOORD pc,
  930. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  931. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  932. ) {
  933. DWORD i;
  934. CHAR szAddr[64];
  935. PIPBOOTP_IF_BINDING pib;
  936. PIPBOOTP_IP_ADDRESS paddr;
  937. pib = (PIPBOOTP_IF_BINDING)pimgod->IMGOD_Buffer;
  938. paddr = IPBOOTP_IF_ADDRESS_TABLE(pib);
  939. WriteLine(
  940. hConsole, *pc, "Interface Index: %d",
  941. pimgod->IMGOD_IfIndex
  942. );
  943. WriteLine(
  944. hConsole, *pc, "Address Count: %d",
  945. pib->IB_AddrCount
  946. );
  947. for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
  948. LPSTR szTemp;
  949. szTemp = INET_NTOA(paddr->IA_Address);
  950. if (szTemp != NULL) {
  951. lstrcpy(szAddr, szTemp);
  952. lstrcat(szAddr, " - ");
  953. szTemp = INET_NTOA(paddr->IA_Netmask);
  954. if ( szTemp != NULL ) { lstrcat(szAddr, szTemp); }
  955. WriteLine(
  956. hConsole, *pc, "Address Entry: %s",
  957. szAddr
  958. );
  959. }
  960. }
  961. pimgid->IMGID_TypeID = IPBOOTP_IF_BINDING_ID;
  962. pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
  963. }
  964. VOID
  965. PrintIfStats(
  966. HANDLE hConsole,
  967. PCOORD pc,
  968. PIPBOOTP_MIB_GET_INPUT_DATA pimgid,
  969. PIPBOOTP_MIB_GET_OUTPUT_DATA pimgod
  970. ) {
  971. PIPBOOTP_IF_STATS pis;
  972. pis = (PIPBOOTP_IF_STATS)pimgod->IMGOD_Buffer;
  973. WriteLine(
  974. hConsole,
  975. *pc,
  976. "Interface Index: %d",
  977. pimgod->IMGOD_IfIndex
  978. );
  979. WriteLine(
  980. hConsole,
  981. *pc,
  982. "Send Failures: %d",
  983. pis->IS_SendFailures
  984. );
  985. WriteLine(
  986. hConsole,
  987. *pc,
  988. "Receive Failures: %d",
  989. pis->IS_ReceiveFailures
  990. );
  991. WriteLine(
  992. hConsole,
  993. *pc,
  994. "ARP Cache Update Failures: %d",
  995. pis->IS_ArpUpdateFailures
  996. );
  997. WriteLine(
  998. hConsole,
  999. *pc,
  1000. "Requests Received: %d",
  1001. pis->IS_RequestsReceived
  1002. );
  1003. WriteLine(
  1004. hConsole,
  1005. *pc,
  1006. "Requests Discarded: %d",
  1007. pis->IS_RequestsDiscarded
  1008. );
  1009. WriteLine(
  1010. hConsole,
  1011. *pc,
  1012. "Replies Received: %d",
  1013. pis->IS_RepliesReceived
  1014. );
  1015. WriteLine(
  1016. hConsole,
  1017. *pc,
  1018. "Replies Discarded: %d",
  1019. pis->IS_RepliesDiscarded
  1020. );
  1021. pimgid->IMGID_TypeID = IPBOOTP_IF_STATS_ID;
  1022. pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
  1023. }