• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Udp6 driver's whole implementation.
3 
4   Copyright (c) 2009 - 2016, 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 "Udp6Impl.h"
17 
18 UINT16  mUdp6RandomPort;
19 
20 /**
21   This function checks and timeouts the I/O datagrams holding by the corresponding
22   service context.
23 
24   @param[in]  Event              The event this function is registered to.
25   @param[in]  Context            The context data registered during the creation of
26                                  the Event.
27 
28 **/
29 VOID
30 EFIAPI
31 Udp6CheckTimeout (
32   IN EFI_EVENT  Event,
33   IN VOID       *Context
34   );
35 
36 /**
37   This function finds the udp instance by the specified <Address, Port> pair.
38 
39   @param[in]  InstanceList       Pointer to the head of the list linking the udp
40                                  instances.
41   @param[in]  Address            Pointer to the specified IPv6 address.
42   @param[in]  Port               The udp port number.
43 
44   @retval TRUE     The specified <Address, Port> pair is found.
45   @retval FALSE    Otherwise.
46 
47 **/
48 BOOLEAN
49 Udp6FindInstanceByPort (
50   IN LIST_ENTRY        *InstanceList,
51   IN EFI_IPv6_ADDRESS  *Address,
52   IN UINT16            Port
53   );
54 
55 /**
56   This function is the packet transmitting notify function registered to the IpIo
57   interface. It's called to signal the udp TxToken when the IpIo layer completes
58   transmitting of the udp datagram.
59 
60   @param[in]  Status            The completion status of the output udp datagram.
61   @param[in]  Context           Pointer to the context data.
62   @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.
63   @param[in]  NotifyData        Pointer to the notify data.
64 
65 **/
66 VOID
67 EFIAPI
68 Udp6DgramSent (
69   IN EFI_STATUS        Status,
70   IN VOID              *Context,
71   IN IP_IO_IP_PROTOCOL Sender,
72   IN VOID              *NotifyData
73   );
74 
75 /**
76   This function processes the received datagram passed up by the IpIo layer.
77 
78   @param[in]  Status            The status of this udp datagram.
79   @param[in]  IcmpError         The IcmpError code, only available when Status is
80                                 EFI_ICMP_ERROR.
81   @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.
82   @param[in]  Packet            Pointer to the NET_BUF containing the received udp
83                                 datagram.
84   @param[in]  Context           Pointer to the context data.
85 
86 **/
87 VOID
88 EFIAPI
89 Udp6DgramRcvd (
90   IN EFI_STATUS            Status,
91   IN UINT8                 IcmpError,
92   IN EFI_NET_SESSION_DATA  *NetSession,
93   IN NET_BUF               *Packet,
94   IN VOID                  *Context
95   );
96 
97 /**
98   This function cancle the token specified by Arg in the Map.
99 
100   @param[in]  Map             Pointer to the NET_MAP.
101   @param[in]  Item            Pointer to the NET_MAP_ITEM.
102   @param[in]  Arg             Pointer to the token to be cancelled, if NULL, all
103                               the tokens in this Map will be cancelled.
104                               This parameter is optional and may be NULL.
105 
106   @retval EFI_SUCCESS         The token is cancelled if Arg is NULL or the token
107                               is not the same as that in the Item if Arg is not
108                               NULL.
109   @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is
110                               cancelled.
111 
112 **/
113 EFI_STATUS
114 EFIAPI
115 Udp6CancelTokens (
116   IN NET_MAP       *Map,
117   IN NET_MAP_ITEM  *Item,
118   IN VOID          *Arg OPTIONAL
119   );
120 
121 /**
122   This function check if the received udp datagram matches with the Instance.
123 
124   @param[in]  Instance           Pointer to the udp instance context data.
125   @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted
126                                  from the received udp datagram.
127 
128   @retval TRUE     The udp datagram matches the receiving requirements of the Instance.
129   @retval FALSE    The udp datagram doe not match the receiving requirements of the Instance.
130 
131 **/
132 BOOLEAN
133 Udp6MatchDgram (
134   IN UDP6_INSTANCE_DATA     *Instance,
135   IN EFI_UDP6_SESSION_DATA  *Udp6Session
136   );
137 
138 /**
139   This function removes the Wrap specified by Context and releases relevant resources.
140 
141   @param[in]  Event                  The Event this notify function is registered to.
142   @param[in]  Context                Pointer to the context data.
143 
144 **/
145 VOID
146 EFIAPI
147 Udp6RecycleRxDataWrap (
148   IN EFI_EVENT  Event,
149   IN VOID       *Context
150   );
151 
152 /**
153   This function wraps the Packet into RxData.
154 
155   @param[in]  Instance           Pointer to the instance context data.
156   @param[in]  Packet             Pointer to the buffer containing the received
157                                  datagram.
158   @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this
159                                  datagram.
160 
161   @return Pointer to the structure wrapping the RxData and the Packet.
162 
163 **/
164 UDP6_RXDATA_WRAP *
165 Udp6WrapRxData (
166   IN UDP6_INSTANCE_DATA     *Instance,
167   IN NET_BUF                *Packet,
168   IN EFI_UDP6_RECEIVE_DATA  *RxData
169   );
170 
171 /**
172   This function enqueues the received datagram into the instances' receiving queues.
173 
174   @param[in]  Udp6Service        Pointer to the udp service context data.
175   @param[in]  Packet             Pointer to the buffer containing the received
176                                  datagram.
177   @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this
178                                  datagram.
179 
180   @return The times this datagram is enqueued.
181 
182 **/
183 UINTN
184 Udp6EnqueueDgram (
185   IN UDP6_SERVICE_DATA      *Udp6Service,
186   IN NET_BUF                *Packet,
187   IN EFI_UDP6_RECEIVE_DATA  *RxData
188   );
189 
190 /**
191   This function delivers the datagrams enqueued in the instances.
192 
193   @param[in]  Udp6Service            Pointer to the udp service context data.
194 
195 **/
196 VOID
197 Udp6DeliverDgram (
198   IN UDP6_SERVICE_DATA  *Udp6Service
199   );
200 
201 /**
202   This function demultiplexes the received udp datagram to the appropriate instances.
203 
204   @param[in]  Udp6Service        Pointer to the udp service context data.
205   @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted from
206                                  the received datagram.
207   @param[in]  Packet             Pointer to the buffer containing the received udp
208                                  datagram.
209 
210 **/
211 VOID
212 Udp6Demultiplex (
213   IN UDP6_SERVICE_DATA     *Udp6Service,
214   IN EFI_NET_SESSION_DATA  *NetSession,
215   IN NET_BUF               *Packet
216   );
217 
218 /**
219   This function handles the received Icmp Error message and demultiplexes it to the
220   instance.
221 
222   @param[in]       Udp6Service        Pointer to the udp service context data.
223   @param[in]       IcmpError          The icmp error code.
224   @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted
225                                       from the received Icmp Error packet.
226   @param[in, out]  Packet             Pointer to the Icmp Error packet.
227 
228 **/
229 VOID
230 Udp6IcmpHandler (
231   IN UDP6_SERVICE_DATA     *Udp6Service,
232   IN UINT8                 IcmpError,
233   IN EFI_NET_SESSION_DATA  *NetSession,
234   IN OUT NET_BUF           *Packet
235   );
236 
237 /**
238   This function builds and sends out a icmp port unreachable message.
239 
240   @param[in]  IpIo               Pointer to the IP_IO instance.
241   @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet
242                                  causes this icmp error message.
243   @param[in]  Udp6Header         Pointer to the udp header of the datagram causes
244                                  this icmp error message.
245 
246 **/
247 VOID
248 Udp6SendPortUnreach (
249   IN IP_IO                 *IpIo,
250   IN EFI_NET_SESSION_DATA  *NetSession,
251   IN VOID                  *Udp6Header
252   );
253 
254 /**
255   Find the key in the netmap
256 
257   @param[in]  Map                    The netmap to search within.
258   @param[in]  Key                    The key to search.
259 
260   @return The point to the item contains the Key, or NULL if Key isn't in the map.
261 
262 **/
263 NET_MAP_ITEM *
264 Udp6MapMultiCastAddr (
265   IN  NET_MAP               *Map,
266   IN  VOID                  *Key
267   );
268 
269 /**
270   Create the Udp service context data.
271 
272   @param[in]  Udp6Service        Pointer to the UDP6_SERVICE_DATA.
273   @param[in]  ImageHandle        The image handle of this udp6 driver.
274   @param[in]  ControllerHandle   The controller handle this udp6 driver binds on.
275 
276   @retval EFI_SUCCESS            The udp6 service context data was created and
277                                  initialized.
278   @retval EFI_OUT_OF_RESOURCES   Cannot allocate memory.
279   @retval Others                 An error condition occurred.
280 
281 **/
282 EFI_STATUS
Udp6CreateService(IN UDP6_SERVICE_DATA * Udp6Service,IN EFI_HANDLE ImageHandle,IN EFI_HANDLE ControllerHandle)283 Udp6CreateService (
284   IN UDP6_SERVICE_DATA  *Udp6Service,
285   IN EFI_HANDLE         ImageHandle,
286   IN EFI_HANDLE         ControllerHandle
287   )
288 {
289   EFI_STATUS       Status;
290   IP_IO_OPEN_DATA  OpenData;
291 
292   ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));
293 
294   Udp6Service->Signature        = UDP6_SERVICE_DATA_SIGNATURE;
295   Udp6Service->ServiceBinding   = mUdp6ServiceBinding;
296   Udp6Service->ImageHandle      = ImageHandle;
297   Udp6Service->ControllerHandle = ControllerHandle;
298   Udp6Service->ChildrenNumber   = 0;
299 
300   InitializeListHead (&Udp6Service->ChildrenList);
301 
302   //
303   // Create the IpIo for this service context.
304   //
305   Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6);
306   if (Udp6Service->IpIo == NULL) {
307     return EFI_OUT_OF_RESOURCES;
308   }
309 
310   //
311   // Set the OpenData used to open the IpIo.
312   //
313   CopyMem (
314     &OpenData.IpConfigData.Ip6CfgData,
315     &mIp6IoDefaultIpConfigData,
316     sizeof (EFI_IP6_CONFIG_DATA)
317     );
318   OpenData.RcvdContext           = (VOID *) Udp6Service;
319   OpenData.SndContext            = NULL;
320   OpenData.PktRcvdNotify         = Udp6DgramRcvd;
321   OpenData.PktSentNotify         = Udp6DgramSent;
322 
323   //
324   // Configure and start the IpIo.
325   //
326   Status = IpIoOpen (Udp6Service->IpIo, &OpenData);
327   if (EFI_ERROR (Status)) {
328     goto ON_ERROR;
329   }
330 
331   //
332   // Create the event for Udp timeout checking.
333   //
334   Status = gBS->CreateEvent (
335                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
336                   TPL_CALLBACK,
337                   Udp6CheckTimeout,
338                   Udp6Service,
339                   &Udp6Service->TimeoutEvent
340                   );
341   if (EFI_ERROR (Status)) {
342     goto ON_ERROR;
343   }
344 
345   //
346   // Start the timeout timer event.
347   //
348   Status = gBS->SetTimer (
349                   Udp6Service->TimeoutEvent,
350                   TimerPeriodic,
351                   UDP6_TIMEOUT_INTERVAL
352                   );
353   if (EFI_ERROR (Status)) {
354     goto ON_ERROR;
355   }
356 
357   return EFI_SUCCESS;
358 
359 ON_ERROR:
360 
361   if (Udp6Service->TimeoutEvent != NULL) {
362     gBS->CloseEvent (Udp6Service->TimeoutEvent);
363   }
364 
365   IpIoDestroy (Udp6Service->IpIo);
366   Udp6Service->IpIo = NULL;
367 
368   return Status;
369 }
370 
371 
372 /**
373   Clean the Udp service context data.
374 
375   @param[in, out]  Udp6Service      Pointer to the UDP6_SERVICE_DATA.
376 
377 **/
378 VOID
Udp6CleanService(IN OUT UDP6_SERVICE_DATA * Udp6Service)379 Udp6CleanService (
380   IN OUT UDP6_SERVICE_DATA  *Udp6Service
381   )
382 {
383   //
384   // Close the TimeoutEvent timer.
385   //
386   gBS->CloseEvent (Udp6Service->TimeoutEvent);
387 
388   //
389   // Destroy the IpIo.
390   //
391   IpIoDestroy (Udp6Service->IpIo);
392   Udp6Service->IpIo = NULL;
393 
394   ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));
395 }
396 
397 
398 /**
399   This function checks and times out the I/O datagrams listed in the
400   UDP6_SERVICE_DATA which is specified by the input parameter Context.
401 
402 
403   @param[in]  Event              The event this function registered to.
404   @param[in]  Context            The context data registered during the creation of
405                                  the Event.
406 
407 **/
408 VOID
409 EFIAPI
Udp6CheckTimeout(IN EFI_EVENT Event,IN VOID * Context)410 Udp6CheckTimeout (
411   IN EFI_EVENT  Event,
412   IN VOID       *Context
413   )
414 {
415   UDP6_SERVICE_DATA   *Udp6Service;
416   LIST_ENTRY          *Entry;
417   UDP6_INSTANCE_DATA  *Instance;
418   LIST_ENTRY          *WrapEntry;
419   LIST_ENTRY          *NextEntry;
420   UDP6_RXDATA_WRAP    *Wrap;
421 
422   Udp6Service = (UDP6_SERVICE_DATA *) Context;
423   NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE);
424 
425   NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
426     //
427     // Iterate all the instances belonging to this service context.
428     //
429     Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
430     NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE);
431 
432     if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {
433       //
434       // Skip this instance if it's not configured or no receive timeout.
435       //
436       continue;
437     }
438 
439     NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {
440       //
441       // Iterate all the rxdatas belonging to this udp instance.
442       //
443       Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link);
444 
445       if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) {
446         //
447         // Remove this RxData if it timeouts.
448         //
449         Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);
450       } else {
451         Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10;
452       }
453     }
454   }
455 }
456 
457 
458 /**
459   This function intializes the new created udp instance.
460 
461   @param[in]       Udp6Service      Pointer to the UDP6_SERVICE_DATA.
462   @param[in, out]  Instance         Pointer to the un-initialized UDP6_INSTANCE_DATA.
463 
464 **/
465 VOID
Udp6InitInstance(IN UDP6_SERVICE_DATA * Udp6Service,IN OUT UDP6_INSTANCE_DATA * Instance)466 Udp6InitInstance (
467   IN UDP6_SERVICE_DATA       *Udp6Service,
468   IN OUT UDP6_INSTANCE_DATA  *Instance
469   )
470 {
471   //
472   // Set the signature.
473   //
474   Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE;
475 
476   //
477   // Init the lists.
478   //
479   InitializeListHead (&Instance->Link);
480   InitializeListHead (&Instance->RcvdDgramQue);
481   InitializeListHead (&Instance->DeliveredDgramQue);
482 
483   //
484   // Init the NET_MAPs.
485   //
486   NetMapInit (&Instance->TxTokens);
487   NetMapInit (&Instance->RxTokens);
488   NetMapInit (&Instance->McastIps);
489 
490   //
491   // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members.
492   //
493   Instance->Udp6Service = Udp6Service;
494   CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL));
495   Instance->IcmpError   = EFI_SUCCESS;
496   Instance->Configured  = FALSE;
497   Instance->IsNoMapping = FALSE;
498   Instance->InDestroy   = FALSE;
499 }
500 
501 
502 /**
503   This function cleans the udp instance.
504 
505   @param[in, out]  Instance       Pointer to the UDP6_INSTANCE_DATA to clean.
506 
507 **/
508 VOID
Udp6CleanInstance(IN OUT UDP6_INSTANCE_DATA * Instance)509 Udp6CleanInstance (
510   IN OUT UDP6_INSTANCE_DATA  *Instance
511   )
512 {
513   NetMapClean (&Instance->McastIps);
514   NetMapClean (&Instance->RxTokens);
515   NetMapClean (&Instance->TxTokens);
516 }
517 
518 
519 /**
520   This function finds the udp instance by the specified <Address, Port> pair.
521 
522   @param[in]  InstanceList       Pointer to the head of the list linking the udp
523                                  instances.
524   @param[in]  Address            Pointer to the specified IPv6 address.
525   @param[in]  Port               The udp port number.
526 
527   @retval TRUE     The specified <Address, Port> pair is found.
528   @retval FALSE    Otherwise.
529 
530 **/
531 BOOLEAN
Udp6FindInstanceByPort(IN LIST_ENTRY * InstanceList,IN EFI_IPv6_ADDRESS * Address,IN UINT16 Port)532 Udp6FindInstanceByPort (
533   IN LIST_ENTRY        *InstanceList,
534   IN EFI_IPv6_ADDRESS  *Address,
535   IN UINT16            Port
536   )
537 {
538   LIST_ENTRY            *Entry;
539   UDP6_INSTANCE_DATA    *Instance;
540   EFI_UDP6_CONFIG_DATA  *ConfigData;
541 
542   NET_LIST_FOR_EACH (Entry, InstanceList) {
543     //
544     // Iterate all the udp instances.
545     //
546     Instance   = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
547     ConfigData = &Instance->ConfigData;
548 
549     if (!Instance->Configured || ConfigData->AcceptAnyPort) {
550       //
551       // If the instance is not configured, or the configdata of the instance indicates
552       // this instance accepts any port, skip it.
553       //
554       continue;
555     }
556 
557     if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) &&
558         (ConfigData->StationPort == Port)
559         ) {
560       //
561       // If both the address and the port are the same, return TRUE.
562       //
563       return TRUE;
564     }
565   }
566 
567   //
568   // Return FALSE when matching fails.
569   //
570   return FALSE;
571 }
572 
573 
574 /**
575   This function tries to bind the udp instance according to the configured port
576   allocation stragety.
577 
578   @param[in]  InstanceList       Pointer to the head of the list linking the udp
579                                  instances.
580   @param[in]  ConfigData         Pointer to the ConfigData of the instance to be
581                                  bound.
582 
583   @retval EFI_SUCCESS            The bound operation completed successfully.
584   @retval EFI_ACCESS_DENIED      The <Address, Port> specified by the ConfigData is
585                                  already used by other instance.
586   @retval EFI_OUT_OF_RESOURCES   No available port resources.
587 
588 **/
589 EFI_STATUS
Udp6Bind(IN LIST_ENTRY * InstanceList,IN EFI_UDP6_CONFIG_DATA * ConfigData)590 Udp6Bind (
591   IN LIST_ENTRY            *InstanceList,
592   IN EFI_UDP6_CONFIG_DATA  *ConfigData
593   )
594 {
595   EFI_IPv6_ADDRESS  *StationAddress;
596   UINT16            StartPort;
597 
598   if (ConfigData->AcceptAnyPort) {
599     return EFI_SUCCESS;
600   }
601 
602   StationAddress = &ConfigData->StationAddress;
603 
604   if (ConfigData->StationPort != 0) {
605 
606     if (!ConfigData->AllowDuplicatePort &&
607         Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)
608         ) {
609       //
610       // Do not allow duplicate ports and the port is already used by other instance.
611       //
612       return EFI_ACCESS_DENIED;
613     }
614   } else {
615     //
616     // Select a random port for this instance.
617     //
618     if (ConfigData->AllowDuplicatePort) {
619       //
620       // Just pick up the random port if the instance allows duplicate port.
621       //
622       ConfigData->StationPort = mUdp6RandomPort;
623     } else {
624 
625       StartPort = mUdp6RandomPort;
626 
627       while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) {
628 
629         mUdp6RandomPort++;
630         if (mUdp6RandomPort == 0) {
631           mUdp6RandomPort = UDP6_PORT_KNOWN;
632         }
633 
634         if (mUdp6RandomPort == StartPort) {
635           //
636           // No available port.
637           //
638           return EFI_OUT_OF_RESOURCES;
639         }
640       }
641 
642       ConfigData->StationPort = mUdp6RandomPort;
643     }
644 
645     mUdp6RandomPort++;
646     if (mUdp6RandomPort == 0) {
647       mUdp6RandomPort = UDP6_PORT_KNOWN;
648     }
649   }
650   return EFI_SUCCESS;
651 }
652 
653 
654 /**
655   This function is used to check whether the NewConfigData has any un-reconfigurable
656   parameters changed compared to the OldConfigData.
657 
658   @param[in]  OldConfigData    Pointer to the current ConfigData the udp instance
659                                uses.
660   @param[in]  NewConfigData    Pointer to the new ConfigData.
661 
662   @retval TRUE     The instance is reconfigurable according to the NewConfigData.
663   @retval FALSE    Otherwise.
664 
665 **/
666 BOOLEAN
Udp6IsReconfigurable(IN EFI_UDP6_CONFIG_DATA * OldConfigData,IN EFI_UDP6_CONFIG_DATA * NewConfigData)667 Udp6IsReconfigurable (
668   IN EFI_UDP6_CONFIG_DATA  *OldConfigData,
669   IN EFI_UDP6_CONFIG_DATA  *NewConfigData
670   )
671 {
672   if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||
673       (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||
674       (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)
675       ) {
676     //
677     // The receiving filter parameters cannot be changed.
678     //
679     return FALSE;
680   }
681 
682   if ((!NewConfigData->AcceptAnyPort) &&
683       (NewConfigData->StationPort != OldConfigData->StationPort)
684       ) {
685     //
686     // The port is not changeable.
687     //
688     return FALSE;
689   }
690 
691   if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) {
692     //
693     //  The StationAddress is not the same.
694     //
695     return FALSE;
696   }
697 
698 
699   if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {
700     //
701     // The remoteaddress is not the same.
702     //
703     return FALSE;
704   }
705 
706   if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) &&
707       (NewConfigData->RemotePort != OldConfigData->RemotePort)
708       ) {
709     //
710     // The RemotePort differs if it's designated in the configdata.
711     //
712     return FALSE;
713   }
714 
715   //
716   // All checks pass, return TRUE.
717   //
718   return TRUE;
719 }
720 
721 
722 /**
723   This function builds the Ip6 configdata from the Udp6ConfigData.
724 
725   @param[in]       Udp6ConfigData         Pointer to the EFI_UDP6_CONFIG_DATA.
726   @param[in, out]  Ip6ConfigData          Pointer to the EFI_IP6_CONFIG_DATA.
727 
728 **/
729 VOID
Udp6BuildIp6ConfigData(IN EFI_UDP6_CONFIG_DATA * Udp6ConfigData,IN OUT EFI_IP6_CONFIG_DATA * Ip6ConfigData)730 Udp6BuildIp6ConfigData (
731   IN EFI_UDP6_CONFIG_DATA      *Udp6ConfigData,
732   IN OUT EFI_IP6_CONFIG_DATA   *Ip6ConfigData
733   )
734 {
735   CopyMem (
736     Ip6ConfigData,
737     &mIp6IoDefaultIpConfigData,
738     sizeof (EFI_IP6_CONFIG_DATA)
739     );
740   Ip6ConfigData->DefaultProtocol      = EFI_IP_PROTO_UDP;
741   Ip6ConfigData->AcceptPromiscuous    = Udp6ConfigData->AcceptPromiscuous;
742   IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress);
743   IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress);
744   //
745   // Use the -1 magic number to disable the receiving process of the ip instance.
746   //
747   Ip6ConfigData->ReceiveTimeout    = (UINT32) (-1);
748 }
749 
750 
751 /**
752   This function validates the TxToken. It returns the error code according to the spec.
753 
754   @param[in]  Instance           Pointer to the udp instance context data.
755   @param[in]  TxToken            Pointer to the token to be checked.
756 
757   @retval EFI_SUCCESS            The TxToken is valid.
758   @retval EFI_INVALID_PARAMETER  One or more of the following are TRUE:
759                                  Token.Event is NULL;
760                                  Token.Packet.TxData is NULL;
761                                  Token.Packet.TxData.FragmentCount is zero;
762                                  Token.Packet.TxData.DataLength is not equal to the
763                                  sum of fragment lengths;
764                                  One or more of the
765                                  Token.Packet.TxData.FragmentTable[].FragmentLength
766                                  fields is zero;
767                                  One or more of the
768                                  Token.Packet.TxData.FragmentTable[].FragmentBuffer
769                                  fields is NULL;
770                                  UdpSessionData.DestinationAddress are not valid
771                                  unicast IPv6 addresses if the UdpSessionData is
772                                  not NULL;
773                                  UdpSessionData.DestinationPort and
774                                  ConfigData.RemotePort are all zero if the
775                                  UdpSessionData is not NULL.
776   @retval EFI_BAD_BUFFER_SIZE    The data length is greater than the maximum UDP
777                                  packet size.
778 
779 **/
780 EFI_STATUS
Udp6ValidateTxToken(IN UDP6_INSTANCE_DATA * Instance,IN EFI_UDP6_COMPLETION_TOKEN * TxToken)781 Udp6ValidateTxToken (
782   IN UDP6_INSTANCE_DATA         *Instance,
783   IN EFI_UDP6_COMPLETION_TOKEN  *TxToken
784   )
785 {
786   EFI_UDP6_TRANSMIT_DATA  *TxData;
787   UINT32                  Index;
788   UINT32                  TotalLen;
789   EFI_UDP6_CONFIG_DATA    *ConfigData;
790   EFI_UDP6_SESSION_DATA   *UdpSessionData;
791 
792 
793   if (TxToken->Event == NULL) {
794     return EFI_INVALID_PARAMETER;
795   }
796 
797   TxData = TxToken->Packet.TxData;
798 
799   if ((TxData == NULL) || (TxData->FragmentCount == 0)) {
800     return EFI_INVALID_PARAMETER;
801   }
802 
803   TotalLen = 0;
804   for (Index = 0; Index < TxData->FragmentCount; Index++) {
805 
806     if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
807         (TxData->FragmentTable[Index].FragmentLength == 0)
808         ) {
809       //
810       // If the FragmentBuffer is NULL, or the FragmentLeng is zero.
811       //
812       return EFI_INVALID_PARAMETER;
813     }
814 
815     TotalLen += TxData->FragmentTable[Index].FragmentLength;
816   }
817 
818   if (TotalLen != TxData->DataLength) {
819     //
820     // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the
821     // DataLength.
822     //
823     return EFI_INVALID_PARAMETER;
824   }
825 
826   ConfigData     = &Instance->ConfigData;
827   UdpSessionData = TxData->UdpSessionData;
828 
829   if (UdpSessionData != NULL) {
830 
831     if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {
832       //
833       // Ambiguous; no avalaible DestinationPort for this token.
834       //
835       return EFI_INVALID_PARAMETER;
836     }
837 
838     if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&
839         NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)
840         ) {
841       //
842       // The DestinationAddress is not specificed.
843       //
844       return EFI_INVALID_PARAMETER;
845     }
846 
847     if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&
848         !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)
849         ) {
850       //
851       // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress
852       // is not zero too.
853       //
854       return EFI_INVALID_PARAMETER;
855     }
856   } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) {
857     //
858     // The configured RemoteAddress is all zero, and the user doesn't override the
859     // destination address.
860     //
861     return EFI_INVALID_PARAMETER;
862   }
863 
864   if (TxData->DataLength > UDP6_MAX_DATA_SIZE) {
865     return EFI_BAD_BUFFER_SIZE;
866   }
867 
868   return EFI_SUCCESS;
869 }
870 
871 
872 /**
873   This function checks whether the specified Token duplicates the one in the Map.
874 
875   @param[in]  Map                Pointer to the NET_MAP.
876   @param[in]  Item               Pointer to the NET_MAP_ITEM contain the pointer to
877                                  the Token.
878   @param[in]  Context            Pointer to the Token to be checked.
879 
880   @retval EFI_SUCCESS            The Token specified by Context differs from the
881                                  one in the Item.
882   @retval EFI_ACCESS_DENIED      The Token duplicates with the one in the Item.
883 
884 **/
885 EFI_STATUS
886 EFIAPI
Udp6TokenExist(IN NET_MAP * Map,IN NET_MAP_ITEM * Item,IN VOID * Context)887 Udp6TokenExist (
888   IN NET_MAP       *Map,
889   IN NET_MAP_ITEM  *Item,
890   IN VOID          *Context
891   )
892 {
893   EFI_UDP6_COMPLETION_TOKEN  *Token;
894   EFI_UDP6_COMPLETION_TOKEN  *TokenInItem;
895 
896   Token       = (EFI_UDP6_COMPLETION_TOKEN *) Context;
897   TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;
898 
899   if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
900     //
901     // The Token duplicates with the TokenInItem in case either the two pointers are the
902     // same, or the Events of these two tokens are the same.
903     //
904     return EFI_ACCESS_DENIED;
905   }
906 
907   return EFI_SUCCESS;
908 }
909 
910 
911 /**
912   This function calculates the checksum for the Packet, utilizing the pre-calculated
913   pseudo HeadSum to reduce some overhead.
914 
915   @param[in]  Packet           Pointer to the NET_BUF contains the udp datagram.
916   @param[in]  HeadSum          Checksum of the pseudo header, execpt the length
917                                field.
918 
919   @return The 16-bit checksum of this udp datagram.
920 
921 **/
922 UINT16
Udp6Checksum(IN NET_BUF * Packet,IN UINT16 HeadSum)923 Udp6Checksum (
924   IN NET_BUF *Packet,
925   IN UINT16  HeadSum
926   )
927 {
928   UINT16  Checksum;
929 
930   Checksum  = NetbufChecksum (Packet);
931   Checksum  = NetAddChecksum (Checksum, HeadSum);
932 
933   Checksum  = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));
934   Checksum  = (UINT16) (~Checksum);
935   return Checksum;
936 }
937 
938 
939 /**
940   This function removes the specified Token from the TokenMap.
941 
942   @param[in]  TokenMap           Pointer to the NET_MAP containing the tokens.
943   @param[in]  Token              Pointer to the Token to be removed.
944 
945   @retval EFI_SUCCESS            The specified Token is removed from the TokenMap.
946   @retval EFI_NOT_FOUND          The specified Token is not found in the TokenMap.
947 
948 **/
949 EFI_STATUS
Udp6RemoveToken(IN NET_MAP * TokenMap,IN EFI_UDP6_COMPLETION_TOKEN * Token)950 Udp6RemoveToken (
951   IN NET_MAP                    *TokenMap,
952   IN EFI_UDP6_COMPLETION_TOKEN  *Token
953   )
954 {
955   NET_MAP_ITEM  *Item;
956 
957   //
958   // Find the Token first.
959   //
960   Item = NetMapFindKey (TokenMap, (VOID *) Token);
961 
962   if (Item != NULL) {
963     //
964     // Remove the token if it's found in the map.
965     //
966     NetMapRemoveItem (TokenMap, Item, NULL);
967 
968     return EFI_SUCCESS;
969   }
970   return EFI_NOT_FOUND;
971 }
972 
973 
974 /**
975   This function is the packet transmitting notify function registered to the IpIo
976   interface. It's called to signal the udp TxToken when IpIo layer completes the
977   transmitting of the udp datagram.
978 
979   @param[in]  Status            The completion status of the output udp datagram.
980   @param[in]  Context           Pointer to the context data.
981   @param[in]  Sender            Specify a EFI_IP6_PROTOCOL for sending.
982   @param[in]  NotifyData        Pointer to the notify data.
983 
984 **/
985 VOID
986 EFIAPI
Udp6DgramSent(IN EFI_STATUS Status,IN VOID * Context,IN IP_IO_IP_PROTOCOL Sender,IN VOID * NotifyData)987 Udp6DgramSent (
988   IN EFI_STATUS        Status,
989   IN VOID              *Context,
990   IN IP_IO_IP_PROTOCOL Sender,
991   IN VOID              *NotifyData
992   )
993 {
994   UDP6_INSTANCE_DATA         *Instance;
995   EFI_UDP6_COMPLETION_TOKEN  *Token;
996 
997   Instance = (UDP6_INSTANCE_DATA *) Context;
998   Token    = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData;
999 
1000   if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {
1001     //
1002     // The token may be cancelled. Only signal it if the remove operation succeeds.
1003     //
1004     Token->Status = Status;
1005     gBS->SignalEvent (Token->Event);
1006     DispatchDpc ();
1007   }
1008 }
1009 
1010 
1011 /**
1012   This function processes the received datagram passed up by the IpIo layer.
1013 
1014   @param[in]  Status            The status of this udp datagram.
1015   @param[in]  IcmpError         The IcmpError code, only available when Status is
1016                                 EFI_ICMP_ERROR.
1017   @param[in]  NetSession        Pointer to the EFI_NET_SESSION_DATA.
1018   @param[in]  Packet            Pointer to the NET_BUF containing the received udp
1019                                 datagram.
1020   @param[in]  Context           Pointer to the context data.
1021 
1022 **/
1023 VOID
1024 EFIAPI
Udp6DgramRcvd(IN EFI_STATUS Status,IN UINT8 IcmpError,IN EFI_NET_SESSION_DATA * NetSession,IN NET_BUF * Packet,IN VOID * Context)1025 Udp6DgramRcvd (
1026   IN EFI_STATUS            Status,
1027   IN UINT8                 IcmpError,
1028   IN EFI_NET_SESSION_DATA  *NetSession,
1029   IN NET_BUF               *Packet,
1030   IN VOID                  *Context
1031   )
1032 {
1033   NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
1034 
1035   //
1036   // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.
1037   //
1038   if (Status == EFI_SUCCESS) {
1039 
1040     //
1041     // Demultiplex the received datagram.
1042     //
1043     Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet);
1044   } else {
1045     //
1046     // Handle the ICMP6 Error packet.
1047     //
1048     Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);
1049   }
1050 
1051   //
1052   // Dispatch the DPC queued by the NotifyFunction of the rx token's events
1053   // that are signaled with received data.
1054   //
1055   DispatchDpc ();
1056 }
1057 
1058 
1059 /**
1060   This function removes the multicast group specified by Arg from the Map.
1061 
1062   @param[in]  Map                Pointer to the NET_MAP.
1063   @param[in]  Item               Pointer to the NET_MAP_ITEM.
1064   @param[in]  Arg                Pointer to the Arg, it's the pointer to a
1065                                  multicast IPv6 Address. This parameter is
1066                                  optional and may be NULL.
1067 
1068   @retval EFI_SUCCESS            The multicast address is removed.
1069   @retval EFI_ABORTED            The specified multicast address is removed, and the
1070                                  Arg is not NULL.
1071 
1072 **/
1073 EFI_STATUS
1074 EFIAPI
Udp6LeaveGroup(IN NET_MAP * Map,IN NET_MAP_ITEM * Item,IN VOID * Arg OPTIONAL)1075 Udp6LeaveGroup (
1076   IN NET_MAP       *Map,
1077   IN NET_MAP_ITEM  *Item,
1078   IN VOID          *Arg OPTIONAL
1079   )
1080 {
1081   EFI_IPv6_ADDRESS  *McastIp;
1082 
1083   McastIp = Arg;
1084 
1085   if ((McastIp != NULL) &&
1086       !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key))
1087       ) {
1088     //
1089     // McastIp is not NULL and the multicast address contained in the Item
1090     // is not the same as McastIp.
1091     //
1092     return EFI_SUCCESS;
1093   }
1094 
1095   FreePool (Item->Key);
1096 
1097   //
1098   // Remove this Item.
1099   //
1100   NetMapRemoveItem (Map, Item, NULL);
1101 
1102   if (McastIp != NULL) {
1103     //
1104     // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.
1105     //
1106     return EFI_ABORTED;
1107   }
1108 
1109   return EFI_SUCCESS;
1110 }
1111 
1112 
1113 /**
1114   This function cancle the token specified by Arg in the Map.
1115 
1116   @param[in]  Map             Pointer to the NET_MAP.
1117   @param[in]  Item            Pointer to the NET_MAP_ITEM.
1118   @param[in]  Arg             Pointer to the token to be cancelled. If NULL, all
1119                               the tokens in this Map will be cancelled.
1120                               This parameter is optional and may be NULL.
1121 
1122   @retval EFI_SUCCESS         The token is cancelled if Arg is NULL, or the token
1123                               is not the same as that in the Item, if Arg is not
1124                               NULL.
1125   @retval EFI_ABORTED         Arg is not NULL, and the token specified by Arg is
1126                               cancelled.
1127 
1128 **/
1129 EFI_STATUS
1130 EFIAPI
Udp6CancelTokens(IN NET_MAP * Map,IN NET_MAP_ITEM * Item,IN VOID * Arg OPTIONAL)1131 Udp6CancelTokens (
1132   IN NET_MAP       *Map,
1133   IN NET_MAP_ITEM  *Item,
1134   IN VOID          *Arg OPTIONAL
1135   )
1136 {
1137   EFI_UDP6_COMPLETION_TOKEN  *TokenToCancel;
1138   NET_BUF                    *Packet;
1139   IP_IO                      *IpIo;
1140 
1141   if ((Arg != NULL) && (Item->Key != Arg)) {
1142     return EFI_SUCCESS;
1143   }
1144 
1145   if (Item->Value != NULL) {
1146     //
1147     // If the token is a transmit token, the corresponding Packet is recorded in
1148     // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken
1149     // will invoke Udp6DgramSent, the token will be signaled and this Item will
1150     // be removed from the Map there.
1151     //
1152     Packet  = (NET_BUF *) (Item->Value);
1153     IpIo    = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
1154 
1155     IpIoCancelTxToken (IpIo, Packet);
1156   } else {
1157     //
1158     // The token is a receive token. Abort it and remove it from the Map.
1159     //
1160     TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;
1161     NetMapRemoveItem (Map, Item, NULL);
1162 
1163     TokenToCancel->Status = EFI_ABORTED;
1164     gBS->SignalEvent (TokenToCancel->Event);
1165   }
1166 
1167   if (Arg != NULL) {
1168     return EFI_ABORTED;
1169   }
1170 
1171   return EFI_SUCCESS;
1172 }
1173 
1174 
1175 /**
1176   This function removes all the Wrap datas in the RcvdDgramQue.
1177 
1178   @param[in]  Instance    Pointer to the Udp6 Instance.
1179 
1180 **/
1181 VOID
Udp6FlushRcvdDgram(IN UDP6_INSTANCE_DATA * Instance)1182 Udp6FlushRcvdDgram (
1183   IN UDP6_INSTANCE_DATA  *Instance
1184   )
1185 {
1186   UDP6_RXDATA_WRAP  *Wrap;
1187 
1188   while (!IsListEmpty (&Instance->RcvdDgramQue)) {
1189     //
1190     // Iterate all the Wraps in the RcvdDgramQue.
1191     //
1192     Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);
1193 
1194     //
1195     // The Wrap will be removed from the RcvdDgramQue by this function call.
1196     //
1197     Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);
1198   }
1199 }
1200 
1201 
1202 
1203 /**
1204   Cancel Udp6 tokens from the Udp6 instance.
1205 
1206   @param[in]  Instance           Pointer to the udp instance context data.
1207   @param[in]  Token              Pointer to the token to be canceled. If NULL, all
1208                                  tokens in this instance will be cancelled.
1209                                  This parameter is optional and may be NULL.
1210 
1211   @retval EFI_SUCCESS            The Token is cancelled.
1212   @retval EFI_NOT_FOUND          The Token is not found.
1213 
1214 **/
1215 EFI_STATUS
Udp6InstanceCancelToken(IN UDP6_INSTANCE_DATA * Instance,IN EFI_UDP6_COMPLETION_TOKEN * Token OPTIONAL)1216 Udp6InstanceCancelToken (
1217   IN UDP6_INSTANCE_DATA         *Instance,
1218   IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL
1219   )
1220 {
1221   EFI_STATUS  Status;
1222 
1223   //
1224   // Cancel this token from the TxTokens map.
1225   //
1226   Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token);
1227 
1228   if ((Token != NULL) && (Status == EFI_ABORTED)) {
1229     //
1230     // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
1231     // the TxTokens and returns success.
1232     //
1233     return EFI_SUCCESS;
1234   }
1235 
1236   //
1237   // Try to cancel this token from the RxTokens map in condition either the Token
1238   // is NULL or the specified Token is not in TxTokens.
1239   //
1240   Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token);
1241 
1242   if ((Token != NULL) && (Status == EFI_SUCCESS)) {
1243     //
1244     // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the
1245     // TxTokens nor the RxTokens, or say, it's not found.
1246     //
1247     return EFI_NOT_FOUND;
1248   }
1249 
1250   ASSERT ((Token != NULL) ||
1251           ((0 == NetMapGetCount (&Instance->TxTokens)) &&
1252           (0 == NetMapGetCount (&Instance->RxTokens)))
1253           );
1254 
1255   return EFI_SUCCESS;
1256 }
1257 
1258 
1259 /**
1260   This function checks if the received udp datagram matches with the Instance.
1261 
1262   @param[in]  Instance           Pointer to the udp instance context data.
1263   @param[in]  Udp6Session        Pointer to the EFI_UDP6_SESSION_DATA abstracted
1264                                  from the received udp datagram.
1265 
1266   @retval TRUE     The udp datagram matches the receiving requirements of the Instance.
1267   @retval FALSE    The udp datagram does not matche the receiving requirements of the Instance.
1268 
1269 **/
1270 BOOLEAN
Udp6MatchDgram(IN UDP6_INSTANCE_DATA * Instance,IN EFI_UDP6_SESSION_DATA * Udp6Session)1271 Udp6MatchDgram (
1272   IN UDP6_INSTANCE_DATA     *Instance,
1273   IN EFI_UDP6_SESSION_DATA  *Udp6Session
1274   )
1275 {
1276   EFI_UDP6_CONFIG_DATA  *ConfigData;
1277   EFI_IPv6_ADDRESS      Destination;
1278 
1279   ConfigData = &Instance->ConfigData;
1280 
1281   if (ConfigData->AcceptPromiscuous) {
1282     //
1283     // Always matches if this instance is in the promiscuous state.
1284     //
1285     return TRUE;
1286   }
1287 
1288   if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) ||
1289       ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort))
1290       ) {
1291     //
1292     // The local port or the remote port doesn't match.
1293     //
1294     return FALSE;
1295   }
1296 
1297   if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) &&
1298       !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress)
1299       ) {
1300     //
1301     // This datagram doesn't come from the instance's specified sender.
1302     //
1303     return FALSE;
1304   }
1305 
1306   if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) ||
1307       EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress)
1308       ) {
1309     //
1310     // The instance is configured to receive datagrams destinated to any station IP or
1311     // the destination address of this datagram matches the configured station IP.
1312     //
1313     return TRUE;
1314   }
1315 
1316   IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress);
1317 
1318   if (IP6_IS_MULTICAST (&Destination) &&
1319       (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination))
1320       ) {
1321     //
1322     // It's a multicast packet and the multicast address is accepted by this instance.
1323     //
1324     return TRUE;
1325   }
1326 
1327   return FALSE;
1328 }
1329 
1330 
1331 /**
1332   This function removes the Wrap specified by Context and release relevant resources.
1333 
1334   @param[in]  Event                  The Event this notify function registered to.
1335   @param[in]  Context                Pointer to the context data.
1336 
1337 **/
1338 VOID
1339 EFIAPI
Udp6RecycleRxDataWrap(IN EFI_EVENT Event,IN VOID * Context)1340 Udp6RecycleRxDataWrap (
1341   IN EFI_EVENT  Event,
1342   IN VOID       *Context
1343   )
1344 {
1345   UDP6_RXDATA_WRAP  *Wrap;
1346 
1347   Wrap = (UDP6_RXDATA_WRAP *) Context;
1348 
1349   //
1350   // Remove the Wrap from the list it belongs to.
1351   //
1352   RemoveEntryList (&Wrap->Link);
1353 
1354   //
1355   // Free the Packet associated with this Wrap.
1356   //
1357   NetbufFree (Wrap->Packet);
1358 
1359   //
1360   // Close the event.
1361   //
1362   gBS->CloseEvent (Wrap->RxData.RecycleSignal);
1363 
1364   FreePool (Wrap);
1365 }
1366 
1367 
1368 /**
1369   This function wraps the Packet into RxData.
1370 
1371   @param[in]  Instance           Pointer to the instance context data.
1372   @param[in]  Packet             Pointer to the buffer containing the received
1373                                  datagram.
1374   @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this
1375                                  datagram.
1376 
1377   @return Pointer to the structure wrapping the RxData and the Packet.
1378 
1379 **/
1380 UDP6_RXDATA_WRAP *
Udp6WrapRxData(IN UDP6_INSTANCE_DATA * Instance,IN NET_BUF * Packet,IN EFI_UDP6_RECEIVE_DATA * RxData)1381 Udp6WrapRxData (
1382   IN UDP6_INSTANCE_DATA     *Instance,
1383   IN NET_BUF                *Packet,
1384   IN EFI_UDP6_RECEIVE_DATA  *RxData
1385   )
1386 {
1387   EFI_STATUS            Status;
1388   UDP6_RXDATA_WRAP      *Wrap;
1389 
1390   //
1391   // Allocate buffer for the Wrap.
1392   //
1393   Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) +
1394          (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA));
1395   if (Wrap == NULL) {
1396     return NULL;
1397   }
1398 
1399   InitializeListHead (&Wrap->Link);
1400 
1401   CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA));
1402   //
1403   // Create the Recycle event.
1404   //
1405   Status = gBS->CreateEvent (
1406                   EVT_NOTIFY_SIGNAL,
1407                   TPL_NOTIFY,
1408                   Udp6RecycleRxDataWrap,
1409                   Wrap,
1410                   &Wrap->RxData.RecycleSignal
1411                   );
1412   if (EFI_ERROR (Status)) {
1413     FreePool (Wrap);
1414     return NULL;
1415   }
1416 
1417   Wrap->Packet      = Packet;
1418   Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;
1419 
1420   return Wrap;
1421 }
1422 
1423 
1424 /**
1425   This function enqueues the received datagram into the instances' receiving queues.
1426 
1427   @param[in]  Udp6Service        Pointer to the udp service context data.
1428   @param[in]  Packet             Pointer to the buffer containing the received
1429                                  datagram.
1430   @param[in]  RxData             Pointer to the EFI_UDP6_RECEIVE_DATA of this
1431                                  datagram.
1432 
1433   @return The times this datagram is enqueued.
1434 
1435 **/
1436 UINTN
Udp6EnqueueDgram(IN UDP6_SERVICE_DATA * Udp6Service,IN NET_BUF * Packet,IN EFI_UDP6_RECEIVE_DATA * RxData)1437 Udp6EnqueueDgram (
1438   IN UDP6_SERVICE_DATA      *Udp6Service,
1439   IN NET_BUF                *Packet,
1440   IN EFI_UDP6_RECEIVE_DATA  *RxData
1441   )
1442 {
1443   LIST_ENTRY          *Entry;
1444   UDP6_INSTANCE_DATA  *Instance;
1445   UDP6_RXDATA_WRAP    *Wrap;
1446   UINTN               Enqueued;
1447 
1448   Enqueued = 0;
1449 
1450   NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
1451     //
1452     // Iterate the instances.
1453     //
1454     Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
1455 
1456     if (!Instance->Configured) {
1457       continue;
1458     }
1459 
1460     if (Udp6MatchDgram (Instance, &RxData->UdpSession)) {
1461       //
1462       // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.
1463       //
1464       Wrap = Udp6WrapRxData (Instance, Packet, RxData);
1465       if (Wrap == NULL) {
1466         continue;
1467       }
1468 
1469       NET_GET_REF (Packet);
1470 
1471       InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);
1472 
1473       Enqueued++;
1474     }
1475   }
1476 
1477   return Enqueued;
1478 }
1479 
1480 
1481 /**
1482   This function delivers the received datagrams to the specified instance.
1483 
1484   @param[in]  Instance               Pointer to the instance context data.
1485 
1486 **/
1487 VOID
Udp6InstanceDeliverDgram(IN UDP6_INSTANCE_DATA * Instance)1488 Udp6InstanceDeliverDgram (
1489   IN UDP6_INSTANCE_DATA  *Instance
1490   )
1491 {
1492   UDP6_RXDATA_WRAP           *Wrap;
1493   EFI_UDP6_COMPLETION_TOKEN  *Token;
1494   NET_BUF                    *Dup;
1495   EFI_UDP6_RECEIVE_DATA      *RxData;
1496   EFI_TPL                    OldTpl;
1497 
1498   if (!IsListEmpty (&Instance->RcvdDgramQue) &&
1499       !NetMapIsEmpty (&Instance->RxTokens)
1500       ) {
1501 
1502     Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);
1503 
1504     if (NET_BUF_SHARED (Wrap->Packet)) {
1505       //
1506       // Duplicate the Packet if it is shared between instances.
1507       //
1508       Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);
1509       if (Dup == NULL) {
1510         return;
1511       }
1512 
1513       NetbufFree (Wrap->Packet);
1514 
1515       Wrap->Packet = Dup;
1516     }
1517 
1518     NetListRemoveHead (&Instance->RcvdDgramQue);
1519 
1520     Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
1521 
1522     //
1523     // Build the FragmentTable and set the FragmentCount in RxData.
1524     //
1525     RxData                = &Wrap->RxData;
1526     RxData->FragmentCount = Wrap->Packet->BlockOpNum;
1527 
1528     NetbufBuildExt (
1529       Wrap->Packet,
1530       (NET_FRAGMENT *) RxData->FragmentTable,
1531       &RxData->FragmentCount
1532       );
1533 
1534     Token->Status        = EFI_SUCCESS;
1535     Token->Packet.RxData = &Wrap->RxData;
1536 
1537     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
1538     InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);
1539     gBS->RestoreTPL (OldTpl);
1540 
1541     gBS->SignalEvent (Token->Event);
1542   }
1543 }
1544 
1545 
1546 /**
1547   This function delivers the datagrams enqueued in the instances.
1548 
1549   @param[in]  Udp6Service            Pointer to the udp service context data.
1550 
1551 **/
1552 VOID
Udp6DeliverDgram(IN UDP6_SERVICE_DATA * Udp6Service)1553 Udp6DeliverDgram (
1554   IN UDP6_SERVICE_DATA  *Udp6Service
1555   )
1556 {
1557   LIST_ENTRY          *Entry;
1558   UDP6_INSTANCE_DATA  *Instance;
1559 
1560   NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
1561     //
1562     // Iterate the instances.
1563     //
1564     Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
1565 
1566     if (!Instance->Configured) {
1567       continue;
1568     }
1569 
1570     //
1571     // Deliver the datagrams of this instance.
1572     //
1573     Udp6InstanceDeliverDgram (Instance);
1574   }
1575 }
1576 
1577 
1578 /**
1579   This function demultiplexes the received udp datagram to the appropriate instances.
1580 
1581   @param[in]  Udp6Service        Pointer to the udp service context data.
1582   @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted from
1583                                  the received datagram.
1584   @param[in]  Packet             Pointer to the buffer containing the received udp
1585                                  datagram.
1586 
1587 **/
1588 VOID
Udp6Demultiplex(IN UDP6_SERVICE_DATA * Udp6Service,IN EFI_NET_SESSION_DATA * NetSession,IN NET_BUF * Packet)1589 Udp6Demultiplex (
1590   IN UDP6_SERVICE_DATA     *Udp6Service,
1591   IN EFI_NET_SESSION_DATA  *NetSession,
1592   IN NET_BUF               *Packet
1593   )
1594 {
1595   EFI_UDP_HEADER        *Udp6Header;
1596   UINT16                 HeadSum;
1597   EFI_UDP6_RECEIVE_DATA  RxData;
1598   EFI_UDP6_SESSION_DATA  *Udp6Session;
1599   UINTN                  Enqueued;
1600 
1601   if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
1602     NetbufFree (Packet);
1603     return;
1604   }
1605 
1606   //
1607   // Get the datagram header from the packet buffer.
1608   //
1609   Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
1610   ASSERT (Udp6Header != NULL);
1611 
1612   if (Udp6Header->Checksum != 0) {
1613     //
1614     // check the checksum.
1615     //
1616     HeadSum = NetIp6PseudoHeadChecksum (
1617                 &NetSession->Source.v6,
1618                 &NetSession->Dest.v6,
1619                 EFI_IP_PROTO_UDP,
1620                 0
1621                 );
1622 
1623     if (Udp6Checksum (Packet, HeadSum) != 0) {
1624       //
1625       // Wrong checksum.
1626       //
1627       NetbufFree (Packet);
1628       return;
1629     }
1630   }
1631 
1632   Udp6Session                  = &RxData.UdpSession;
1633   Udp6Session->SourcePort      = NTOHS (Udp6Header->SrcPort);
1634   Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort);
1635 
1636   IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source);
1637   IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest);
1638 
1639   //
1640   // Trim the UDP header.
1641   //
1642   NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE);
1643 
1644   RxData.DataLength = (UINT32) Packet->TotalSize;
1645 
1646   //
1647   // Try to enqueue this datagram into the instances.
1648   //
1649   Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData);
1650 
1651   if (Enqueued == 0) {
1652     //
1653     // Send the port unreachable ICMP packet before we free this NET_BUF
1654     //
1655     Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header);
1656   }
1657 
1658   //
1659   // Try to free the packet before deliver it.
1660   //
1661   NetbufFree (Packet);
1662 
1663   if (Enqueued > 0) {
1664     //
1665     // Deliver the datagram.
1666     //
1667     Udp6DeliverDgram (Udp6Service);
1668   }
1669 }
1670 
1671 
1672 /**
1673   This function builds and sends out a icmp port unreachable message.
1674 
1675   @param[in]  IpIo               Pointer to the IP_IO instance.
1676   @param[in]  NetSession         Pointer to the EFI_NET_SESSION_DATA of the packet
1677                                  causes this icmp error message.
1678   @param[in]  Udp6Header         Pointer to the udp header of the datagram causes
1679                                  this icmp error message.
1680 
1681 **/
1682 VOID
Udp6SendPortUnreach(IN IP_IO * IpIo,IN EFI_NET_SESSION_DATA * NetSession,IN VOID * Udp6Header)1683 Udp6SendPortUnreach (
1684   IN IP_IO                 *IpIo,
1685   IN EFI_NET_SESSION_DATA  *NetSession,
1686   IN VOID                  *Udp6Header
1687   )
1688 {
1689   NET_BUF              *Packet;
1690   UINT32               Len;
1691   IP6_ICMP_ERROR_HEAD  *IcmpErrHdr;
1692   UINT8                *Ptr;
1693   IP_IO_OVERRIDE       Override;
1694   IP_IO_IP_INFO        *IpSender;
1695   EFI_IP6_MODE_DATA    *Ip6ModeData;
1696   EFI_STATUS           Status;
1697   EFI_IP6_PROTOCOL     *Ip6Protocol;
1698 
1699   Ip6ModeData = NULL;
1700 
1701   //
1702   // An ICMPv6 error message MUST NOT be originated as A packet destined to
1703   // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address
1704   //
1705   if (NetSession->IpVersion == IP_VERSION_6) {
1706     if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) ||
1707       IP6_IS_MULTICAST (&NetSession->Dest.v6)
1708       ) {
1709       goto EXIT;
1710     }
1711   }
1712 
1713 
1714   IpSender    = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);
1715 
1716   //
1717   // Get the Ipv6 Mode Data.
1718   //
1719   Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));
1720   ASSERT (Ip6ModeData != NULL);
1721 
1722   //
1723   // If not finding the related IpSender use the default IpIo to send out
1724   // the port unreachable ICMP message.
1725   //
1726   if (IpSender == NULL) {
1727     Ip6Protocol = IpIo->Ip.Ip6;
1728   } else {
1729     Ip6Protocol = IpSender->Ip.Ip6;
1730   }
1731 
1732   Status = Ip6Protocol->GetModeData (
1733                           Ip6Protocol,
1734                           Ip6ModeData,
1735                           NULL,
1736                           NULL
1737                           );
1738 
1739   if (EFI_ERROR (Status)) {
1740     goto EXIT;
1741   }
1742   //
1743   // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header.
1744   //
1745   Len = NetSession->IpHdrLen +
1746         NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) +
1747         sizeof (IP6_ICMP_ERROR_HEAD);
1748 
1749   //
1750   // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU.
1751   //
1752   if (Ip6ModeData->MaxPacketSize < Len) {
1753     Len = Ip6ModeData->MaxPacketSize;
1754   }
1755 
1756   //
1757   // Allocate buffer for the icmp error message.
1758   //
1759   Packet = NetbufAlloc (Len);
1760   if (Packet == NULL) {
1761     goto EXIT;
1762   }
1763 
1764   //
1765   // Allocate space for the IP6_ICMP_ERROR_HEAD.
1766   //
1767   IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);
1768   ASSERT (IcmpErrHdr != NULL);
1769 
1770   //
1771   // Set the required fields for the icmp port unreachable message.
1772   //
1773   IcmpErrHdr->Head.Type     = ICMP_V6_DEST_UNREACHABLE;
1774   IcmpErrHdr->Head.Code     = ICMP_V6_PORT_UNREACHABLE;
1775   IcmpErrHdr->Head.Checksum = 0;
1776   IcmpErrHdr->Fourth        = 0;
1777 
1778   //
1779   // Copy as much of invoking Packet as possible without the ICMPv6 packet
1780   // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains
1781   // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD
1782   // for pointer movement that fact should be considered.
1783   //
1784   Ptr = (VOID *) &IcmpErrHdr->Head;
1785   Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER));
1786   CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen);
1787   CopyMem (
1788     Ptr + NetSession->IpHdrLen,
1789     Udp6Header,
1790     Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER)
1791     );
1792 
1793   //
1794   // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header.
1795   //
1796   IcmpErrHdr->Head.Checksum = 0;
1797 
1798   //
1799   // Fill the override data.
1800   //
1801   Override.Ip6OverrideData.FlowLabel     = 0;
1802   Override.Ip6OverrideData.HopLimit      = 255;
1803   Override.Ip6OverrideData.Protocol      = IP6_ICMP;
1804 
1805   //
1806   // Send out this icmp packet.
1807   //
1808   IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);
1809 
1810   NetbufFree (Packet);
1811 
1812 EXIT:
1813   if (Ip6ModeData != NULL) {
1814     FreePool (Ip6ModeData);
1815   }
1816 }
1817 
1818 
1819 /**
1820   This function handles the received Icmp Error message and de-multiplexes it to the
1821   instance.
1822 
1823   @param[in]       Udp6Service        Pointer to the udp service context data.
1824   @param[in]       IcmpError          The icmp error code.
1825   @param[in]       NetSession         Pointer to the EFI_NET_SESSION_DATA abstracted
1826                                       from the received Icmp Error packet.
1827   @param[in, out]  Packet             Pointer to the Icmp Error packet.
1828 
1829 **/
1830 VOID
Udp6IcmpHandler(IN UDP6_SERVICE_DATA * Udp6Service,IN UINT8 IcmpError,IN EFI_NET_SESSION_DATA * NetSession,IN OUT NET_BUF * Packet)1831 Udp6IcmpHandler (
1832   IN UDP6_SERVICE_DATA     *Udp6Service,
1833   IN UINT8                 IcmpError,
1834   IN EFI_NET_SESSION_DATA  *NetSession,
1835   IN OUT NET_BUF           *Packet
1836   )
1837 {
1838   EFI_UDP_HEADER         *Udp6Header;
1839   EFI_UDP6_SESSION_DATA  Udp6Session;
1840   LIST_ENTRY             *Entry;
1841   UDP6_INSTANCE_DATA     *Instance;
1842 
1843   if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
1844     NetbufFree (Packet);
1845     return;
1846   }
1847 
1848   Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
1849   ASSERT (Udp6Header != NULL);
1850 
1851   IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source);
1852   IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest);
1853 
1854   Udp6Session.SourcePort      = NTOHS (Udp6Header->DstPort);
1855   Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort);
1856 
1857   NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
1858     //
1859     // Iterate all the instances.
1860     //
1861     Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
1862 
1863     if (!Instance->Configured) {
1864       continue;
1865     }
1866 
1867     if (Udp6MatchDgram (Instance, &Udp6Session)) {
1868       //
1869       // Translate the Icmp Error code according to the udp spec.
1870       //
1871       Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL);
1872 
1873       if (IcmpError > ICMP_ERR_UNREACH_PORT) {
1874         Instance->IcmpError = EFI_ICMP_ERROR;
1875       }
1876 
1877       //
1878       // Notify the instance with the received Icmp Error.
1879       //
1880       Udp6ReportIcmpError (Instance);
1881 
1882       break;
1883     }
1884   }
1885 
1886   NetbufFree (Packet);
1887 }
1888 
1889 
1890 /**
1891   This function reports the received ICMP error.
1892 
1893   @param[in]  Instance          Pointer to the udp instance context data.
1894 
1895 **/
1896 VOID
Udp6ReportIcmpError(IN UDP6_INSTANCE_DATA * Instance)1897 Udp6ReportIcmpError (
1898   IN UDP6_INSTANCE_DATA  *Instance
1899   )
1900 {
1901   EFI_UDP6_COMPLETION_TOKEN  *Token;
1902 
1903   if (NetMapIsEmpty (&Instance->RxTokens)) {
1904     //
1905     // There are no receive tokens to deliver the ICMP error.
1906     //
1907     return;
1908   }
1909 
1910   if (EFI_ERROR (Instance->IcmpError)) {
1911     //
1912     // Try to get a RxToken from the RxTokens map.
1913     //
1914     Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
1915 
1916     if (Token != NULL) {
1917       //
1918       // Report the error through the Token.
1919       //
1920       Token->Status = Instance->IcmpError;
1921       gBS->SignalEvent (Token->Event);
1922 
1923       //
1924       // Clear the IcmpError.
1925       //
1926       Instance->IcmpError = EFI_SUCCESS;
1927     }
1928   }
1929 }
1930 
1931 
1932 /**
1933   This function is a dummy ext-free function for the NET_BUF created for the output
1934   udp datagram.
1935 
1936   @param[in]  Context                Pointer to the context data.
1937 
1938 **/
1939 VOID
1940 EFIAPI
Udp6NetVectorExtFree(IN VOID * Context)1941 Udp6NetVectorExtFree (
1942   IN VOID  *Context
1943   )
1944 {
1945 }
1946 
1947 /**
1948   Find the key in the netmap.
1949 
1950   @param[in]  Map                    The netmap to search within.
1951   @param[in]  Key                    The key to search.
1952 
1953   @return The point to the item contains the Key, or NULL, if Key isn't in the map.
1954 
1955 **/
1956 NET_MAP_ITEM *
Udp6MapMultiCastAddr(IN NET_MAP * Map,IN VOID * Key)1957 Udp6MapMultiCastAddr (
1958   IN  NET_MAP               *Map,
1959   IN  VOID                  *Key
1960   )
1961 {
1962   LIST_ENTRY        *Entry;
1963   NET_MAP_ITEM      *Item;
1964   EFI_IPv6_ADDRESS  *Addr;
1965 
1966   ASSERT (Map != NULL);
1967   NET_LIST_FOR_EACH (Entry, &Map->Used) {
1968     Item  = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
1969     Addr  = (EFI_IPv6_ADDRESS *) Item->Key;
1970     if (EFI_IP6_EQUAL (Addr, Key)) {
1971       return Item;
1972     }
1973   }
1974   return NULL;
1975 }
1976 
1977