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**/
25VOID
26EFIAPI
27Ip6OnFrameSent (
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**/
44BOOLEAN
45Ip6CancelInstanceFrame (
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**/
80EFI_STATUS
81Ip6SetAddress (
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**/
204IP6_INTERFACE *
205Ip6CreateInterface (
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
277ON_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**/
295VOID
296Ip6CleanInterface (
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**/
359IP6_LINK_TX_TOKEN *
360Ip6CreateLinkTxToken (
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**/
430VOID
431Ip6FreeLinkTxToken (
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**/
448VOID
449EFIAPI
450Ip6RecycleFrame (
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**/
472VOID
473EFIAPI
474Ip6OnFrameReceivedDpc (
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**/
536VOID
537EFIAPI
538Ip6OnFrameReceived (
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**/
560EFI_STATUS
561Ip6ReceiveFrame (
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**/
590VOID
591EFIAPI
592Ip6OnFrameSentDpc (
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**/
620VOID
621EFIAPI
622Ip6OnFrameSent (
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**/
653EFI_STATUS
654Ip6SendFrame (
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
755SendNow:
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
769Error:
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**/
783VOID
784EFIAPI
785Ip6TimerTicking (
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