]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Ip6Dxe/Ip6If.c
a93418847948e698f858f174a04909f613e6e4cb
[mirror_edk2.git] / NetworkPkg / Ip6Dxe / Ip6If.c
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
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
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 *
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
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 *
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
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
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
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
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
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 if (IpSb->InDestroy) {
570 return EFI_INVALID_PARAMETER;
571 }
572
573 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
574
575 Token = &IpSb->RecvRequest;
576 Token->CallBack = CallBack;
577 Token->Context = (VOID *) IpSb;
578
579 Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);
580 if (EFI_ERROR (Status)) {
581 return Status;
582 }
583
584 return EFI_SUCCESS;
585 }
586
587 /**
588 Callback funtion when frame transmission is finished. It will
589 call the frame owner's callback function to tell it the result.
590
591 @param[in] Context Context which points to the token.
592
593 **/
594 VOID
595 EFIAPI
596 Ip6OnFrameSentDpc (
597 IN VOID *Context
598 )
599 {
600 IP6_LINK_TX_TOKEN *Token;
601
602 Token = (IP6_LINK_TX_TOKEN *) Context;
603 NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);
604
605 RemoveEntryList (&Token->Link);
606
607 Token->CallBack (
608 Token->Packet,
609 Token->MnpToken.Status,
610 0,
611 Token->Context
612 );
613
614 Ip6FreeLinkTxToken (Token);
615 }
616
617 /**
618 Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.
619
620 @param[in] Event The transmit token's event.
621 @param[in] Context Context which points to the token.
622
623 **/
624 VOID
625 EFIAPI
626 Ip6OnFrameSent (
627 IN EFI_EVENT Event,
628 IN VOID *Context
629 )
630 {
631 //
632 // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK
633 //
634 QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);
635 }
636
637 /**
638 Send a frame from the interface. If the next hop is a multicast address,
639 it is transmitted immediately. If the next hop is a unicast,
640 and the NextHop's MAC is not known, it will perform address resolution.
641 If an error occurred, the CallBack won't be called. So, the caller
642 must test the return value, and take action when there is an error.
643
644 @param[in] Interface The interface to send the frame from
645 @param[in] IpInstance The IP child that request the transmission.
646 NULL if it is the IP6 driver itself.
647 @param[in] Packet The packet to transmit.
648 @param[in] NextHop The immediate destination to transmit the packet to.
649 @param[in] CallBack Function to call back when transmit finished.
650 @param[in] Context Opaque parameter to the callback.
651
652 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame.
653 @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop.
654 @retval EFI_SUCCESS The packet successfully transmitted.
655
656 **/
657 EFI_STATUS
658 Ip6SendFrame (
659 IN IP6_INTERFACE *Interface,
660 IN IP6_PROTOCOL *IpInstance OPTIONAL,
661 IN NET_BUF *Packet,
662 IN EFI_IPv6_ADDRESS *NextHop,
663 IN IP6_FRAME_CALLBACK CallBack,
664 IN VOID *Context
665 )
666 {
667 IP6_SERVICE *IpSb;
668 IP6_LINK_TX_TOKEN *Token;
669 EFI_STATUS Status;
670 IP6_NEIGHBOR_ENTRY *NeighborCache;
671 LIST_ENTRY *Entry;
672 IP6_NEIGHBOR_ENTRY *ArpQue;
673
674 IpSb = Interface->Service;
675 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
676
677 //
678 // Only when link local address is performing DAD, the interface could be used in unconfigured.
679 //
680 if (IpSb->LinkLocalOk) {
681 ASSERT (Interface->Configured);
682 }
683
684 Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);
685
686 if (Token == NULL) {
687 return EFI_OUT_OF_RESOURCES;
688 }
689
690 if (IP6_IS_MULTICAST (NextHop)) {
691 Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);
692 if (EFI_ERROR (Status)) {
693 goto Error;
694 }
695
696 goto SendNow;
697 }
698
699 //
700 // If send to itself, directly send out
701 //
702 if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {
703 IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);
704 goto SendNow;
705 }
706
707 //
708 // If unicast, check the neighbor state.
709 //
710
711 NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);
712 ASSERT (NeighborCache != NULL);
713
714 if (NeighborCache->Interface == NULL) {
715 NeighborCache->Interface = Interface;
716 }
717
718 switch (NeighborCache->State) {
719 case EfiNeighborStale:
720 NeighborCache->State = EfiNeighborDelay;
721 NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
722 //
723 // Fall through
724 //
725 case EfiNeighborReachable:
726 case EfiNeighborDelay:
727 case EfiNeighborProbe:
728 IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);
729 goto SendNow;
730 break;
731
732 default:
733 break;
734 }
735
736 //
737 // Have to do asynchronous ARP resolution. First check whether there is
738 // already a pending request.
739 //
740 NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
741 ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
742 if (ArpQue == NeighborCache) {
743 InsertTailList (&NeighborCache->Frames, &Token->Link);
744 NeighborCache->ArpFree = TRUE;
745 return EFI_SUCCESS;
746 }
747 }
748
749 //
750 // First frame requires ARP.
751 //
752 InsertTailList (&NeighborCache->Frames, &Token->Link);
753 InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);
754
755 NeighborCache->ArpFree = TRUE;
756
757 return EFI_SUCCESS;
758
759 SendNow:
760 //
761 // Insert the tx token into the SentFrames list before calling Mnp->Transmit.
762 // Remove it if the returned status is not EFI_SUCCESS.
763 //
764 InsertTailList (&Interface->SentFrames, &Token->Link);
765 Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
766 if (EFI_ERROR (Status)) {
767 RemoveEntryList (&Token->Link);
768 goto Error;
769 }
770
771 return EFI_SUCCESS;
772
773 Error:
774 Ip6FreeLinkTxToken (Token);
775 return Status;
776 }
777
778 /**
779 The heartbeat timer of IP6 service instance. It times out
780 all of its IP6 children's received-but-not-delivered and
781 transmitted-but-not-recycle packets.
782
783 @param[in] Event The IP6 service instance's heartbeat timer.
784 @param[in] Context The IP6 service instance.
785
786 **/
787 VOID
788 EFIAPI
789 Ip6TimerTicking (
790 IN EFI_EVENT Event,
791 IN VOID *Context
792 )
793 {
794 IP6_SERVICE *IpSb;
795
796 IpSb = (IP6_SERVICE *) Context;
797 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
798
799 Ip6PacketTimerTicking (IpSb);
800 Ip6NdTimerTicking (IpSb);
801 Ip6MldTimerTicking (IpSb);
802 }