1 /** @file
2   Implement IP6 pesudo interface.
3 
4   Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php.
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "Ip6Impl.h"
17 
18 /**
19   Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.
20 
21   @param[in]  Event              The transmit token's event.
22   @param[in]  Context            The Context which is pointed to the token.
23 
24 **/
25 VOID
26 EFIAPI
27 Ip6OnFrameSent (
28   IN EFI_EVENT               Event,
29   IN VOID                    *Context
30   );
31 
32 /**
33   Fileter function to cancel all the frame related to an IP instance.
34 
35   @param[in]  Frame             The transmit request to test whether to cancel.
36   @param[in]  Context           The context which is the Ip instance that issued
37                                 the transmit.
38 
39   @retval TRUE                  The frame belongs to this instance and is to be
40                                 removed.
41   @retval FALSE                 The frame doesn't belong to this instance.
42 
43 **/
44 BOOLEAN
Ip6CancelInstanceFrame(IN IP6_LINK_TX_TOKEN * Frame,IN VOID * Context)45 Ip6CancelInstanceFrame (
46   IN IP6_LINK_TX_TOKEN *Frame,
47   IN VOID              *Context
48   )
49 {
50   if (Frame->IpInstance == (IP6_PROTOCOL *) Context) {
51     return TRUE;
52   }
53 
54   return FALSE;
55 }
56 
57 /**
58   Set the interface's address. This will trigger the DAD process for the
59   address to set. To set an already set address, the lifetimes wil be
60   updated to the new value passed in.
61 
62   @param[in]  Interface             The interface to set the address.
63   @param[in]  Ip6Addr               The interface's to be assigned IPv6 address.
64   @param[in]  IsAnycast             If TRUE, the unicast IPv6 address is anycast.
65                                     Otherwise, it is not anycast.
66   @param[in]  PrefixLength          The prefix length of the Ip6Addr.
67   @param[in]  ValidLifetime         The valid lifetime for this address.
68   @param[in]  PreferredLifetime     The preferred lifetime for this address.
69   @param[in]  DadCallback           The caller's callback to trigger when DAD finishes.
70                                     This is an optional parameter that may be NULL.
71   @param[in]  Context               The context that will be passed to DadCallback.
72                                     This is an optional parameter that may be NULL.
73 
74   @retval EFI_SUCCESS               The interface is scheduled to be configured with
75                                     the specified address.
76   @retval EFI_OUT_OF_RESOURCES      Failed to set the interface's address due to
77                                     lack of resources.
78 
79 **/
80 EFI_STATUS
Ip6SetAddress(IN IP6_INTERFACE * Interface,IN EFI_IPv6_ADDRESS * Ip6Addr,IN BOOLEAN IsAnycast,IN UINT8 PrefixLength,IN UINT32 ValidLifetime,IN UINT32 PreferredLifetime,IN IP6_DAD_CALLBACK DadCallback OPTIONAL,IN VOID * Context OPTIONAL)81 Ip6SetAddress (
82   IN IP6_INTERFACE          *Interface,
83   IN EFI_IPv6_ADDRESS       *Ip6Addr,
84   IN BOOLEAN                IsAnycast,
85   IN UINT8                  PrefixLength,
86   IN UINT32                 ValidLifetime,
87   IN UINT32                 PreferredLifetime,
88   IN IP6_DAD_CALLBACK       DadCallback  OPTIONAL,
89   IN VOID                   *Context     OPTIONAL
90   )
91 {
92   IP6_SERVICE            *IpSb;
93   IP6_ADDRESS_INFO       *AddressInfo;
94   LIST_ENTRY             *Entry;
95   IP6_PREFIX_LIST_ENTRY  *PrefixEntry;
96   UINT64                 Delay;
97   IP6_DELAY_JOIN_LIST    *DelayNode;
98 
99   NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);
100 
101   IpSb = Interface->Service;
102 
103   if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) {
104     ASSERT (AddressInfo != NULL);
105     //
106     // Update the lifetime.
107     //
108     AddressInfo->ValidLifetime     = ValidLifetime;
109     AddressInfo->PreferredLifetime = PreferredLifetime;
110 
111     if (DadCallback != NULL) {
112       DadCallback (TRUE, Ip6Addr, Context);
113     }
114 
115     return EFI_SUCCESS;
116   }
117 
118   AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO));
119   if (AddressInfo == NULL) {
120     return EFI_OUT_OF_RESOURCES;
121   }
122 
123   AddressInfo->Signature         = IP6_ADDR_INFO_SIGNATURE;
124   IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr);
125   AddressInfo->IsAnycast         = IsAnycast;
126   AddressInfo->PrefixLength      = PrefixLength;
127   AddressInfo->ValidLifetime     = ValidLifetime;
128   AddressInfo->PreferredLifetime = PreferredLifetime;
129 
130   if (AddressInfo->PrefixLength == 0) {
131     //
132     // Find an appropriate prefix from on-link prefixes and update the prefixlength.
133     // Longest prefix match is used here.
134     //
135     NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
136       PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
137 
138       if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {
139         AddressInfo->PrefixLength = PrefixEntry->PrefixLength;
140         break;
141       }
142     }
143   }
144 
145   if (AddressInfo->PrefixLength == 0) {
146     //
147     // If the prefix length is still zero, try the autonomous prefixes.
148     // Longest prefix match is used here.
149     //
150     NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {
151       PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
152 
153       if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {
154         AddressInfo->PrefixLength = PrefixEntry->PrefixLength;
155         break;
156       }
157     }
158   }
159 
160   if (AddressInfo->PrefixLength == 0) {
161     //
162     // BUGBUG: Stil fail, use 64 as the default prefix length.
163     //
164     AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
165   }
166 
167 
168   //
169   // Node should delay joining the solicited-node mulitcast address by a random delay
170   // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second).
171   // Thus queue the address to be processed in Duplicate Address Detection module
172   // after the delay time (in milliseconds).
173   //
174   Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ());
175   Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS);
176   Delay = RShiftU64 (Delay, 32);
177 
178   DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST));
179   if (DelayNode == NULL) {
180     FreePool (AddressInfo);
181     return EFI_OUT_OF_RESOURCES;
182   }
183 
184   DelayNode->DelayTime   = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS));
185   DelayNode->Interface   = Interface;
186   DelayNode->AddressInfo = AddressInfo;
187   DelayNode->DadCallback = DadCallback;
188   DelayNode->Context     = Context;
189 
190   InsertTailList (&Interface->DelayJoinList, &DelayNode->Link);
191   return EFI_SUCCESS;
192 }
193 
194 /**
195   Create an IP6_INTERFACE.
196 
197   @param[in]  IpSb                  The IP6 service binding instance.
198   @param[in]  LinkLocal             If TRUE, the instance is created for link-local address.
199                                     Otherwise, it is not for a link-local address.
200 
201   @return Point to the created IP6_INTERFACE, otherwise NULL.
202 
203 **/
204 IP6_INTERFACE *
Ip6CreateInterface(IN IP6_SERVICE * IpSb,IN BOOLEAN LinkLocal)205 Ip6CreateInterface (
206   IN IP6_SERVICE            *IpSb,
207   IN BOOLEAN                LinkLocal
208   )
209 {
210   EFI_STATUS                Status;
211   IP6_INTERFACE             *Interface;
212   EFI_IPv6_ADDRESS          *Ip6Addr;
213 
214   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
215 
216   Interface = AllocatePool (sizeof (IP6_INTERFACE));
217   if (Interface == NULL) {
218     return NULL;
219   }
220 
221   Interface->Signature        = IP6_INTERFACE_SIGNATURE;
222   Interface->RefCnt           = 1;
223 
224   InitializeListHead (&Interface->AddressList);
225   Interface->AddressCount     = 0;
226   Interface->Configured       = FALSE;
227 
228   Interface->Service          = IpSb;
229   Interface->Controller       = IpSb->Controller;
230   Interface->Image            = IpSb->Image;
231 
232   InitializeListHead (&Interface->ArpQues);
233   InitializeListHead (&Interface->SentFrames);
234 
235   Interface->DupAddrDetect    = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits;
236   InitializeListHead (&Interface->DupAddrDetectList);
237 
238   InitializeListHead (&Interface->DelayJoinList);
239 
240   InitializeListHead (&Interface->IpInstances);
241   Interface->PromiscRecv      = FALSE;
242 
243   if (!LinkLocal) {
244     return Interface;
245   }
246 
247   //
248   // Get the link local addr
249   //
250   Ip6Addr = Ip6CreateLinkLocalAddr (IpSb);
251   if (Ip6Addr == NULL) {
252     goto ON_ERROR;
253   }
254 
255   //
256   // Perform DAD - Duplicate Address Detection.
257   //
258   Status = Ip6SetAddress (
259              Interface,
260              Ip6Addr,
261              FALSE,
262              IP6_LINK_LOCAL_PREFIX_LENGTH,
263              (UINT32) IP6_INFINIT_LIFETIME,
264              (UINT32) IP6_INFINIT_LIFETIME,
265              NULL,
266              NULL
267              );
268 
269   FreePool (Ip6Addr);
270 
271   if (EFI_ERROR (Status)) {
272     goto ON_ERROR;
273   }
274 
275   return Interface;
276 
277 ON_ERROR:
278 
279   FreePool (Interface);
280   return NULL;
281 }
282 
283 /**
284   Free the interface used by IpInstance. All the IP instance with
285   the same Ip/prefix pair share the same interface. It is reference
286   counted. All the frames that haven't been sent will be cancelled.
287   Because the IpInstance is optional, the caller must remove
288   IpInstance from the interface's instance list.
289 
290   @param[in]  Interface         The interface used by the IpInstance.
291   @param[in]  IpInstance        The IP instance that free the interface. NULL if
292                                 the IP driver is releasing the default interface.
293 
294 **/
295 VOID
Ip6CleanInterface(IN IP6_INTERFACE * Interface,IN IP6_PROTOCOL * IpInstance OPTIONAL)296 Ip6CleanInterface (
297   IN  IP6_INTERFACE         *Interface,
298   IN  IP6_PROTOCOL          *IpInstance           OPTIONAL
299   )
300 {
301   IP6_DAD_ENTRY             *Duplicate;
302   IP6_DELAY_JOIN_LIST       *Delay;
303 
304   NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);
305   ASSERT (Interface->RefCnt > 0);
306 
307   //
308   // Remove all the pending transmit token related to this IP instance.
309   //
310   Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance);
311 
312   if (--Interface->RefCnt > 0) {
313     return;
314   }
315 
316   //
317   // Destroy the interface if this is the last IP instance.
318   // Remove all the system transmitted packets
319   // from this interface, cancel the receive request if exists.
320   //
321   Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL);
322 
323   ASSERT (IsListEmpty (&Interface->IpInstances));
324   ASSERT (IsListEmpty (&Interface->ArpQues));
325   ASSERT (IsListEmpty (&Interface->SentFrames));
326 
327   while (!IsListEmpty (&Interface->DupAddrDetectList)) {
328     Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link);
329     NetListRemoveHead (&Interface->DupAddrDetectList);
330     FreePool (Duplicate);
331   }
332 
333   while (!IsListEmpty (&Interface->DelayJoinList)) {
334     Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link);
335     NetListRemoveHead (&Interface->DelayJoinList);
336     FreePool (Delay);
337   }
338 
339   Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0);
340 
341   RemoveEntryList (&Interface->Link);
342   FreePool (Interface);
343 }
344 
345 /**
346   Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN.
347 
348   @param[in]  Interface         The interface to send out from.
349   @param[in]  IpInstance        The IpInstance that transmit the packet.  NULL if
350                                 the packet is sent by the IP6 driver itself.
351   @param[in]  Packet            The packet to transmit
352   @param[in]  CallBack          Call back function to execute if transmission
353                                 finished.
354   @param[in]  Context           Opaque parameter to the callback.
355 
356   @return The wrapped token if succeed or NULL.
357 
358 **/
359 IP6_LINK_TX_TOKEN *
Ip6CreateLinkTxToken(IN IP6_INTERFACE * Interface,IN IP6_PROTOCOL * IpInstance OPTIONAL,IN NET_BUF * Packet,IN IP6_FRAME_CALLBACK CallBack,IN VOID * Context)360 Ip6CreateLinkTxToken (
361   IN IP6_INTERFACE          *Interface,
362   IN IP6_PROTOCOL           *IpInstance    OPTIONAL,
363   IN NET_BUF                *Packet,
364   IN IP6_FRAME_CALLBACK     CallBack,
365   IN VOID                   *Context
366   )
367 {
368   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;
369   EFI_MANAGED_NETWORK_TRANSMIT_DATA     *MnpTxData;
370   IP6_LINK_TX_TOKEN                     *Token;
371   EFI_STATUS                            Status;
372   UINT32                                Count;
373 
374   Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));
375 
376   if (Token == NULL) {
377     return NULL;
378   }
379 
380   Token->Signature = IP6_LINK_TX_SIGNATURE;
381   InitializeListHead (&Token->Link);
382 
383   Token->IpInstance = IpInstance;
384   Token->CallBack   = CallBack;
385   Token->Packet     = Packet;
386   Token->Context    = Context;
387   ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS));
388   IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress);
389 
390   MnpToken          = &(Token->MnpToken);
391   MnpToken->Status  = EFI_NOT_READY;
392 
393   Status = gBS->CreateEvent (
394                   EVT_NOTIFY_SIGNAL,
395                   TPL_NOTIFY,
396                   Ip6OnFrameSent,
397                   Token,
398                   &MnpToken->Event
399                   );
400 
401   if (EFI_ERROR (Status)) {
402     FreePool (Token);
403     return NULL;
404   }
405 
406   MnpTxData                     = &Token->MnpTxData;
407   MnpToken->Packet.TxData       = MnpTxData;
408 
409   MnpTxData->DestinationAddress = &Token->DstMac;
410   MnpTxData->SourceAddress      = &Token->SrcMac;
411   MnpTxData->ProtocolType       = IP6_ETHER_PROTO;
412   MnpTxData->DataLength         = Packet->TotalSize;
413   MnpTxData->HeaderLength       = 0;
414 
415   Count                         = Packet->BlockOpNum;
416 
417   NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);
418   MnpTxData->FragmentCount      = (UINT16)Count;
419 
420   return Token;
421 }
422 
423 /**
424   Free the link layer transmit token. It will close the event,
425   then free the memory used.
426 
427   @param[in]  Token                 Token to free.
428 
429 **/
430 VOID
Ip6FreeLinkTxToken(IN IP6_LINK_TX_TOKEN * Token)431 Ip6FreeLinkTxToken (
432   IN IP6_LINK_TX_TOKEN      *Token
433   )
434 {
435   NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);
436 
437   gBS->CloseEvent (Token->MnpToken.Event);
438   FreePool (Token);
439 }
440 
441 /**
442   Callback function when the received packet is freed.
443   Check Ip6OnFrameReceived for information.
444 
445   @param[in]  Context       Points to EFI_MANAGED_NETWORK_RECEIVE_DATA.
446 
447 **/
448 VOID
449 EFIAPI
Ip6RecycleFrame(IN VOID * Context)450 Ip6RecycleFrame (
451   IN VOID                   *Context
452   )
453 {
454   EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData;
455 
456   RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context;
457 
458   gBS->SignalEvent (RxData->RecycleEvent);
459 }
460 
461 /**
462   Received a frame from MNP. Wrap it in net buffer then deliver
463   it to IP's input function. The ownship of the packet also
464   is transferred to IP. When Ip is finished with this packet, it
465   will call NetbufFree to release the packet, NetbufFree will
466   again call the Ip6RecycleFrame to signal MNP's event and free
467   the token used.
468 
469   @param[in]  Context         Context for the callback.
470 
471 **/
472 VOID
473 EFIAPI
Ip6OnFrameReceivedDpc(IN VOID * Context)474 Ip6OnFrameReceivedDpc (
475   IN VOID                     *Context
476   )
477 {
478   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;
479   EFI_MANAGED_NETWORK_RECEIVE_DATA      *MnpRxData;
480   IP6_LINK_RX_TOKEN                     *Token;
481   NET_FRAGMENT                          Netfrag;
482   NET_BUF                               *Packet;
483   UINT32                                Flag;
484   IP6_SERVICE                           *IpSb;
485 
486   Token = (IP6_LINK_RX_TOKEN *) Context;
487   NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE);
488 
489   //
490   // First clear the interface's receive request in case the
491   // caller wants to call Ip6ReceiveFrame in the callback.
492   //
493   IpSb = (IP6_SERVICE *) Token->Context;
494   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
495 
496 
497   MnpToken  = &Token->MnpToken;
498   MnpRxData = MnpToken->Packet.RxData;
499 
500   if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {
501     Token->CallBack (NULL, MnpToken->Status, 0, Token->Context);
502     return ;
503   }
504 
505   //
506   // Wrap the frame in a net buffer then deliever it to IP input.
507   // IP will reassemble the packet, and deliver it to upper layer
508   //
509   Netfrag.Len  = MnpRxData->DataLength;
510   Netfrag.Bulk = MnpRxData->PacketData;
511 
512   Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData);
513 
514   if (Packet == NULL) {
515     gBS->SignalEvent (MnpRxData->RecycleEvent);
516 
517     Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);
518 
519     return ;
520   }
521 
522   Flag  = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0);
523   Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0);
524   Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0);
525 
526   Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context);
527 }
528 
529 /**
530   Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
531 
532   @param  Event                 The receive event delivered to MNP for receive.
533   @param  Context               Context for the callback.
534 
535 **/
536 VOID
537 EFIAPI
Ip6OnFrameReceived(IN EFI_EVENT Event,IN VOID * Context)538 Ip6OnFrameReceived (
539   IN EFI_EVENT                Event,
540   IN VOID                     *Context
541   )
542 {
543   //
544   // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK
545   //
546   QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context);
547 }
548 
549 /**
550   Request to receive the packet from the interface.
551 
552   @param[in]  CallBack          Function to call when receive finished.
553   @param[in]  IpSb              Points to IP6 service binding instance.
554 
555   @retval EFI_ALREADY_STARTED   There is already a pending receive request.
556   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to receive.
557   @retval EFI_SUCCESS           The recieve request has been started.
558 
559 **/
560 EFI_STATUS
Ip6ReceiveFrame(IN IP6_FRAME_CALLBACK CallBack,IN IP6_SERVICE * IpSb)561 Ip6ReceiveFrame (
562   IN  IP6_FRAME_CALLBACK    CallBack,
563   IN  IP6_SERVICE           *IpSb
564   )
565 {
566   EFI_STATUS                Status;
567   IP6_LINK_RX_TOKEN         *Token;
568 
569   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
570 
571   Token           = &IpSb->RecvRequest;
572   Token->CallBack = CallBack;
573   Token->Context  = (VOID *) IpSb;
574 
575   Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);
576   if (EFI_ERROR (Status)) {
577     return Status;
578   }
579 
580   return EFI_SUCCESS;
581 }
582 
583 /**
584   Callback funtion when frame transmission is finished. It will
585   call the frame owner's callback function to tell it the result.
586 
587   @param[in]  Context        Context which points to the token.
588 
589 **/
590 VOID
591 EFIAPI
Ip6OnFrameSentDpc(IN VOID * Context)592 Ip6OnFrameSentDpc (
593   IN VOID                    *Context
594   )
595 {
596   IP6_LINK_TX_TOKEN         *Token;
597 
598   Token = (IP6_LINK_TX_TOKEN *) Context;
599   NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);
600 
601   RemoveEntryList (&Token->Link);
602 
603   Token->CallBack (
604           Token->Packet,
605           Token->MnpToken.Status,
606           0,
607           Token->Context
608           );
609 
610   Ip6FreeLinkTxToken (Token);
611 }
612 
613 /**
614   Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.
615 
616   @param[in]  Event                 The transmit token's event.
617   @param[in]  Context               Context which points to the token.
618 
619 **/
620 VOID
621 EFIAPI
Ip6OnFrameSent(IN EFI_EVENT Event,IN VOID * Context)622 Ip6OnFrameSent (
623   IN EFI_EVENT               Event,
624   IN VOID                    *Context
625   )
626 {
627   //
628   // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK
629   //
630   QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);
631 }
632 
633 /**
634   Send a frame from the interface. If the next hop is a multicast address,
635   it is transmitted immediately. If the next hop is a unicast,
636   and the NextHop's MAC is not known, it will perform address resolution.
637   If an error occurred, the CallBack won't be called. So, the caller
638   must test the return value, and take action when there is an error.
639 
640   @param[in]  Interface         The interface to send the frame from
641   @param[in]  IpInstance        The IP child that request the transmission.
642                                 NULL if it is the IP6 driver itself.
643   @param[in]  Packet            The packet to transmit.
644   @param[in]  NextHop           The immediate destination to transmit the packet to.
645   @param[in]  CallBack          Function to call back when transmit finished.
646   @param[in]  Context           Opaque parameter to the callback.
647 
648   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame.
649   @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop.
650   @retval EFI_SUCCESS           The packet successfully transmitted.
651 
652 **/
653 EFI_STATUS
Ip6SendFrame(IN IP6_INTERFACE * Interface,IN IP6_PROTOCOL * IpInstance OPTIONAL,IN NET_BUF * Packet,IN EFI_IPv6_ADDRESS * NextHop,IN IP6_FRAME_CALLBACK CallBack,IN VOID * Context)654 Ip6SendFrame (
655   IN  IP6_INTERFACE         *Interface,
656   IN  IP6_PROTOCOL          *IpInstance      OPTIONAL,
657   IN  NET_BUF               *Packet,
658   IN  EFI_IPv6_ADDRESS      *NextHop,
659   IN  IP6_FRAME_CALLBACK    CallBack,
660   IN  VOID                  *Context
661   )
662 {
663   IP6_SERVICE               *IpSb;
664   IP6_LINK_TX_TOKEN         *Token;
665   EFI_STATUS                Status;
666   IP6_NEIGHBOR_ENTRY        *NeighborCache;
667   LIST_ENTRY                *Entry;
668   IP6_NEIGHBOR_ENTRY        *ArpQue;
669 
670   IpSb = Interface->Service;
671   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
672 
673   //
674   // Only when link local address is performing DAD, the interface could be used in unconfigured.
675   //
676   if (IpSb->LinkLocalOk) {
677     ASSERT (Interface->Configured);
678   }
679 
680   Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);
681 
682   if (Token == NULL) {
683     return EFI_OUT_OF_RESOURCES;
684   }
685 
686   if (IP6_IS_MULTICAST (NextHop)) {
687     Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);
688     if (EFI_ERROR (Status)) {
689       goto Error;
690     }
691 
692     goto SendNow;
693   }
694 
695   //
696   // If send to itself, directly send out
697   //
698   if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {
699     IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);
700     goto SendNow;
701   }
702 
703   //
704   // If unicast, check the neighbor state.
705   //
706 
707   NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);
708   ASSERT (NeighborCache != NULL);
709 
710   if (NeighborCache->Interface == NULL) {
711     NeighborCache->Interface = Interface;
712   }
713 
714   switch (NeighborCache->State) {
715   case EfiNeighborStale:
716     NeighborCache->State = EfiNeighborDelay;
717     NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
718     //
719     // Fall through
720     //
721   case EfiNeighborReachable:
722   case EfiNeighborDelay:
723   case EfiNeighborProbe:
724     IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);
725     goto SendNow;
726     break;
727 
728   default:
729     break;
730   }
731 
732   //
733   // Have to do asynchronous ARP resolution. First check whether there is
734   // already a pending request.
735   //
736   NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
737     ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
738     if (ArpQue == NeighborCache) {
739       InsertTailList (&NeighborCache->Frames, &Token->Link);
740       NeighborCache->ArpFree = TRUE;
741       return EFI_SUCCESS;
742     }
743   }
744 
745   //
746   // First frame requires ARP.
747   //
748   InsertTailList (&NeighborCache->Frames, &Token->Link);
749   InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);
750 
751   NeighborCache->ArpFree = TRUE;
752 
753   return EFI_SUCCESS;
754 
755 SendNow:
756  //
757   // Insert the tx token into the SentFrames list before calling Mnp->Transmit.
758   // Remove it if the returned status is not EFI_SUCCESS.
759   //
760   InsertTailList (&Interface->SentFrames, &Token->Link);
761   Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
762   if (EFI_ERROR (Status)) {
763     RemoveEntryList (&Token->Link);
764     goto Error;
765   }
766 
767   return EFI_SUCCESS;
768 
769 Error:
770   Ip6FreeLinkTxToken (Token);
771   return Status;
772 }
773 
774 /**
775   The heartbeat timer of IP6 service instance. It times out
776   all of its IP6 children's received-but-not-delivered and
777   transmitted-but-not-recycle packets.
778 
779   @param[in]  Event                 The IP6 service instance's heartbeat timer.
780   @param[in]  Context               The IP6 service instance.
781 
782 **/
783 VOID
784 EFIAPI
Ip6TimerTicking(IN EFI_EVENT Event,IN VOID * Context)785 Ip6TimerTicking (
786   IN EFI_EVENT              Event,
787   IN VOID                   *Context
788   )
789 {
790   IP6_SERVICE               *IpSb;
791 
792   IpSb = (IP6_SERVICE *) Context;
793   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
794 
795   Ip6PacketTimerTicking (IpSb);
796   Ip6NdTimerTicking (IpSb);
797   Ip6MldTimerTicking (IpSb);
798 }
799