Dhcp4Io.c 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620
  1. /** @file
  2. EFI DHCP protocol implementation.
  3. Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "Dhcp4Impl.h"
  7. UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
  8. /**
  9. Send an initial DISCOVER or REQUEST message according to the
  10. DHCP service's current state.
  11. @param[in] DhcpSb The DHCP service instance
  12. @retval EFI_SUCCESS The request has been sent
  13. @retval other Some error occurs when sending the request.
  14. **/
  15. EFI_STATUS
  16. DhcpInitRequest (
  17. IN DHCP_SERVICE *DhcpSb
  18. )
  19. {
  20. EFI_STATUS Status;
  21. ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
  22. //
  23. // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
  24. //
  25. DhcpSb->ActiveChild->ElaspedTime = 0;
  26. if (DhcpSb->DhcpState == Dhcp4Init) {
  27. DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
  28. Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
  29. if (EFI_ERROR (Status)) {
  30. DhcpSb->DhcpState = Dhcp4Init;
  31. return Status;
  32. }
  33. } else {
  34. DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
  35. Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
  36. if (EFI_ERROR (Status)) {
  37. DhcpSb->DhcpState = Dhcp4InitReboot;
  38. return Status;
  39. }
  40. }
  41. return EFI_SUCCESS;
  42. }
  43. /**
  44. Call user provided callback function, and return the value the
  45. function returns. If the user doesn't provide a callback, a
  46. proper return value is selected to let the caller continue the
  47. normal process.
  48. @param[in] DhcpSb The DHCP service instance
  49. @param[in] Event The event as defined in the spec
  50. @param[in] Packet The current packet trigger the event
  51. @param[out] NewPacket The user's return new packet
  52. @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
  53. @retval EFI_SUCCESS The user function returns success.
  54. @retval EFI_ABORTED The user function ask it to abort.
  55. **/
  56. EFI_STATUS
  57. DhcpCallUser (
  58. IN DHCP_SERVICE *DhcpSb,
  59. IN EFI_DHCP4_EVENT Event,
  60. IN EFI_DHCP4_PACKET *Packet OPTIONAL,
  61. OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
  62. )
  63. {
  64. EFI_DHCP4_CONFIG_DATA *Config;
  65. EFI_STATUS Status;
  66. if (NewPacket != NULL) {
  67. *NewPacket = NULL;
  68. }
  69. //
  70. // If user doesn't provide the call back function, return the value
  71. // that directs the client to continue the normal process.
  72. // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
  73. // the offers and select a offer, EFI_NOT_READY tells the client to
  74. // collect more offers.
  75. //
  76. Config = &DhcpSb->ActiveConfig;
  77. if (Config->Dhcp4Callback == NULL) {
  78. if (Event == Dhcp4RcvdOffer) {
  79. return EFI_NOT_READY;
  80. }
  81. return EFI_SUCCESS;
  82. }
  83. Status = Config->Dhcp4Callback (
  84. &DhcpSb->ActiveChild->Dhcp4Protocol,
  85. Config->CallbackContext,
  86. (EFI_DHCP4_STATE)DhcpSb->DhcpState,
  87. Event,
  88. Packet,
  89. NewPacket
  90. );
  91. //
  92. // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
  93. // and EFI_ABORTED. If it returns values other than those, assume
  94. // it to be EFI_ABORTED.
  95. //
  96. if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
  97. return Status;
  98. }
  99. return EFI_ABORTED;
  100. }
  101. /**
  102. Notify the user about the operation result.
  103. @param DhcpSb DHCP service instance
  104. @param Which Which notify function to signal
  105. **/
  106. VOID
  107. DhcpNotifyUser (
  108. IN DHCP_SERVICE *DhcpSb,
  109. IN INTN Which
  110. )
  111. {
  112. DHCP_PROTOCOL *Child;
  113. if ((Child = DhcpSb->ActiveChild) == NULL) {
  114. return;
  115. }
  116. if ((Child->CompletionEvent != NULL) &&
  117. ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))
  118. )
  119. {
  120. gBS->SignalEvent (Child->CompletionEvent);
  121. Child->CompletionEvent = NULL;
  122. }
  123. if ((Child->RenewRebindEvent != NULL) &&
  124. ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))
  125. )
  126. {
  127. gBS->SignalEvent (Child->RenewRebindEvent);
  128. Child->RenewRebindEvent = NULL;
  129. }
  130. }
  131. /**
  132. Set the DHCP state. If CallUser is true, it will try to notify
  133. the user before change the state by DhcpNotifyUser. It returns
  134. EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
  135. EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
  136. the return value of this function.
  137. @param DhcpSb The DHCP service instance
  138. @param State The new DHCP state to change to
  139. @param CallUser Whether we need to call user
  140. @retval EFI_SUCCESS The state is changed
  141. @retval EFI_ABORTED The user asks to abort the DHCP process.
  142. **/
  143. EFI_STATUS
  144. DhcpSetState (
  145. IN OUT DHCP_SERVICE *DhcpSb,
  146. IN INTN State,
  147. IN BOOLEAN CallUser
  148. )
  149. {
  150. EFI_STATUS Status;
  151. if (CallUser) {
  152. Status = EFI_SUCCESS;
  153. if (State == Dhcp4Renewing) {
  154. Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
  155. } else if (State == Dhcp4Rebinding) {
  156. Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
  157. } else if (State == Dhcp4Bound) {
  158. Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
  159. }
  160. if (EFI_ERROR (Status)) {
  161. return Status;
  162. }
  163. }
  164. //
  165. // Update the retransmission timer during the state transition.
  166. // This will clear the retry count. This is also why the rule
  167. // first transit the state, then send packets.
  168. //
  169. if (State == Dhcp4Selecting) {
  170. DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
  171. } else {
  172. DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
  173. }
  174. if (DhcpSb->MaxRetries == 0) {
  175. DhcpSb->MaxRetries = 4;
  176. }
  177. DhcpSb->CurRetry = 0;
  178. DhcpSb->PacketToLive = 0;
  179. DhcpSb->LastTimeout = 0;
  180. DhcpSb->DhcpState = State;
  181. return EFI_SUCCESS;
  182. }
  183. /**
  184. Set the retransmit timer for the packet. It will select from either
  185. the discover timeouts/request timeouts or the default timeout values.
  186. @param DhcpSb The DHCP service instance.
  187. **/
  188. VOID
  189. DhcpSetTransmitTimer (
  190. IN OUT DHCP_SERVICE *DhcpSb
  191. )
  192. {
  193. UINT32 *Times;
  194. ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
  195. if (DhcpSb->DhcpState == Dhcp4Selecting) {
  196. Times = DhcpSb->ActiveConfig.DiscoverTimeout;
  197. } else {
  198. Times = DhcpSb->ActiveConfig.RequestTimeout;
  199. }
  200. if (Times == NULL) {
  201. Times = mDhcp4DefaultTimeout;
  202. }
  203. DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
  204. DhcpSb->LastTimeout = DhcpSb->PacketToLive;
  205. return;
  206. }
  207. /**
  208. Compute the lease. If the server grants a permanent lease, just
  209. process it as a normal timeout value since the lease will last
  210. more than 100 years.
  211. @param DhcpSb The DHCP service instance
  212. @param Para The DHCP parameter extracted from the server's
  213. response.
  214. **/
  215. VOID
  216. DhcpComputeLease (
  217. IN OUT DHCP_SERVICE *DhcpSb,
  218. IN DHCP_PARAMETER *Para
  219. )
  220. {
  221. ASSERT (Para != NULL);
  222. DhcpSb->Lease = Para->Lease;
  223. DhcpSb->T2 = Para->T2;
  224. DhcpSb->T1 = Para->T1;
  225. if (DhcpSb->Lease == 0) {
  226. DhcpSb->Lease = DHCP_DEFAULT_LEASE;
  227. }
  228. if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
  229. DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
  230. }
  231. if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
  232. DhcpSb->T1 = DhcpSb->Lease >> 1;
  233. }
  234. }
  235. /**
  236. Configure a UDP IO port to use the acquired lease address.
  237. DHCP driver needs this port to unicast packet to the server
  238. such as DHCP release.
  239. @param[in] UdpIo The UDP IO to configure
  240. @param[in] Context Dhcp service instance.
  241. @retval EFI_SUCCESS The UDP IO port is successfully configured.
  242. @retval Others It failed to configure the port.
  243. **/
  244. EFI_STATUS
  245. EFIAPI
  246. DhcpConfigLeaseIoPort (
  247. IN UDP_IO *UdpIo,
  248. IN VOID *Context
  249. )
  250. {
  251. EFI_UDP4_CONFIG_DATA UdpConfigData;
  252. EFI_IPv4_ADDRESS Subnet;
  253. EFI_IPv4_ADDRESS Gateway;
  254. DHCP_SERVICE *DhcpSb;
  255. EFI_STATUS Status;
  256. IP4_ADDR Ip;
  257. DhcpSb = (DHCP_SERVICE *)Context;
  258. UdpConfigData.AcceptBroadcast = FALSE;
  259. UdpConfigData.AcceptPromiscuous = FALSE;
  260. UdpConfigData.AcceptAnyPort = FALSE;
  261. UdpConfigData.AllowDuplicatePort = TRUE;
  262. UdpConfigData.TypeOfService = 0;
  263. UdpConfigData.TimeToLive = 64;
  264. UdpConfigData.DoNotFragment = FALSE;
  265. UdpConfigData.ReceiveTimeout = 1;
  266. UdpConfigData.TransmitTimeout = 0;
  267. UdpConfigData.UseDefaultAddress = FALSE;
  268. UdpConfigData.StationPort = DHCP_CLIENT_PORT;
  269. UdpConfigData.RemotePort = DHCP_SERVER_PORT;
  270. Ip = HTONL (DhcpSb->ClientAddr);
  271. CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
  272. Ip = HTONL (DhcpSb->Netmask);
  273. CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
  274. ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
  275. Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
  276. if (EFI_ERROR (Status)) {
  277. return Status;
  278. }
  279. //
  280. // Add a default route if received from the server.
  281. //
  282. if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
  283. ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
  284. Ip = HTONL (DhcpSb->Para->Router);
  285. CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
  286. UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);
  287. }
  288. return EFI_SUCCESS;
  289. }
  290. /**
  291. Update the lease states when a new lease is acquired. It will not only
  292. save the acquired the address and lease time, it will also create a UDP
  293. child to provide address resolution for the address.
  294. @param DhcpSb The DHCP service instance
  295. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
  296. @retval EFI_SUCCESS The lease is recorded.
  297. **/
  298. EFI_STATUS
  299. DhcpLeaseAcquired (
  300. IN OUT DHCP_SERVICE *DhcpSb
  301. )
  302. {
  303. DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
  304. if (DhcpSb->Para != NULL) {
  305. DhcpSb->Netmask = DhcpSb->Para->NetMask;
  306. DhcpSb->ServerAddr = DhcpSb->Para->ServerId;
  307. }
  308. if (DhcpSb->Netmask == 0) {
  309. return EFI_ABORTED;
  310. }
  311. if (DhcpSb->LeaseIoPort != NULL) {
  312. UdpIoFreeIo (DhcpSb->LeaseIoPort);
  313. }
  314. //
  315. // Create a UDP/IP child to provide ARP service for the Leased IP,
  316. // and transmit unicast packet with it as source address. Don't
  317. // start receive on this port, the queued packet will be timeout.
  318. //
  319. DhcpSb->LeaseIoPort = UdpIoCreateIo (
  320. DhcpSb->Controller,
  321. DhcpSb->Image,
  322. DhcpConfigLeaseIoPort,
  323. UDP_IO_UDP4_VERSION,
  324. DhcpSb
  325. );
  326. if (DhcpSb->LeaseIoPort == NULL) {
  327. return EFI_OUT_OF_RESOURCES;
  328. }
  329. if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
  330. DhcpComputeLease (DhcpSb, DhcpSb->Para);
  331. }
  332. return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
  333. }
  334. /**
  335. Clean up the DHCP related states, IoStatus isn't reset.
  336. @param DhcpSb The DHCP instance service.
  337. **/
  338. VOID
  339. DhcpCleanLease (
  340. IN DHCP_SERVICE *DhcpSb
  341. )
  342. {
  343. DhcpSb->DhcpState = Dhcp4Init;
  344. DhcpSb->Xid = DhcpSb->Xid + 1;
  345. DhcpSb->ClientAddr = 0;
  346. DhcpSb->Netmask = 0;
  347. DhcpSb->ServerAddr = 0;
  348. if (DhcpSb->LastOffer != NULL) {
  349. FreePool (DhcpSb->LastOffer);
  350. DhcpSb->LastOffer = NULL;
  351. }
  352. if (DhcpSb->Selected != NULL) {
  353. FreePool (DhcpSb->Selected);
  354. DhcpSb->Selected = NULL;
  355. }
  356. if (DhcpSb->Para != NULL) {
  357. FreePool (DhcpSb->Para);
  358. DhcpSb->Para = NULL;
  359. }
  360. DhcpSb->Lease = 0;
  361. DhcpSb->T1 = 0;
  362. DhcpSb->T2 = 0;
  363. DhcpSb->ExtraRefresh = FALSE;
  364. if (DhcpSb->LeaseIoPort != NULL) {
  365. UdpIoFreeIo (DhcpSb->LeaseIoPort);
  366. DhcpSb->LeaseIoPort = NULL;
  367. }
  368. if (DhcpSb->LastPacket != NULL) {
  369. FreePool (DhcpSb->LastPacket);
  370. DhcpSb->LastPacket = NULL;
  371. }
  372. DhcpSb->PacketToLive = 0;
  373. DhcpSb->LastTimeout = 0;
  374. DhcpSb->CurRetry = 0;
  375. DhcpSb->MaxRetries = 0;
  376. DhcpSb->LeaseLife = 0;
  377. //
  378. // Clean active config data.
  379. //
  380. DhcpCleanConfigure (&DhcpSb->ActiveConfig);
  381. }
  382. /**
  383. Select a offer among all the offers collected. If the offer selected is
  384. of BOOTP, the lease is recorded and user notified. If the offer is of
  385. DHCP, it will request the offer from the server.
  386. @param[in] DhcpSb The DHCP service instance.
  387. @retval EFI_SUCCESS One of the offer is selected.
  388. **/
  389. EFI_STATUS
  390. DhcpChooseOffer (
  391. IN DHCP_SERVICE *DhcpSb
  392. )
  393. {
  394. EFI_DHCP4_PACKET *Selected;
  395. EFI_DHCP4_PACKET *NewPacket;
  396. EFI_DHCP4_PACKET *TempPacket;
  397. EFI_STATUS Status;
  398. ASSERT (DhcpSb->LastOffer != NULL);
  399. //
  400. // User will cache previous offers if he wants to select
  401. // from multiple offers. If user provides an invalid packet,
  402. // use the last offer, otherwise use the provided packet.
  403. //
  404. NewPacket = NULL;
  405. Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
  406. if (EFI_ERROR (Status)) {
  407. return Status;
  408. }
  409. Selected = DhcpSb->LastOffer;
  410. if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
  411. TempPacket = (EFI_DHCP4_PACKET *)AllocatePool (NewPacket->Size);
  412. if (TempPacket != NULL) {
  413. CopyMem (TempPacket, NewPacket, NewPacket->Size);
  414. FreePool (Selected);
  415. Selected = TempPacket;
  416. }
  417. }
  418. DhcpSb->Selected = Selected;
  419. DhcpSb->LastOffer = NULL;
  420. DhcpSb->Para = NULL;
  421. DhcpValidateOptions (Selected, &DhcpSb->Para);
  422. //
  423. // A bootp offer has been selected, save the lease status,
  424. // enter bound state then notify the user.
  425. //
  426. if (DHCP_IS_BOOTP (DhcpSb->Para)) {
  427. Status = DhcpLeaseAcquired (DhcpSb);
  428. if (EFI_ERROR (Status)) {
  429. return Status;
  430. }
  431. DhcpSb->IoStatus = EFI_SUCCESS;
  432. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
  433. return EFI_SUCCESS;
  434. }
  435. //
  436. // Send a DHCP requests
  437. //
  438. Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
  439. if (EFI_ERROR (Status)) {
  440. return Status;
  441. }
  442. return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
  443. }
  444. /**
  445. Terminate the current address acquire. All the allocated resources
  446. are released. Be careful when calling this function. A rule related
  447. to this is: only call DhcpEndSession at the highest level, such as
  448. DhcpInput, DhcpOnTimerTick...At the other level, just return error.
  449. @param[in] DhcpSb The DHCP service instance
  450. @param[in] Status The result of the DHCP process.
  451. **/
  452. VOID
  453. DhcpEndSession (
  454. IN DHCP_SERVICE *DhcpSb,
  455. IN EFI_STATUS Status
  456. )
  457. {
  458. if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
  459. DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
  460. } else {
  461. DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
  462. }
  463. DhcpCleanLease (DhcpSb);
  464. DhcpSb->IoStatus = Status;
  465. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
  466. }
  467. /**
  468. Handle packets in DHCP select state.
  469. @param[in] DhcpSb The DHCP service instance
  470. @param[in] Packet The DHCP packet received
  471. @param[in] Para The DHCP parameter extracted from the packet. That
  472. is, all the option value that we care.
  473. @retval EFI_SUCCESS The packet is successfully processed.
  474. @retval Others Some error occurred.
  475. **/
  476. EFI_STATUS
  477. DhcpHandleSelect (
  478. IN DHCP_SERVICE *DhcpSb,
  479. IN EFI_DHCP4_PACKET *Packet,
  480. IN DHCP_PARAMETER *Para
  481. )
  482. {
  483. EFI_STATUS Status;
  484. Status = EFI_SUCCESS;
  485. //
  486. // First validate the message:
  487. // 1. the offer is a unicast
  488. // 2. if it is a DHCP message, it must contains a server ID.
  489. // Don't return a error for these two case otherwise the session is ended.
  490. //
  491. if (!DHCP_IS_BOOTP (Para) &&
  492. ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
  493. )
  494. {
  495. goto ON_EXIT;
  496. }
  497. //
  498. // Call the user's callback. The action according to the return is as:
  499. // 1. EFI_SUCCESS: stop waiting for more offers, select the offer now
  500. // 2. EFI_NOT_READY: wait for more offers
  501. // 3. EFI_ABORTED: abort the address acquiring.
  502. //
  503. Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
  504. if (Status == EFI_SUCCESS) {
  505. if (DhcpSb->LastOffer != NULL) {
  506. FreePool (DhcpSb->LastOffer);
  507. }
  508. DhcpSb->LastOffer = Packet;
  509. return DhcpChooseOffer (DhcpSb);
  510. } else if (Status == EFI_NOT_READY) {
  511. if (DhcpSb->LastOffer != NULL) {
  512. FreePool (DhcpSb->LastOffer);
  513. }
  514. DhcpSb->LastOffer = Packet;
  515. } else if (Status == EFI_ABORTED) {
  516. //
  517. // DhcpInput will end the session upon error return. Remember
  518. // only to call DhcpEndSession at the top level call.
  519. //
  520. goto ON_EXIT;
  521. }
  522. return EFI_SUCCESS;
  523. ON_EXIT:
  524. FreePool (Packet);
  525. return Status;
  526. }
  527. /**
  528. Handle packets in DHCP request state.
  529. @param[in] DhcpSb The DHCP service instance
  530. @param[in] Packet The DHCP packet received
  531. @param[in] Para The DHCP parameter extracted from the packet. That
  532. is, all the option value that we care.
  533. @retval EFI_SUCCESS The packet is successfully processed.
  534. @retval Others Some error occurred.
  535. **/
  536. EFI_STATUS
  537. DhcpHandleRequest (
  538. IN DHCP_SERVICE *DhcpSb,
  539. IN EFI_DHCP4_PACKET *Packet,
  540. IN DHCP_PARAMETER *Para
  541. )
  542. {
  543. EFI_DHCP4_HEADER *Head;
  544. EFI_DHCP4_HEADER *Selected;
  545. EFI_STATUS Status;
  546. UINT8 *Message;
  547. ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
  548. Head = &Packet->Dhcp4.Header;
  549. Selected = &DhcpSb->Selected->Dhcp4.Header;
  550. //
  551. // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
  552. //
  553. if (DHCP_IS_BOOTP (Para) ||
  554. (Para->ServerId != DhcpSb->Para->ServerId) ||
  555. ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
  556. )
  557. {
  558. Status = EFI_SUCCESS;
  559. goto ON_EXIT;
  560. }
  561. //
  562. // Received a NAK, end the session no matter what the user returns
  563. //
  564. Status = EFI_DEVICE_ERROR;
  565. if (Para->DhcpType == DHCP_MSG_NAK) {
  566. DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
  567. goto ON_EXIT;
  568. }
  569. //
  570. // Check whether the ACK matches the selected offer
  571. //
  572. Message = NULL;
  573. if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
  574. Message = (UINT8 *)"Lease confirmed isn't the same as that in the offer";
  575. goto REJECT;
  576. }
  577. Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
  578. if (EFI_ERROR (Status)) {
  579. Message = (UINT8 *)"Lease is denied upon received ACK";
  580. goto REJECT;
  581. }
  582. //
  583. // Record the lease, transit to BOUND state, then notify the user
  584. //
  585. Status = DhcpLeaseAcquired (DhcpSb);
  586. if (EFI_ERROR (Status)) {
  587. Message = (UINT8 *)"Lease is denied upon entering bound";
  588. goto REJECT;
  589. }
  590. DhcpSb->IoStatus = EFI_SUCCESS;
  591. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
  592. FreePool (Packet);
  593. return EFI_SUCCESS;
  594. REJECT:
  595. DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
  596. ON_EXIT:
  597. FreePool (Packet);
  598. return Status;
  599. }
  600. /**
  601. Handle packets in DHCP renew/rebound state.
  602. @param[in] DhcpSb The DHCP service instance
  603. @param[in] Packet The DHCP packet received
  604. @param[in] Para The DHCP parameter extracted from the packet. That
  605. is, all the option value that we care.
  606. @retval EFI_SUCCESS The packet is successfully processed.
  607. @retval Others Some error occurred.
  608. **/
  609. EFI_STATUS
  610. DhcpHandleRenewRebind (
  611. IN DHCP_SERVICE *DhcpSb,
  612. IN EFI_DHCP4_PACKET *Packet,
  613. IN DHCP_PARAMETER *Para
  614. )
  615. {
  616. EFI_DHCP4_HEADER *Head;
  617. EFI_DHCP4_HEADER *Selected;
  618. EFI_STATUS Status;
  619. ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
  620. Head = &Packet->Dhcp4.Header;
  621. Selected = &DhcpSb->Selected->Dhcp4.Header;
  622. //
  623. // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
  624. //
  625. if (DHCP_IS_BOOTP (Para) ||
  626. (Para->ServerId != DhcpSb->Para->ServerId) ||
  627. ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
  628. )
  629. {
  630. Status = EFI_SUCCESS;
  631. goto ON_EXIT;
  632. }
  633. //
  634. // Received a NAK, ignore the user's return then terminate the process
  635. //
  636. Status = EFI_DEVICE_ERROR;
  637. if (Para->DhcpType == DHCP_MSG_NAK) {
  638. DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
  639. goto ON_EXIT;
  640. }
  641. //
  642. // The lease is different from the selected. Don't send a DECLINE
  643. // since it isn't existed in the client's FSM.
  644. //
  645. if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
  646. goto ON_EXIT;
  647. }
  648. Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
  649. if (EFI_ERROR (Status)) {
  650. goto ON_EXIT;
  651. }
  652. //
  653. // Record the lease, start timer for T1 and T2,
  654. //
  655. DhcpComputeLease (DhcpSb, Para);
  656. DhcpSb->LeaseLife = 0;
  657. DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
  658. if (DhcpSb->ExtraRefresh != 0) {
  659. DhcpSb->ExtraRefresh = FALSE;
  660. DhcpSb->IoStatus = EFI_SUCCESS;
  661. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
  662. }
  663. ON_EXIT:
  664. FreePool (Packet);
  665. return Status;
  666. }
  667. /**
  668. Handle packets in DHCP reboot state.
  669. @param[in] DhcpSb The DHCP service instance
  670. @param[in] Packet The DHCP packet received
  671. @param[in] Para The DHCP parameter extracted from the packet. That
  672. is, all the option value that we care.
  673. @retval EFI_SUCCESS The packet is successfully processed.
  674. @retval Others Some error occurred.
  675. **/
  676. EFI_STATUS
  677. DhcpHandleReboot (
  678. IN DHCP_SERVICE *DhcpSb,
  679. IN EFI_DHCP4_PACKET *Packet,
  680. IN DHCP_PARAMETER *Para
  681. )
  682. {
  683. EFI_DHCP4_HEADER *Head;
  684. EFI_STATUS Status;
  685. Head = &Packet->Dhcp4.Header;
  686. //
  687. // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
  688. //
  689. if (DHCP_IS_BOOTP (Para) ||
  690. ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
  691. )
  692. {
  693. Status = EFI_SUCCESS;
  694. goto ON_EXIT;
  695. }
  696. //
  697. // If a NAK is received, transit to INIT and try again.
  698. //
  699. if (Para->DhcpType == DHCP_MSG_NAK) {
  700. DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
  701. DhcpSb->ClientAddr = 0;
  702. DhcpSb->DhcpState = Dhcp4Init;
  703. Status = DhcpInitRequest (DhcpSb);
  704. goto ON_EXIT;
  705. }
  706. //
  707. // Check whether the ACK matches the selected offer
  708. //
  709. if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
  710. Status = EFI_DEVICE_ERROR;
  711. goto ON_EXIT;
  712. }
  713. Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
  714. if (EFI_ERROR (Status)) {
  715. goto ON_EXIT;
  716. }
  717. //
  718. // OK, get the parameter from server, record the lease
  719. //
  720. DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
  721. if (DhcpSb->Para == NULL) {
  722. Status = EFI_OUT_OF_RESOURCES;
  723. goto ON_EXIT;
  724. }
  725. DhcpSb->Selected = Packet;
  726. Status = DhcpLeaseAcquired (DhcpSb);
  727. if (EFI_ERROR (Status)) {
  728. return Status;
  729. }
  730. DhcpSb->IoStatus = EFI_SUCCESS;
  731. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
  732. return EFI_SUCCESS;
  733. ON_EXIT:
  734. FreePool (Packet);
  735. return Status;
  736. }
  737. /**
  738. Handle the received DHCP packets. This function drives the DHCP
  739. state machine.
  740. @param UdpPacket The UDP packets received.
  741. @param EndPoint The local/remote UDP access point
  742. @param IoStatus The status of the UDP receive
  743. @param Context The opaque parameter to the function.
  744. **/
  745. VOID
  746. EFIAPI
  747. DhcpInput (
  748. NET_BUF *UdpPacket,
  749. UDP_END_POINT *EndPoint,
  750. EFI_STATUS IoStatus,
  751. VOID *Context
  752. )
  753. {
  754. DHCP_SERVICE *DhcpSb;
  755. EFI_DHCP4_HEADER *Head;
  756. EFI_DHCP4_PACKET *Packet;
  757. DHCP_PARAMETER *Para;
  758. EFI_STATUS Status;
  759. UINT32 Len;
  760. Packet = NULL;
  761. DhcpSb = (DHCP_SERVICE *)Context;
  762. //
  763. // Don't restart receive if error occurs or DHCP is destroyed.
  764. //
  765. if (EFI_ERROR (IoStatus)) {
  766. return;
  767. } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
  768. NetbufFree (UdpPacket);
  769. return;
  770. }
  771. ASSERT (UdpPacket != NULL);
  772. if (DhcpSb->DhcpState == Dhcp4Stopped) {
  773. goto RESTART;
  774. }
  775. //
  776. // Validate the packet received
  777. //
  778. if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
  779. goto RESTART;
  780. }
  781. //
  782. // Copy the DHCP message to a continuous memory block
  783. //
  784. Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
  785. Packet = (EFI_DHCP4_PACKET *)AllocatePool (Len);
  786. if (Packet == NULL) {
  787. goto RESTART;
  788. }
  789. Packet->Size = Len;
  790. Head = &Packet->Dhcp4.Header;
  791. Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *)Head);
  792. if (Packet->Length != UdpPacket->TotalSize) {
  793. goto RESTART;
  794. }
  795. //
  796. // Is this packet the answer to our packet?
  797. //
  798. if ((Head->OpCode != BOOTP_REPLY) ||
  799. (NTOHL (Head->Xid) != DhcpSb->Xid) ||
  800. (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0))
  801. {
  802. goto RESTART;
  803. }
  804. //
  805. // Validate the options and retrieve the interested options
  806. //
  807. Para = NULL;
  808. if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
  809. (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
  810. EFI_ERROR (DhcpValidateOptions (Packet, &Para)))
  811. {
  812. goto RESTART;
  813. }
  814. //
  815. // Call the handler for each state. The handler should return
  816. // EFI_SUCCESS if the process can go on no matter whether the
  817. // packet is ignored or not. If the return is EFI_ERROR, the
  818. // session will be terminated. Packet's ownership is handled
  819. // over to the handlers. If operation succeeds, the handler
  820. // must notify the user. It isn't necessary to do if EFI_ERROR
  821. // is returned because the DhcpEndSession will notify the user.
  822. //
  823. Status = EFI_SUCCESS;
  824. switch (DhcpSb->DhcpState) {
  825. case Dhcp4Selecting:
  826. Status = DhcpHandleSelect (DhcpSb, Packet, Para);
  827. break;
  828. case Dhcp4Requesting:
  829. Status = DhcpHandleRequest (DhcpSb, Packet, Para);
  830. break;
  831. case Dhcp4InitReboot:
  832. case Dhcp4Init:
  833. case Dhcp4Bound:
  834. //
  835. // Ignore the packet in INITREBOOT, INIT and BOUND states
  836. //
  837. FreePool (Packet);
  838. Status = EFI_SUCCESS;
  839. break;
  840. case Dhcp4Renewing:
  841. case Dhcp4Rebinding:
  842. Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
  843. break;
  844. case Dhcp4Rebooting:
  845. Status = DhcpHandleReboot (DhcpSb, Packet, Para);
  846. break;
  847. }
  848. if (Para != NULL) {
  849. FreePool (Para);
  850. }
  851. Packet = NULL;
  852. if (EFI_ERROR (Status)) {
  853. NetbufFree (UdpPacket);
  854. UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
  855. DhcpEndSession (DhcpSb, Status);
  856. return;
  857. }
  858. RESTART:
  859. NetbufFree (UdpPacket);
  860. if (Packet != NULL) {
  861. FreePool (Packet);
  862. }
  863. Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
  864. if (EFI_ERROR (Status)) {
  865. DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
  866. }
  867. }
  868. /**
  869. Release the net buffer when packet is sent.
  870. @param UdpPacket The UDP packets received.
  871. @param EndPoint The local/remote UDP access point
  872. @param IoStatus The status of the UDP receive
  873. @param Context The opaque parameter to the function.
  874. **/
  875. VOID
  876. EFIAPI
  877. DhcpOnPacketSent (
  878. NET_BUF *Packet,
  879. UDP_END_POINT *EndPoint,
  880. EFI_STATUS IoStatus,
  881. VOID *Context
  882. )
  883. {
  884. NetbufFree (Packet);
  885. }
  886. /**
  887. Build and transmit a DHCP message according to the current states.
  888. This function implement the Table 5. of RFC 2131. Always transits
  889. the state (as defined in Figure 5. of the same RFC) before sending
  890. a DHCP message. The table is adjusted accordingly.
  891. @param[in] DhcpSb The DHCP service instance
  892. @param[in] Seed The seed packet which the new packet is based on
  893. @param[in] Para The DHCP parameter of the Seed packet
  894. @param[in] Type The message type to send
  895. @param[in] Msg The human readable message to include in the packet
  896. sent.
  897. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
  898. @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
  899. @retval EFI_SUCCESS The message is sent
  900. @retval other Other error occurs
  901. **/
  902. EFI_STATUS
  903. DhcpSendMessage (
  904. IN DHCP_SERVICE *DhcpSb,
  905. IN EFI_DHCP4_PACKET *Seed,
  906. IN DHCP_PARAMETER *Para,
  907. IN UINT8 Type,
  908. IN UINT8 *Msg
  909. )
  910. {
  911. EFI_DHCP4_CONFIG_DATA *Config;
  912. EFI_DHCP4_PACKET *Packet;
  913. EFI_DHCP4_PACKET *NewPacket;
  914. EFI_DHCP4_HEADER *Head;
  915. EFI_DHCP4_HEADER *SeedHead;
  916. UDP_IO *UdpIo;
  917. UDP_END_POINT EndPoint;
  918. NET_BUF *Wrap;
  919. NET_FRAGMENT Frag;
  920. EFI_STATUS Status;
  921. IP4_ADDR IpAddr;
  922. UINT8 *Buf;
  923. UINT16 MaxMsg;
  924. UINT32 Len;
  925. UINT32 Index;
  926. //
  927. // Allocate a big enough memory block to hold the DHCP packet
  928. //
  929. Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
  930. if (Msg != NULL) {
  931. Len += (UINT32)AsciiStrLen ((CHAR8 *)Msg);
  932. }
  933. Packet = AllocatePool (Len);
  934. if (Packet == NULL) {
  935. return EFI_OUT_OF_RESOURCES;
  936. }
  937. Packet->Size = Len;
  938. Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
  939. //
  940. // Fill in the DHCP header fields
  941. //
  942. Config = &DhcpSb->ActiveConfig;
  943. SeedHead = NULL;
  944. if (Seed != NULL) {
  945. SeedHead = &Seed->Dhcp4.Header;
  946. }
  947. Head = &Packet->Dhcp4.Header;
  948. ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
  949. Head->OpCode = BOOTP_REQUEST;
  950. Head->HwType = DhcpSb->HwType;
  951. Head->HwAddrLen = DhcpSb->HwLen;
  952. Head->Xid = HTONL (DhcpSb->Xid);
  953. Head->Reserved = HTONS (0x8000); // Server, broadcast the message please.
  954. EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
  955. CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
  956. if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
  957. Head->Seconds = 0;
  958. } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
  959. //
  960. // Use the same value as the original DHCPDISCOVER message.
  961. //
  962. Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
  963. } else {
  964. SetElapsedTime (&Head->Seconds, DhcpSb->ActiveChild);
  965. }
  966. //
  967. // Append the DHCP message type
  968. //
  969. Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
  970. Buf = Packet->Dhcp4.Option;
  971. Buf = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);
  972. //
  973. // Append the serverid option if necessary:
  974. // 1. DHCP decline message
  975. // 2. DHCP release message
  976. // 3. DHCP request to confirm one lease.
  977. //
  978. if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
  979. ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
  980. )
  981. {
  982. ASSERT ((Para != NULL) && (Para->ServerId != 0));
  983. IpAddr = HTONL (Para->ServerId);
  984. Buf = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *)&IpAddr);
  985. }
  986. //
  987. // Append the requested IP option if necessary:
  988. // 1. DHCP request to use the previously allocated address
  989. // 2. DHCP request to confirm one lease
  990. // 3. DHCP decline to decline one lease
  991. //
  992. IpAddr = 0;
  993. if (Type == DHCP_MSG_REQUEST) {
  994. if (DhcpSb->DhcpState == Dhcp4Rebooting) {
  995. IpAddr = EFI_IP4 (Config->ClientAddress);
  996. } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
  997. ASSERT (SeedHead != NULL);
  998. IpAddr = EFI_IP4 (SeedHead->YourAddr);
  999. }
  1000. } else if (Type == DHCP_MSG_DECLINE) {
  1001. ASSERT (SeedHead != NULL);
  1002. IpAddr = EFI_IP4 (SeedHead->YourAddr);
  1003. }
  1004. if (IpAddr != 0) {
  1005. Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *)&IpAddr);
  1006. }
  1007. //
  1008. // Append the Max Message Length option if it isn't a DECLINE
  1009. // or RELEASE to direct the server use large messages instead of
  1010. // override the BOOTFILE and SERVER fields in the message head.
  1011. //
  1012. if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
  1013. MaxMsg = HTONS (0xFF00);
  1014. Buf = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *)&MaxMsg);
  1015. }
  1016. //
  1017. // Append the user's message if it isn't NULL
  1018. //
  1019. if (Msg != NULL) {
  1020. Len = MIN ((UINT32)AsciiStrLen ((CHAR8 *)Msg), 255);
  1021. Buf = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16)Len, Msg);
  1022. }
  1023. //
  1024. // Append the user configured options
  1025. //
  1026. if (DhcpSb->UserOptionLen != 0) {
  1027. for (Index = 0; Index < Config->OptionCount; Index++) {
  1028. //
  1029. // We can't use any option other than the client ID from user
  1030. // if it is a DHCP decline or DHCP release .
  1031. //
  1032. if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
  1033. (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID))
  1034. {
  1035. continue;
  1036. }
  1037. Buf = DhcpAppendOption (
  1038. Buf,
  1039. Config->OptionList[Index]->OpCode,
  1040. Config->OptionList[Index]->Length,
  1041. Config->OptionList[Index]->Data
  1042. );
  1043. }
  1044. }
  1045. *(Buf++) = DHCP4_TAG_EOP;
  1046. Packet->Length += (UINT32)(Buf - Packet->Dhcp4.Option);
  1047. //
  1048. // OK, the message is built, call the user to override it.
  1049. //
  1050. Status = EFI_SUCCESS;
  1051. NewPacket = NULL;
  1052. if (Type == DHCP_MSG_DISCOVER) {
  1053. Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
  1054. } else if (Type == DHCP_MSG_REQUEST) {
  1055. Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
  1056. } else if (Type == DHCP_MSG_DECLINE) {
  1057. Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
  1058. }
  1059. if (EFI_ERROR (Status)) {
  1060. FreePool (Packet);
  1061. return Status;
  1062. }
  1063. if (NewPacket != NULL) {
  1064. FreePool (Packet);
  1065. Packet = NewPacket;
  1066. }
  1067. //
  1068. // Save the Client Address will be sent out
  1069. //
  1070. CopyMem (
  1071. &DhcpSb->ClientAddressSendOut[0],
  1072. &Packet->Dhcp4.Header.ClientHwAddr[0],
  1073. Packet->Dhcp4.Header.HwAddrLen
  1074. );
  1075. //
  1076. // Wrap it into a netbuf then send it.
  1077. //
  1078. Frag.Bulk = (UINT8 *)&Packet->Dhcp4.Header;
  1079. Frag.Len = Packet->Length;
  1080. Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
  1081. if (Wrap == NULL) {
  1082. FreePool (Packet);
  1083. return EFI_OUT_OF_RESOURCES;
  1084. }
  1085. //
  1086. // Save it as the last sent packet for retransmission
  1087. //
  1088. if (DhcpSb->LastPacket != NULL) {
  1089. FreePool (DhcpSb->LastPacket);
  1090. }
  1091. DhcpSb->LastPacket = Packet;
  1092. DhcpSetTransmitTimer (DhcpSb);
  1093. //
  1094. // Broadcast the message, unless we know the server address.
  1095. // Use the lease UdpIo port to send the unicast packet.
  1096. //
  1097. EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
  1098. EndPoint.LocalAddr.Addr[0] = 0;
  1099. EndPoint.RemotePort = DHCP_SERVER_PORT;
  1100. EndPoint.LocalPort = DHCP_CLIENT_PORT;
  1101. UdpIo = DhcpSb->UdpIo;
  1102. if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
  1103. EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
  1104. EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
  1105. UdpIo = DhcpSb->LeaseIoPort;
  1106. }
  1107. ASSERT (UdpIo != NULL);
  1108. Status = UdpIoSendDatagram (
  1109. UdpIo,
  1110. Wrap,
  1111. &EndPoint,
  1112. NULL,
  1113. DhcpOnPacketSent,
  1114. DhcpSb
  1115. );
  1116. if (EFI_ERROR (Status)) {
  1117. NetbufFree (Wrap);
  1118. return EFI_ACCESS_DENIED;
  1119. }
  1120. return EFI_SUCCESS;
  1121. }
  1122. /**
  1123. Retransmit a saved packet. Only DISCOVER and REQUEST messages
  1124. will be retransmitted.
  1125. @param[in] DhcpSb The DHCP service instance
  1126. @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
  1127. @retval EFI_SUCCESS The packet is retransmitted.
  1128. **/
  1129. EFI_STATUS
  1130. DhcpRetransmit (
  1131. IN DHCP_SERVICE *DhcpSb
  1132. )
  1133. {
  1134. UDP_IO *UdpIo;
  1135. UDP_END_POINT EndPoint;
  1136. NET_BUF *Wrap;
  1137. NET_FRAGMENT Frag;
  1138. EFI_STATUS Status;
  1139. ASSERT (DhcpSb->LastPacket != NULL);
  1140. //
  1141. // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
  1142. //
  1143. if (DhcpSb->DhcpState != Dhcp4Requesting) {
  1144. SetElapsedTime (&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
  1145. }
  1146. //
  1147. // Wrap it into a netbuf then send it.
  1148. //
  1149. Frag.Bulk = (UINT8 *)&DhcpSb->LastPacket->Dhcp4.Header;
  1150. Frag.Len = DhcpSb->LastPacket->Length;
  1151. Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
  1152. if (Wrap == NULL) {
  1153. return EFI_OUT_OF_RESOURCES;
  1154. }
  1155. //
  1156. // Broadcast the message, unless we know the server address.
  1157. //
  1158. EndPoint.RemotePort = DHCP_SERVER_PORT;
  1159. EndPoint.LocalPort = DHCP_CLIENT_PORT;
  1160. EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
  1161. EndPoint.LocalAddr.Addr[0] = 0;
  1162. UdpIo = DhcpSb->UdpIo;
  1163. if (DhcpSb->DhcpState == Dhcp4Renewing) {
  1164. EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
  1165. EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;
  1166. UdpIo = DhcpSb->LeaseIoPort;
  1167. }
  1168. ASSERT (UdpIo != NULL);
  1169. Status = UdpIoSendDatagram (
  1170. UdpIo,
  1171. Wrap,
  1172. &EndPoint,
  1173. NULL,
  1174. DhcpOnPacketSent,
  1175. DhcpSb
  1176. );
  1177. if (EFI_ERROR (Status)) {
  1178. NetbufFree (Wrap);
  1179. return EFI_ACCESS_DENIED;
  1180. }
  1181. return EFI_SUCCESS;
  1182. }
  1183. /**
  1184. Each DHCP service has three timer. Two of them are count down timer.
  1185. One for the packet retransmission. The other is to collect the offers.
  1186. The third timer increments the lease life which is compared to T1, T2,
  1187. and lease to determine the time to renew and rebind the lease.
  1188. DhcpOnTimerTick will be called once every second.
  1189. @param[in] Event The timer event
  1190. @param[in] Context The context, which is the DHCP service instance.
  1191. **/
  1192. VOID
  1193. EFIAPI
  1194. DhcpOnTimerTick (
  1195. IN EFI_EVENT Event,
  1196. IN VOID *Context
  1197. )
  1198. {
  1199. LIST_ENTRY *Entry;
  1200. LIST_ENTRY *Next;
  1201. DHCP_SERVICE *DhcpSb;
  1202. DHCP_PROTOCOL *Instance;
  1203. EFI_STATUS Status;
  1204. DhcpSb = (DHCP_SERVICE *)Context;
  1205. Instance = DhcpSb->ActiveChild;
  1206. //
  1207. // 0xffff is the maximum supported value for elapsed time according to RFC.
  1208. //
  1209. if ((Instance != NULL) && (Instance->ElaspedTime < 0xffff)) {
  1210. Instance->ElaspedTime++;
  1211. }
  1212. //
  1213. // Check the retransmit timer
  1214. //
  1215. if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
  1216. //
  1217. // Select offer at each timeout if any offer received.
  1218. //
  1219. if ((DhcpSb->DhcpState == Dhcp4Selecting) && (DhcpSb->LastOffer != NULL)) {
  1220. Status = DhcpChooseOffer (DhcpSb);
  1221. if (EFI_ERROR (Status)) {
  1222. if (DhcpSb->LastOffer != NULL) {
  1223. FreePool (DhcpSb->LastOffer);
  1224. DhcpSb->LastOffer = NULL;
  1225. }
  1226. } else {
  1227. goto ON_EXIT;
  1228. }
  1229. }
  1230. if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
  1231. //
  1232. // Still has another try
  1233. //
  1234. DhcpRetransmit (DhcpSb);
  1235. DhcpSetTransmitTimer (DhcpSb);
  1236. } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
  1237. //
  1238. // Retransmission failed, if the DHCP request is initiated by
  1239. // user, adjust the current state according to the lease life.
  1240. // Otherwise do nothing to wait the lease to timeout
  1241. //
  1242. if (DhcpSb->ExtraRefresh != 0) {
  1243. Status = EFI_SUCCESS;
  1244. if (DhcpSb->LeaseLife < DhcpSb->T1) {
  1245. Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
  1246. } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
  1247. Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
  1248. } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
  1249. Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
  1250. } else {
  1251. goto END_SESSION;
  1252. }
  1253. DhcpSb->IoStatus = EFI_TIMEOUT;
  1254. DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
  1255. }
  1256. } else {
  1257. goto END_SESSION;
  1258. }
  1259. }
  1260. //
  1261. // If an address has been acquired, check whether need to
  1262. // refresh or whether it has expired.
  1263. //
  1264. if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
  1265. DhcpSb->LeaseLife++;
  1266. //
  1267. // Don't timeout the lease, only count the life if user is
  1268. // requesting extra renew/rebind. Adjust the state after that.
  1269. //
  1270. if (DhcpSb->ExtraRefresh != 0) {
  1271. return;
  1272. }
  1273. if (DhcpSb->LeaseLife == DhcpSb->Lease) {
  1274. //
  1275. // Lease expires, end the session
  1276. //
  1277. goto END_SESSION;
  1278. } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
  1279. //
  1280. // T2 expires, transit to rebinding then send a REQUEST to any server
  1281. //
  1282. if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
  1283. goto END_SESSION;
  1284. }
  1285. if (Instance != NULL) {
  1286. Instance->ElaspedTime = 0;
  1287. }
  1288. Status = DhcpSendMessage (
  1289. DhcpSb,
  1290. DhcpSb->Selected,
  1291. DhcpSb->Para,
  1292. DHCP_MSG_REQUEST,
  1293. NULL
  1294. );
  1295. if (EFI_ERROR (Status)) {
  1296. goto END_SESSION;
  1297. }
  1298. } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
  1299. //
  1300. // T1 expires, transit to renewing, then send a REQUEST to the server
  1301. //
  1302. if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
  1303. goto END_SESSION;
  1304. }
  1305. if (Instance != NULL) {
  1306. Instance->ElaspedTime = 0;
  1307. }
  1308. Status = DhcpSendMessage (
  1309. DhcpSb,
  1310. DhcpSb->Selected,
  1311. DhcpSb->Para,
  1312. DHCP_MSG_REQUEST,
  1313. NULL
  1314. );
  1315. if (EFI_ERROR (Status)) {
  1316. goto END_SESSION;
  1317. }
  1318. }
  1319. }
  1320. ON_EXIT:
  1321. //
  1322. // Iterate through all the DhcpSb Children.
  1323. //
  1324. NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
  1325. Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
  1326. Instance->Timeout--;
  1327. if ((Instance->Timeout == 0) && (Instance->Token != NULL)) {
  1328. PxeDhcpDone (Instance);
  1329. }
  1330. }
  1331. return;
  1332. END_SESSION:
  1333. DhcpEndSession (DhcpSb, EFI_TIMEOUT);
  1334. return;
  1335. }