1 /** @file
2   The implementation of IPsec.
3 
4   Copyright (c) 2009 - 2011, 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 "IpSecImpl.h"
17 #include "IkeService.h"
18 #include "IpSecDebug.h"
19 #include "IpSecCryptIo.h"
20 #include "IpSecConfigImpl.h"
21 
22 /**
23   Check if the specified Address is the Valid Address Range.
24 
25   This function checks if the bytes after prefixed length are all Zero in this
26   Address. This Address is supposed to point to a range address. That means it
27   should gives the correct prefixed address and the bytes outside the prefixed are
28   zero.
29 
30   @param[in]  IpVersion         The IP version.
31   @param[in]  Address           Points to EFI_IP_ADDRESS to be checked.
32   @param[in]  PrefixLength      The PrefixeLength of this address.
33 
34   @retval     TRUE      The address is a vaild address range.
35   @retval     FALSE     The address is not a vaild address range.
36 
37 **/
38 BOOLEAN
IpSecValidAddressRange(IN UINT8 IpVersion,IN EFI_IP_ADDRESS * Address,IN UINT8 PrefixLength)39 IpSecValidAddressRange (
40   IN UINT8                     IpVersion,
41   IN EFI_IP_ADDRESS            *Address,
42   IN UINT8                     PrefixLength
43   )
44 {
45   UINT8           Div;
46   UINT8           Mod;
47   UINT8           Mask;
48   UINT8           AddrLen;
49   UINT8           *Addr;
50   EFI_IP_ADDRESS  ZeroAddr;
51 
52   if (PrefixLength == 0) {
53     return TRUE;
54   }
55 
56   AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);
57 
58   if (AddrLen <= PrefixLength) {
59     return FALSE;
60   }
61 
62   Div   = (UINT8) (PrefixLength / 8);
63   Mod   = (UINT8) (PrefixLength % 8);
64   Addr  = (UINT8 *) Address;
65   ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));
66 
67   //
68   // Check whether the mod part of host scope is zero or not.
69   //
70   if (Mod > 0) {
71     Mask = (UINT8) (0xFF << (8 - Mod));
72 
73     if ((Addr[Div] | Mask) != Mask) {
74       return FALSE;
75     }
76 
77     Div++;
78   }
79   //
80   // Check whether the div part of host scope is zero or not.
81   //
82   if (CompareMem (
83         &Addr[Div],
84         &ZeroAddr,
85         sizeof (EFI_IP_ADDRESS) - Div
86         ) != 0) {
87     return FALSE;
88   }
89 
90   return TRUE;
91 }
92 
93 /**
94   Extrct the Address Range from a Address.
95 
96   This function keep the prefix address and zero other part address.
97 
98   @param[in]  Address           Point to a specified address.
99   @param[in]  PrefixLength      The prefix length.
100   @param[out] Range             Contain the return Address Range.
101 
102 **/
103 VOID
IpSecExtractAddressRange(IN EFI_IP_ADDRESS * Address,IN UINT8 PrefixLength,OUT EFI_IP_ADDRESS * Range)104 IpSecExtractAddressRange (
105   IN EFI_IP_ADDRESS            *Address,
106   IN UINT8                     PrefixLength,
107   OUT EFI_IP_ADDRESS           *Range
108   )
109 {
110   UINT8 Div;
111   UINT8 Mod;
112   UINT8 Mask;
113   UINT8 *Addr;
114 
115   if (PrefixLength == 0) {
116     return ;
117   }
118 
119   Div   = (UINT8) (PrefixLength / 8);
120   Mod   = (UINT8) (PrefixLength % 8);
121   Addr  = (UINT8 *) Range;
122 
123   CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));
124 
125   //
126   // Zero the mod part of host scope.
127   //
128   if (Mod > 0) {
129     Mask      = (UINT8) (0xFF << (8 - Mod));
130     Addr[Div] = (UINT8) (Addr[Div] & Mask);
131     Div++;
132   }
133   //
134   // Zero the div part of host scope.
135   //
136   ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);
137 
138 }
139 
140 /**
141   Checks if the IP Address in the address range of AddressInfos specified.
142 
143   @param[in]  IpVersion         The IP version.
144   @param[in]  IpAddr            Point to EFI_IP_ADDRESS to be check.
145   @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check
146                                 the IP Address is matched.
147   @param[in]  AddressCount      The total numbers of the AddressInfo.
148 
149   @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.
150   @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.
151 
152 **/
153 BOOLEAN
IpSecMatchIpAddress(IN UINT8 IpVersion,IN EFI_IP_ADDRESS * IpAddr,IN EFI_IP_ADDRESS_INFO * AddressInfo,IN UINT32 AddressCount)154 IpSecMatchIpAddress (
155   IN UINT8                     IpVersion,
156   IN EFI_IP_ADDRESS            *IpAddr,
157   IN EFI_IP_ADDRESS_INFO       *AddressInfo,
158   IN UINT32                    AddressCount
159   )
160 {
161   EFI_IP_ADDRESS  Range;
162   UINT32          Index;
163   BOOLEAN         IsMatch;
164 
165   IsMatch = FALSE;
166 
167   for (Index = 0; Index < AddressCount; Index++) {
168     //
169     // Check whether the target address is in the address range
170     // if it's a valid range of address.
171     //
172     if (IpSecValidAddressRange (
173           IpVersion,
174           &AddressInfo[Index].Address,
175           AddressInfo[Index].PrefixLength
176           )) {
177       //
178       // Get the range of the target address belongs to.
179       //
180       ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));
181       IpSecExtractAddressRange (
182         IpAddr,
183         AddressInfo[Index].PrefixLength,
184         &Range
185         );
186 
187       if (CompareMem (
188             &Range,
189             &AddressInfo[Index].Address,
190             sizeof (EFI_IP_ADDRESS)
191             ) == 0) {
192         //
193         // The target address is in the address range.
194         //
195         IsMatch = TRUE;
196         break;
197       }
198     }
199 
200     if (CompareMem (
201           IpAddr,
202           &AddressInfo[Index].Address,
203           sizeof (EFI_IP_ADDRESS)
204           ) == 0) {
205       //
206       // The target address is exact same as the address.
207       //
208       IsMatch = TRUE;
209       break;
210     }
211   }
212   return IsMatch;
213 }
214 
215 /**
216   Check if the specified Protocol and Prot is supported by the specified SPD Entry.
217 
218   This function is the subfunction of IPsecLookUpSpdEntry() that is used to
219   check if the sent/received IKE packet has the related SPD entry support.
220 
221   @param[in]  Protocol          The Protocol to be checked.
222   @param[in]  IpPayload         Point to IP Payload to be check.
223   @param[in]  SpdProtocol       The Protocol supported by SPD.
224   @param[in]  SpdLocalPort      The Local Port in SPD.
225   @param[in]  SpdRemotePort     The Remote Port in SPD.
226   @param[in]  IsOutbound        Flag to indicate the is for IKE Packet sending or recieving.
227 
228   @retval     TRUE      The Protocol and Port are supported by the SPD Entry.
229   @retval     FALSE     The Protocol and Port are not supported by the SPD Entry.
230 
231 **/
232 BOOLEAN
IpSecMatchNextLayerProtocol(IN UINT8 Protocol,IN UINT8 * IpPayload,IN UINT16 SpdProtocol,IN UINT16 SpdLocalPort,IN UINT16 SpdRemotePort,IN BOOLEAN IsOutbound)233 IpSecMatchNextLayerProtocol (
234   IN UINT8                     Protocol,
235   IN UINT8                     *IpPayload,
236   IN UINT16                    SpdProtocol,
237   IN UINT16                    SpdLocalPort,
238   IN UINT16                    SpdRemotePort,
239   IN BOOLEAN                   IsOutbound
240   )
241 {
242   BOOLEAN IsMatch;
243 
244   if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {
245     return TRUE;
246   }
247 
248   IsMatch = FALSE;
249 
250   if (SpdProtocol == Protocol) {
251     switch (Protocol) {
252     case EFI_IP_PROTO_UDP:
253     case EFI_IP_PROTO_TCP:
254       //
255       // For udp and tcp, (0, 0) means no need to check local and remote
256       // port. The payload is passed from upper level, which means it should
257       // be in network order.
258       //
259       IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
260       IsMatch = (BOOLEAN) (IsMatch ||
261                            (IsOutbound &&
262                            (BOOLEAN)(
263                               NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&
264                               NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort
265                               )
266                             ));
267 
268       IsMatch = (BOOLEAN) (IsMatch ||
269                            (!IsOutbound &&
270                            (BOOLEAN)(
271                               NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&
272                               NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort
273                               )
274                            ));
275       break;
276 
277     case EFI_IP_PROTO_ICMP:
278       //
279       // For icmpv4, type code is replaced with local port and remote port,
280       // and (0, 0) means no need to check.
281       //
282       IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
283       IsMatch = (BOOLEAN) (IsMatch ||
284                            (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
285                                       ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
286                                       )
287                            );
288       break;
289 
290     case IP6_ICMP:
291       //
292       // For icmpv6, type code is replaced with local port and remote port,
293       // and (0, 0) means no need to check.
294       //
295       IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
296 
297       IsMatch = (BOOLEAN) (IsMatch ||
298                            (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
299                                       ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
300                                       )
301                           );
302       break;
303 
304     default:
305       IsMatch = TRUE;
306       break;
307     }
308   }
309 
310   return IsMatch;
311 }
312 
313 /**
314   Find the SAD through a specified SPD's SAD list.
315 
316   @param[in]  SadList           SAD list related to a specified SPD entry.
317   @param[in]  DestAddress       The destination address used to find the SAD entry.
318   @param[in]  IpVersion         The IP version. Ip4 or Ip6.
319 
320   @return  The pointer to a certain SAD entry.
321 
322 **/
323 IPSEC_SAD_ENTRY *
IpSecLookupSadBySpd(IN LIST_ENTRY * SadList,IN EFI_IP_ADDRESS * DestAddress,IN UINT8 IpVersion)324 IpSecLookupSadBySpd (
325   IN LIST_ENTRY                 *SadList,
326   IN EFI_IP_ADDRESS             *DestAddress,
327   IN UINT8                      IpVersion
328   )
329 {
330   LIST_ENTRY      *Entry;
331   IPSEC_SAD_ENTRY *SadEntry;
332 
333   NET_LIST_FOR_EACH (Entry, SadList) {
334 
335     SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);
336     //
337     // Find the right SAD entry which contains the appointed dest address.
338     //
339     if (IpSecMatchIpAddress (
340           IpVersion,
341           DestAddress,
342           SadEntry->Data->SpdSelector->RemoteAddress,
343           SadEntry->Data->SpdSelector->RemoteAddressCount
344           )){
345       return SadEntry;
346     }
347   }
348 
349   return NULL;
350 }
351 
352 /**
353   Find the SAD through whole SAD list.
354 
355   @param[in]  Spi               The SPI used to search the SAD entry.
356   @param[in]  DestAddress       The destination used to search the SAD entry.
357   @param[in]  IpVersion         The IP version. Ip4 or Ip6.
358 
359   @return  the pointer to a certain SAD entry.
360 
361 **/
362 IPSEC_SAD_ENTRY *
IpSecLookupSadBySpi(IN UINT32 Spi,IN EFI_IP_ADDRESS * DestAddress,IN UINT8 IpVersion)363 IpSecLookupSadBySpi (
364   IN UINT32                   Spi,
365   IN EFI_IP_ADDRESS           *DestAddress,
366   IN UINT8                    IpVersion
367   )
368 {
369   LIST_ENTRY      *Entry;
370   LIST_ENTRY      *SadList;
371   IPSEC_SAD_ENTRY *SadEntry;
372 
373   SadList = &mConfigData[IPsecConfigDataTypeSad];
374 
375   NET_LIST_FOR_EACH (Entry, SadList) {
376 
377     SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
378 
379     //
380     // Find the right SAD entry which contain the appointed spi and dest addr.
381     //
382     if (SadEntry->Id->Spi == Spi) {
383       if (SadEntry->Data->Mode == EfiIPsecTunnel) {
384         if (CompareMem (
385               &DestAddress,
386               &SadEntry->Data->TunnelDestAddress,
387               sizeof (EFI_IP_ADDRESS)
388               )) {
389           return SadEntry;
390         }
391       } else {
392         if (SadEntry->Data->SpdSelector != NULL &&
393             IpSecMatchIpAddress (
394               IpVersion,
395               DestAddress,
396               SadEntry->Data->SpdSelector->RemoteAddress,
397               SadEntry->Data->SpdSelector->RemoteAddressCount
398               )
399             ) {
400           return SadEntry;
401         }
402       }
403     }
404   }
405   return NULL;
406 }
407 
408 /**
409   Look up if there is existing SAD entry for specified IP packet sending.
410 
411   This function is called by the IPsecProcess when there is some IP packet needed to
412   send out. This function checks if there is an existing SAD entry that can be serviced
413   to this IP packet sending. If no existing SAD entry could be used, this
414   function will invoke an IPsec Key Exchange Negotiation.
415 
416   @param[in]  Private           Points to private data.
417   @param[in]  NicHandle         Points to a NIC handle.
418   @param[in]  IpVersion         The version of IP.
419   @param[in]  IpHead            The IP Header of packet to be sent out.
420   @param[in]  IpPayload         The IP Payload to be sent out.
421   @param[in]  OldLastHead       The Last protocol of the IP packet.
422   @param[in]  SpdEntry          Points to a related SPD entry.
423   @param[out] SadEntry          Contains the Point of a related SAD entry.
424 
425   @retval EFI_DEVICE_ERROR  One of following conditions is TRUE:
426                             - If don't find related UDP service.
427                             - Sequence Number is used up.
428                             - Extension Sequence Number is used up.
429   @retval EFI_NOT_READY     No existing SAD entry could be used.
430   @retval EFI_SUCCESS       Find the related SAD entry.
431 
432 **/
433 EFI_STATUS
IpSecLookupSadEntry(IN IPSEC_PRIVATE_DATA * Private,IN EFI_HANDLE NicHandle,IN UINT8 IpVersion,IN VOID * IpHead,IN UINT8 * IpPayload,IN UINT8 OldLastHead,IN IPSEC_SPD_ENTRY * SpdEntry,OUT IPSEC_SAD_ENTRY ** SadEntry)434 IpSecLookupSadEntry (
435   IN IPSEC_PRIVATE_DATA      *Private,
436   IN EFI_HANDLE              NicHandle,
437   IN UINT8                   IpVersion,
438   IN VOID                    *IpHead,
439   IN UINT8                   *IpPayload,
440   IN UINT8                   OldLastHead,
441   IN IPSEC_SPD_ENTRY         *SpdEntry,
442   OUT IPSEC_SAD_ENTRY        **SadEntry
443   )
444 {
445   IKE_UDP_SERVICE *UdpService;
446   IPSEC_SAD_ENTRY *Entry;
447   IPSEC_SAD_DATA  *Data;
448   EFI_IP_ADDRESS  DestIp;
449   UINT32          SeqNum32;
450 
451   *SadEntry   = NULL;
452   UdpService  = IkeLookupUdp (Private, NicHandle, IpVersion);
453 
454   if (UdpService == NULL) {
455     return EFI_DEVICE_ERROR;
456   }
457   //
458   // Parse the destination address from ip header.
459   //
460   ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
461   if (IpVersion == IP_VERSION_4) {
462     CopyMem (
463       &DestIp,
464       &((IP4_HEAD *) IpHead)->Dst,
465       sizeof (IP4_ADDR)
466       );
467   } else {
468     CopyMem (
469       &DestIp,
470       &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
471       sizeof (EFI_IP_ADDRESS)
472       );
473   }
474 
475   //
476   // Find the SAD entry in the spd.sas list according to the dest address.
477   //
478   Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp, IpVersion);
479 
480   if (Entry == NULL) {
481     if (OldLastHead != IP6_ICMP ||
482         (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)
483         ) {
484       //
485       // Start ike negotiation process except the request packet of ping.
486       //
487       if (SpdEntry->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
488         IkeNegotiate (
489           UdpService,
490           SpdEntry,
491           &SpdEntry->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress
492           );
493       } else {
494         IkeNegotiate (
495           UdpService,
496           SpdEntry,
497           &DestIp
498         );
499       }
500 
501     }
502 
503     return EFI_NOT_READY;
504   }
505 
506   Data = Entry->Data;
507 
508   if (!Data->ManualSet) {
509     if (Data->ESNEnabled) {
510       //
511       // Validate the 64bit sn number if 64bit sn enabled.
512       //
513       if ((UINT64) (Data->SequenceNumber + 1) == 0) {
514         //
515         // TODO: Re-negotiate SA
516         //
517         return EFI_DEVICE_ERROR;
518       }
519     } else {
520       //
521       // Validate the 32bit sn number if 64bit sn disabled.
522       //
523       SeqNum32 = (UINT32) Data->SequenceNumber;
524       if ((UINT32) (SeqNum32 + 1) == 0) {
525         //
526         // TODO: Re-negotiate SA
527         //
528         return EFI_DEVICE_ERROR;
529       }
530     }
531   }
532 
533   *SadEntry = Entry;
534 
535   return EFI_SUCCESS;
536 }
537 
538 /**
539   Find a PAD entry according to a remote IP address.
540 
541   @param[in]  IpVersion         The version of IP.
542   @param[in]  IpAddr            Points to remote IP address.
543 
544   @return the pointer of related PAD entry.
545 
546 **/
547 IPSEC_PAD_ENTRY *
IpSecLookupPadEntry(IN UINT8 IpVersion,IN EFI_IP_ADDRESS * IpAddr)548 IpSecLookupPadEntry (
549   IN UINT8                   IpVersion,
550   IN EFI_IP_ADDRESS          *IpAddr
551   )
552 {
553   LIST_ENTRY          *PadList;
554   LIST_ENTRY          *Entry;
555   EFI_IP_ADDRESS_INFO *IpAddrInfo;
556   IPSEC_PAD_ENTRY     *PadEntry;
557 
558   PadList = &mConfigData[IPsecConfigDataTypePad];
559 
560   for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {
561 
562     PadEntry    = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
563     IpAddrInfo  = &PadEntry->Id->Id.IpAddress;
564     //
565     // Find the right pad entry which contain the appointed dest addr.
566     //
567     if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {
568       return PadEntry;
569     }
570   }
571 
572   return NULL;
573 }
574 
575 /**
576   Check if the specified IP packet can be serviced by this SPD entry.
577 
578   @param[in]  SpdEntry          Point to SPD entry.
579   @param[in]  IpVersion         Version of IP.
580   @param[in]  IpHead            Point to IP header.
581   @param[in]  IpPayload         Point to IP payload.
582   @param[in]  Protocol          The Last protocol of IP packet.
583   @param[in]  IsOutbound        Traffic direction.
584   @param[out] Action            The support action of SPD entry.
585 
586   @retval EFI_SUCCESS       Find the related SPD.
587   @retval EFI_NOT_FOUND     Not find the related SPD entry;
588 
589 **/
590 EFI_STATUS
IpSecLookupSpdEntry(IN IPSEC_SPD_ENTRY * SpdEntry,IN UINT8 IpVersion,IN VOID * IpHead,IN UINT8 * IpPayload,IN UINT8 Protocol,IN BOOLEAN IsOutbound,OUT EFI_IPSEC_ACTION * Action)591 IpSecLookupSpdEntry (
592   IN     IPSEC_SPD_ENTRY         *SpdEntry,
593   IN     UINT8                   IpVersion,
594   IN     VOID                    *IpHead,
595   IN     UINT8                   *IpPayload,
596   IN     UINT8                   Protocol,
597   IN     BOOLEAN                 IsOutbound,
598      OUT EFI_IPSEC_ACTION        *Action
599   )
600 {
601   EFI_IPSEC_SPD_SELECTOR  *SpdSel;
602   IP4_HEAD                *Ip4;
603   EFI_IP6_HEADER          *Ip6;
604   EFI_IP_ADDRESS          SrcAddr;
605   EFI_IP_ADDRESS          DstAddr;
606   BOOLEAN                 SpdMatch;
607 
608   ASSERT (SpdEntry != NULL);
609   SpdSel  = SpdEntry->Selector;
610   Ip4     = (IP4_HEAD *) IpHead;
611   Ip6     = (EFI_IP6_HEADER *) IpHead;
612 
613   ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));
614   ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));
615 
616   //
617   // Parse the source and destination address from ip header.
618   //
619   if (IpVersion == IP_VERSION_4) {
620     CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));
621     CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));
622   } else {
623     CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));
624     CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));
625   }
626   //
627   // Check the local and remote addresses for outbound traffic
628   //
629   SpdMatch = (BOOLEAN)(IsOutbound &&
630                        IpSecMatchIpAddress (
631                          IpVersion,
632                          &SrcAddr,
633                          SpdSel->LocalAddress,
634                          SpdSel->LocalAddressCount
635                          ) &&
636                        IpSecMatchIpAddress (
637                          IpVersion,
638                          &DstAddr,
639                          SpdSel->RemoteAddress,
640                          SpdSel->RemoteAddressCount
641                          )
642                        );
643 
644   //
645   // Check the local and remote addresses for inbound traffic
646   //
647   SpdMatch = (BOOLEAN) (SpdMatch ||
648                         (!IsOutbound &&
649                         IpSecMatchIpAddress (
650                           IpVersion,
651                           &DstAddr,
652                           SpdSel->LocalAddress,
653                           SpdSel->LocalAddressCount
654                           ) &&
655                         IpSecMatchIpAddress (
656                           IpVersion,
657                           &SrcAddr,
658                           SpdSel->RemoteAddress,
659                           SpdSel->RemoteAddressCount
660                           )
661                         ));
662 
663   //
664   // Check the next layer protocol and local and remote ports.
665   //
666   SpdMatch = (BOOLEAN) (SpdMatch &&
667                         IpSecMatchNextLayerProtocol (
668                           Protocol,
669                           IpPayload,
670                           SpdSel->NextLayerProtocol,
671                           SpdSel->LocalPort,
672                           SpdSel->RemotePort,
673                           IsOutbound
674                           )
675                         );
676 
677   if (SpdMatch) {
678     //
679     // Find the right SPD entry if match the 5 key elements.
680     //
681     *Action = SpdEntry->Data->Action;
682     return EFI_SUCCESS;
683   }
684 
685   return EFI_NOT_FOUND;
686 }
687 
688 /**
689   The call back function of NetbufFromExt.
690 
691   @param[in]  Arg            The argument passed from the caller.
692 
693 **/
694 VOID
695 EFIAPI
IpSecOnRecyclePacket(IN VOID * Arg)696 IpSecOnRecyclePacket (
697   IN VOID                            *Arg
698   )
699 {
700 }
701 
702 /**
703   This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP
704   is released.
705 
706   @param[in]  Event              The related event.
707   @param[in]  Context            The data passed by the caller.
708 
709 **/
710 VOID
711 EFIAPI
IpSecRecycleCallback(IN EFI_EVENT Event,IN VOID * Context)712 IpSecRecycleCallback (
713   IN EFI_EVENT                       Event,
714   IN VOID                            *Context
715   )
716 {
717   IPSEC_RECYCLE_CONTEXT *RecycleContext;
718 
719   RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;
720 
721   if (RecycleContext->FragmentTable != NULL) {
722     FreePool (RecycleContext->FragmentTable);
723   }
724 
725   if (RecycleContext->PayloadBuffer != NULL) {
726     FreePool (RecycleContext->PayloadBuffer);
727   }
728 
729   FreePool (RecycleContext);
730   gBS->CloseEvent (Event);
731 
732 }
733 
734 /**
735   Calculate the extension hader of IP. The return length only doesn't contain
736   the fixed IP header length.
737 
738   @param[in]  IpHead             Points to an IP head to be calculated.
739   @param[in]  LastHead           Points to the last header of the IP header.
740 
741   @return The length of the extension header.
742 
743 **/
744 UINT16
IpSecGetPlainExtHeadSize(IN VOID * IpHead,IN UINT8 * LastHead)745 IpSecGetPlainExtHeadSize (
746   IN VOID                             *IpHead,
747   IN UINT8                            *LastHead
748   )
749 {
750   UINT16  Size;
751 
752   Size = (UINT16) (LastHead - (UINT8 *) IpHead);
753 
754   if (Size > sizeof (EFI_IP6_HEADER)) {
755     //
756     // * (LastHead+1) point the last header's length but not include the first
757     // 8 octers, so this formluation add 8 at the end.
758     //
759     Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);
760   } else {
761     Size = 0;
762   }
763 
764   return Size;
765 }
766 
767 /**
768   Verify if the Authentication payload is correct.
769 
770   @param[in]  EspBuffer          Points to the ESP wrapped buffer.
771   @param[in]  EspSize            The size of the ESP wrapped buffer.
772   @param[in]  SadEntry           The related SAD entry to store the authentication
773                                  algorithm key.
774   @param[in]  IcvSize            The length of ICV.
775 
776   @retval EFI_SUCCESS        The authentication data is correct.
777   @retval EFI_ACCESS_DENIED  The authentication data is not correct.
778 
779 **/
780 EFI_STATUS
IpSecEspAuthVerifyPayload(IN UINT8 * EspBuffer,IN UINTN EspSize,IN IPSEC_SAD_ENTRY * SadEntry,IN UINTN IcvSize)781 IpSecEspAuthVerifyPayload (
782   IN UINT8                           *EspBuffer,
783   IN UINTN                           EspSize,
784   IN IPSEC_SAD_ENTRY                 *SadEntry,
785   IN UINTN                           IcvSize
786   )
787 {
788   EFI_STATUS           Status;
789   UINTN                AuthSize;
790   UINT8                IcvBuffer[12];
791   HASH_DATA_FRAGMENT   HashFragment[1];
792 
793   //
794   // Calculate the size of authentication payload.
795   //
796   AuthSize  = EspSize - IcvSize;
797 
798   //
799   // Calculate the icv buffer and size of the payload.
800   //
801   HashFragment[0].Data     = EspBuffer;
802   HashFragment[0].DataSize = AuthSize;
803 
804   Status = IpSecCryptoIoHmac (
805              SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,
806              SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
807              SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,
808              HashFragment,
809              1,
810              IcvBuffer,
811              IcvSize
812              );
813   if (EFI_ERROR (Status)) {
814     return Status;
815   }
816 
817   //
818   // Compare the calculated icv and the appended original icv.
819   //
820   if (CompareMem (EspBuffer + AuthSize, IcvBuffer, IcvSize) == 0) {
821     return EFI_SUCCESS;
822   }
823 
824   DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));
825   return EFI_ACCESS_DENIED;
826 }
827 
828 /**
829   Search the related SAD entry by the input .
830 
831   @param[in]  IpHead       The pointer to IP header.
832   @param[in]  IpVersion    The version of IP (IP4 or IP6).
833   @param[in]  Spi          The SPI used to search the related SAD entry.
834 
835 
836   @retval     NULL             Not find the related SAD entry.
837   @retval     IPSEC_SAD_ENTRY  Return the related SAD entry.
838 
839 **/
840 IPSEC_SAD_ENTRY *
IpSecFoundSadFromInboundPacket(UINT8 * IpHead,UINT8 IpVersion,UINT32 Spi)841 IpSecFoundSadFromInboundPacket (
842    UINT8   *IpHead,
843    UINT8   IpVersion,
844    UINT32  Spi
845    )
846 {
847   EFI_IP_ADDRESS   DestIp;
848 
849   //
850   // Parse destination address from ip header.
851   //
852   ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
853   if (IpVersion == IP_VERSION_4) {
854     CopyMem (
855       &DestIp,
856       &((IP4_HEAD *) IpHead)->Dst,
857       sizeof (IP4_ADDR)
858       );
859   } else {
860     CopyMem (
861       &DestIp,
862       &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
863       sizeof (EFI_IPv6_ADDRESS)
864       );
865   }
866 
867   //
868   // Lookup SAD entry according to the spi and dest address.
869   //
870   return IpSecLookupSadBySpi (Spi, &DestIp, IpVersion);
871 }
872 
873 /**
874   Validate the IP6 extension header format for both the packets we received
875   and that we will transmit.
876 
877   @param[in]  NextHeader    The next header field in IPv6 basic header.
878   @param[in]  ExtHdrs       The first bye of the option.
879   @param[in]  ExtHdrsLen    The length of the whole option.
880   @param[out] LastHeader    The pointer of NextHeader of the last extension
881                             header processed by IP6.
882   @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.
883                             This is an optional parameter that may be NULL.
884 
885   @retval     TRUE          The option is properly formated.
886   @retval     FALSE         The option is malformated.
887 
888 **/
889 BOOLEAN
IpSecIsIp6ExtsValid(IN UINT8 * NextHeader,IN UINT8 * ExtHdrs,IN UINT32 ExtHdrsLen,OUT UINT8 ** LastHeader,OUT UINT32 * RealExtsLen OPTIONAL)890 IpSecIsIp6ExtsValid (
891   IN UINT8                  *NextHeader,
892   IN UINT8                  *ExtHdrs,
893   IN UINT32                 ExtHdrsLen,
894   OUT UINT8                 **LastHeader,
895   OUT UINT32                *RealExtsLen    OPTIONAL
896   )
897 {
898   UINT32                     Pointer;
899   UINT8                      *Option;
900   UINT8                      OptionLen;
901   BOOLEAN                    Flag;
902   UINT8                      CountD;
903   UINT8                      CountF;
904   UINT8                      CountA;
905 
906   if (RealExtsLen != NULL) {
907     *RealExtsLen = 0;
908   }
909 
910   *LastHeader = NextHeader;
911 
912   if (ExtHdrs == NULL && ExtHdrsLen == 0) {
913     return TRUE;
914   }
915 
916   if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
917     return FALSE;
918   }
919 
920   Pointer = 0;
921   Flag    = FALSE;
922   CountD  = 0;
923   CountF  = 0;
924   CountA  = 0;
925 
926   while (Pointer <= ExtHdrsLen) {
927 
928     switch (*NextHeader) {
929     case IP6_HOP_BY_HOP:
930       if (Pointer != 0) {
931         return FALSE;
932       }
933 
934       Flag = TRUE;
935 
936     //
937     // Fall through
938     //
939     case IP6_DESTINATION:
940       if (*NextHeader == IP6_DESTINATION) {
941         CountD++;
942       }
943 
944       if (CountD > 2) {
945         return FALSE;
946       }
947 
948       NextHeader = ExtHdrs + Pointer;
949 
950       Pointer++;
951       Option     = ExtHdrs + Pointer;
952       OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);
953       Option++;
954       Pointer++;
955 
956       Pointer = Pointer + OptionLen;
957       break;
958 
959     case IP6_FRAGMENT:
960       if (++CountF > 1) {
961         return FALSE;
962       }
963       //
964       // RFC2402, AH header should after fragment header.
965       //
966       if (CountA > 1) {
967         return FALSE;
968       }
969 
970       NextHeader = ExtHdrs + Pointer;
971       Pointer    = Pointer + 8;
972       break;
973 
974     case IP6_AH:
975       if (++CountA > 1) {
976         return FALSE;
977       }
978 
979       Option     = ExtHdrs + Pointer;
980       NextHeader = Option;
981       Option++;
982       //
983       // RFC2402, Payload length is specified in 32-bit words, minus "2".
984       //
985       OptionLen  = (UINT8) ((*Option + 2) * 4);
986       Pointer    = Pointer + OptionLen;
987       break;
988 
989     default:
990       *LastHeader = NextHeader;
991        if (RealExtsLen != NULL) {
992          *RealExtsLen = Pointer;
993        }
994 
995        return TRUE;
996     }
997   }
998 
999   *LastHeader = NextHeader;
1000 
1001   if (RealExtsLen != NULL) {
1002     *RealExtsLen = Pointer;
1003   }
1004 
1005   return TRUE;
1006 }
1007 
1008 /**
1009   The actual entry to process the tunnel header and inner header for tunnel mode
1010   outbound traffic.
1011 
1012   This function is the subfunction of IpSecEspInboundPacket(). It change the destination
1013   Ip address to the station address and recalculate the uplayyer's checksum.
1014 
1015 
1016   @param[in, out] IpHead             Points to the IP header containing the ESP header
1017                                      to be trimed on input, and without ESP header
1018                                      on return.
1019   @param[in]      IpPayload          The decrypted Ip payload. It start from the inner
1020                                      header.
1021   @param[in]      IpVersion          The version of IP.
1022   @param[in]      SadData            Pointer of the relevant SAD.
1023   @param[in, out] LastHead           The Last Header in IP header on return.
1024 
1025 **/
1026 VOID
IpSecTunnelInboundPacket(IN OUT UINT8 * IpHead,IN UINT8 * IpPayload,IN UINT8 IpVersion,IN IPSEC_SAD_DATA * SadData,IN OUT UINT8 * LastHead)1027 IpSecTunnelInboundPacket (
1028   IN OUT UINT8           *IpHead,
1029   IN     UINT8           *IpPayload,
1030   IN     UINT8           IpVersion,
1031   IN     IPSEC_SAD_DATA  *SadData,
1032   IN OUT UINT8           *LastHead
1033   )
1034 {
1035   EFI_UDP_HEADER   *UdpHeader;
1036   TCP_HEAD         *TcpHeader;
1037   UINT16            *Checksum;
1038   UINT16           PseudoChecksum;
1039   UINT16           PacketChecksum;
1040   UINT32           OptionLen;
1041   IP6_ICMP_HEAD    *Icmp6Head;
1042 
1043   Checksum = NULL;
1044 
1045   if (IpVersion == IP_VERSION_4) {
1046     //
1047     // Zero OutIP header use this to indicate the input packet is under
1048     // IPsec Tunnel protected.
1049     //
1050     ZeroMem (
1051       (IP4_HEAD *)IpHead,
1052       sizeof (IP4_HEAD)
1053       );
1054     CopyMem (
1055       &((IP4_HEAD *)IpPayload)->Dst,
1056       &SadData->TunnelDestAddress.v4,
1057       sizeof (EFI_IPv4_ADDRESS)
1058       );
1059 
1060     //
1061     // Recalculate IpHeader Checksum
1062     //
1063     if (((IP4_HEAD *)(IpPayload))->Checksum != 0 ) {
1064       ((IP4_HEAD *)(IpPayload))->Checksum = 0;
1065       ((IP4_HEAD *)(IpPayload))->Checksum = (UINT16) (~NetblockChecksum (
1066                                                         (UINT8 *)IpPayload,
1067                                                         ((IP4_HEAD *)IpPayload)->HeadLen << 2
1068                                                         ));
1069 
1070 
1071     }
1072 
1073     //
1074     // Recalcualte PseudoChecksum
1075     //
1076     switch (((IP4_HEAD *)IpPayload)->Protocol) {
1077     case EFI_IP_PROTO_UDP :
1078       UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));
1079       Checksum  = & UdpHeader->Checksum;
1080       *Checksum = 0;
1081       break;
1082 
1083     case EFI_IP_PROTO_TCP:
1084       TcpHeader = (TCP_HEAD *) ((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));
1085       Checksum  = &TcpHeader->Checksum;
1086       *Checksum = 0;
1087       break;
1088 
1089     default:
1090       break;
1091       }
1092     PacketChecksum = NetblockChecksum (
1093                        (UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2),
1094                        NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)
1095                        );
1096     PseudoChecksum = NetPseudoHeadChecksum (
1097                        ((IP4_HEAD *)IpPayload)->Src,
1098                        ((IP4_HEAD *)IpPayload)->Dst,
1099                        ((IP4_HEAD *)IpPayload)->Protocol,
1100                        0
1101                        );
1102 
1103       if (Checksum != NULL) {
1104         *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
1105         *Checksum = (UINT16) ~(NetAddChecksum (*Checksum, HTONS((UINT16)(NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)))));
1106       }
1107     }else {
1108       //
1109       //  Zero OutIP header use this to indicate the input packet is under
1110       //  IPsec Tunnel protected.
1111       //
1112       ZeroMem (
1113         IpHead,
1114         sizeof (EFI_IP6_HEADER)
1115         );
1116       CopyMem (
1117         &((EFI_IP6_HEADER*)IpPayload)->DestinationAddress,
1118         &SadData->TunnelDestAddress.v6,
1119         sizeof (EFI_IPv6_ADDRESS)
1120         );
1121 
1122       //
1123       // Get the Extension Header and Header length.
1124       //
1125       IpSecIsIp6ExtsValid (
1126         &((EFI_IP6_HEADER *)IpPayload)->NextHeader,
1127         IpPayload + sizeof (EFI_IP6_HEADER),
1128         ((EFI_IP6_HEADER *)IpPayload)->PayloadLength,
1129         &LastHead,
1130         &OptionLen
1131         );
1132 
1133       //
1134       // Recalcualte PseudoChecksum
1135       //
1136       switch (*LastHead) {
1137       case EFI_IP_PROTO_UDP:
1138         UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
1139         Checksum  = &UdpHeader->Checksum;
1140         *Checksum = 0;
1141         break;
1142 
1143       case EFI_IP_PROTO_TCP:
1144         TcpHeader = (TCP_HEAD *)(IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
1145         Checksum  = &TcpHeader->Checksum;
1146         *Checksum = 0;
1147         break;
1148 
1149       case IP6_ICMP:
1150         Icmp6Head  = (IP6_ICMP_HEAD *) (IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
1151         Checksum   = &Icmp6Head->Checksum;
1152         *Checksum  = 0;
1153         break;
1154       }
1155       PacketChecksum = NetblockChecksum (
1156                          IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen,
1157                          NTOHS(((EFI_IP6_HEADER *)IpPayload)->PayloadLength) - OptionLen
1158                          );
1159       PseudoChecksum = NetIp6PseudoHeadChecksum (
1160                          &((EFI_IP6_HEADER *)IpPayload)->SourceAddress,
1161                          &((EFI_IP6_HEADER *)IpPayload)->DestinationAddress,
1162                          *LastHead,
1163                          0
1164                          );
1165 
1166     if (Checksum != NULL) {
1167       *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
1168       *Checksum = (UINT16) ~(NetAddChecksum (
1169                                *Checksum,
1170                                HTONS ((UINT16)((NTOHS (((EFI_IP6_HEADER *)(IpPayload))->PayloadLength)) - OptionLen))
1171                                ));
1172     }
1173   }
1174 }
1175 
1176 /**
1177   The actual entry to create inner header for tunnel mode inbound traffic.
1178 
1179   This function is the subfunction of IpSecEspOutboundPacket(). It create
1180   the sending packet by encrypting its payload and inserting ESP header in the orginal
1181   IP header, then return the IpHeader and IPsec protected Fragmentable.
1182 
1183   @param[in, out] IpHead             Points to IP header containing the orginal IP header
1184                                      to be processed on input, and inserted ESP header
1185                                      on return.
1186   @param[in]      IpVersion          The version of IP.
1187   @param[in]      SadData            The related SAD data.
1188   @param[in, out] LastHead           The Last Header in IP header.
1189   @param[in]      OptionsBuffer      Pointer to the options buffer.
1190   @param[in]      OptionsLength      Length of the options buffer.
1191   @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
1192                                      IPsec on input, and with IPsec protected
1193                                      on return.
1194   @param[in]      FragmentCount      The number of fragments.
1195 
1196   @retval EFI_SUCCESS              The operation was successful.
1197   @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
1198 
1199 **/
1200 UINT8 *
IpSecTunnelOutboundPacket(IN OUT UINT8 * IpHead,IN UINT8 IpVersion,IN IPSEC_SAD_DATA * SadData,IN OUT UINT8 * LastHead,IN VOID ** OptionsBuffer,IN UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN UINT32 * FragmentCount)1201 IpSecTunnelOutboundPacket (
1202   IN OUT UINT8                   *IpHead,
1203   IN     UINT8                   IpVersion,
1204   IN     IPSEC_SAD_DATA          *SadData,
1205   IN OUT UINT8                   *LastHead,
1206   IN     VOID                    **OptionsBuffer,
1207   IN     UINT32                  *OptionsLength,
1208   IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
1209   IN     UINT32                  *FragmentCount
1210   )
1211 {
1212   UINT8         *InnerHead;
1213   NET_BUF       *Packet;
1214   UINT16        PacketChecksum;
1215   UINT16        *Checksum;
1216   UINT16        PseudoChecksum;
1217   IP6_ICMP_HEAD *IcmpHead;
1218 
1219   Checksum = NULL;
1220   if (OptionsLength == NULL) {
1221     return NULL;
1222   }
1223 
1224   if (IpVersion == IP_VERSION_4) {
1225     InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength);
1226     ASSERT (InnerHead != NULL);
1227     CopyMem (
1228       InnerHead,
1229       IpHead,
1230       sizeof (IP4_HEAD)
1231       );
1232     CopyMem (
1233       InnerHead + sizeof (IP4_HEAD),
1234       *OptionsBuffer,
1235       *OptionsLength
1236       );
1237   } else {
1238     InnerHead = AllocateZeroPool (sizeof (EFI_IP6_HEADER) + *OptionsLength);
1239     ASSERT (InnerHead != NULL);
1240     CopyMem (
1241       InnerHead,
1242       IpHead,
1243       sizeof (EFI_IP6_HEADER)
1244       );
1245     CopyMem (
1246       InnerHead + sizeof (EFI_IP6_HEADER),
1247       *OptionsBuffer,
1248       *OptionsLength
1249       );
1250   }
1251   if (OptionsBuffer != NULL) {
1252     if (*OptionsLength != 0) {
1253 
1254       *OptionsBuffer = NULL;
1255       *OptionsLength = 0;
1256     }
1257   }
1258 
1259   //
1260   // 2. Reassamlbe Fragment into Packet
1261   //
1262   Packet = NetbufFromExt (
1263              (NET_FRAGMENT *)(*FragmentTable),
1264              *FragmentCount,
1265              0,
1266              0,
1267              IpSecOnRecyclePacket,
1268              NULL
1269              );
1270   ASSERT (Packet != NULL);
1271   //
1272   // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo
1273   //    CheckSum.
1274   //
1275   switch (*LastHead) {
1276   case EFI_IP_PROTO_UDP:
1277     Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0);
1278     ASSERT (Packet->Udp != NULL);
1279     Checksum = &Packet->Udp->Checksum;
1280     *Checksum = 0;
1281     break;
1282 
1283   case EFI_IP_PROTO_TCP:
1284     Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0);
1285     ASSERT (Packet->Tcp != NULL);
1286     Checksum = &Packet->Tcp->Checksum;
1287     *Checksum = 0;
1288     break;
1289 
1290   case IP6_ICMP:
1291     IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
1292     ASSERT (IcmpHead != NULL);
1293     Checksum = &IcmpHead->Checksum;
1294     *Checksum = 0;
1295     break;
1296 
1297   default:
1298     break;
1299   }
1300 
1301   PacketChecksum = NetbufChecksum (Packet);
1302 
1303   if (IpVersion == IP_VERSION_4) {
1304     //
1305     // Replace the source address of Inner Header.
1306     //
1307     CopyMem (
1308       &((IP4_HEAD *)InnerHead)->Src,
1309       &SadData->SpdSelector->LocalAddress[0].Address.v4,
1310       sizeof (EFI_IPv4_ADDRESS)
1311       );
1312 
1313     PacketChecksum = NetbufChecksum (Packet);
1314     PseudoChecksum = NetPseudoHeadChecksum (
1315                        ((IP4_HEAD *)InnerHead)->Src,
1316                        ((IP4_HEAD *)InnerHead)->Dst,
1317                        *LastHead,
1318                        0
1319                        );
1320 
1321    } else {
1322      //
1323      // Replace the source address of Inner Header.
1324      //
1325      CopyMem (
1326        &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
1327        &(SadData->SpdSelector->LocalAddress[0].Address.v6),
1328        sizeof (EFI_IPv6_ADDRESS)
1329        );
1330      PacketChecksum = NetbufChecksum (Packet);
1331      PseudoChecksum = NetIp6PseudoHeadChecksum (
1332                       &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
1333                       &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress,
1334                       *LastHead,
1335                       0
1336                       );
1337 
1338    }
1339    if (Checksum != NULL) {
1340      *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
1341      *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize)));
1342    }
1343 
1344   if (Packet != NULL) {
1345     NetbufFree (Packet);
1346   }
1347   return InnerHead;
1348 }
1349 
1350 /**
1351   The actual entry to relative function processes the inbound traffic of ESP header.
1352 
1353   This function is the subfunction of IpSecProtectInboundPacket(). It checks the
1354   received packet security property and trim the ESP header and then returns without
1355   an IPsec protected IP Header and FramgmentTable.
1356 
1357   @param[in]      IpVersion          The version of IP.
1358   @param[in, out] IpHead             Points to the IP header containing the ESP header
1359                                      to be trimed on input, and without ESP header
1360                                      on return.
1361   @param[out]     LastHead           The Last Header in IP header on return.
1362   @param[in, out] OptionsBuffer      Pointer to the options buffer.
1363   @param[in, out] OptionsLength      Length of the options buffer.
1364   @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec
1365                                      protected on input, and without IPsec protected
1366                                      on return.
1367   @param[in, out] FragmentCount      The number of fragments.
1368   @param[out]     SpdSelector        Pointer to contain the address of SPD selector on return.
1369   @param[out]     RecycleEvent       The event for recycling of resources.
1370 
1371   @retval EFI_SUCCESS              The operation was successful.
1372   @retval EFI_ACCESS_DENIED        One or more following conditions is TRUE:
1373                                    - ESP header was not found or mal-format.
1374                                    - The related SAD entry was not found.
1375                                    - The related SAD entry does not support the ESP protocol.
1376   @retval EFI_OUT_OF_RESOURCES     The required system resource can't be allocated.
1377 
1378 **/
1379 EFI_STATUS
IpSecEspInboundPacket(IN UINT8 IpVersion,IN OUT VOID * IpHead,OUT UINT8 * LastHead,IN OUT VOID ** OptionsBuffer,IN OUT UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN OUT UINT32 * FragmentCount,OUT EFI_IPSEC_SPD_SELECTOR ** SpdSelector,OUT EFI_EVENT * RecycleEvent)1380 IpSecEspInboundPacket (
1381   IN     UINT8                       IpVersion,
1382   IN OUT VOID                        *IpHead,
1383      OUT UINT8                       *LastHead,
1384   IN OUT VOID                        **OptionsBuffer,
1385   IN OUT UINT32                      *OptionsLength,
1386   IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
1387   IN OUT UINT32                      *FragmentCount,
1388      OUT EFI_IPSEC_SPD_SELECTOR      **SpdSelector,
1389      OUT EFI_EVENT                   *RecycleEvent
1390   )
1391 {
1392   EFI_STATUS            Status;
1393   NET_BUF               *Payload;
1394   UINTN                 EspSize;
1395   UINTN                 IvSize;
1396   UINTN                 BlockSize;
1397   UINTN                 MiscSize;
1398   UINTN                 PlainPayloadSize;
1399   UINTN                 PaddingSize;
1400   UINTN                 IcvSize;
1401   UINT8                 *ProcessBuffer;
1402   EFI_ESP_HEADER        *EspHeader;
1403   EFI_ESP_TAIL          *EspTail;
1404   EFI_IPSEC_SA_ID       *SaId;
1405   IPSEC_SAD_DATA        *SadData;
1406   IPSEC_SAD_ENTRY       *SadEntry;
1407   IPSEC_RECYCLE_CONTEXT *RecycleContext;
1408   UINT8                 NextHeader;
1409   UINT16                IpSecHeadSize;
1410   UINT8                 *InnerHead;
1411 
1412   Status            = EFI_SUCCESS;
1413   Payload           = NULL;
1414   ProcessBuffer     = NULL;
1415   RecycleContext    = NULL;
1416   *RecycleEvent     = NULL;
1417   PlainPayloadSize  = 0;
1418   NextHeader        = 0;
1419 
1420   //
1421   // Build netbuf from fragment table first.
1422   //
1423   Payload = NetbufFromExt (
1424               (NET_FRAGMENT *) *FragmentTable,
1425               *FragmentCount,
1426               0,
1427               sizeof (EFI_ESP_HEADER),
1428               IpSecOnRecyclePacket,
1429               NULL
1430               );
1431   if (Payload == NULL) {
1432     Status = EFI_OUT_OF_RESOURCES;
1433     goto ON_EXIT;
1434   }
1435 
1436   //
1437   // Get the esp size and esp header from netbuf.
1438   //
1439   EspSize   = Payload->TotalSize;
1440   EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);
1441 
1442   if (EspHeader == NULL) {
1443     Status = EFI_ACCESS_DENIED;
1444     goto ON_EXIT;
1445   }
1446 
1447   //
1448   // Parse destination address from ip header and found the related SAD Entry.
1449   //
1450   SadEntry = IpSecFoundSadFromInboundPacket (
1451                IpHead,
1452                IpVersion,
1453                NTOHL (EspHeader->Spi)
1454                );
1455 
1456   if (SadEntry == NULL) {
1457     Status = EFI_ACCESS_DENIED;
1458     goto ON_EXIT;
1459   }
1460 
1461   SaId    = SadEntry->Id;
1462   SadData = SadEntry->Data;
1463 
1464   //
1465   // Only support esp protocol currently.
1466   //
1467   if (SaId->Proto != EfiIPsecESP) {
1468     Status = EFI_ACCESS_DENIED;
1469     goto ON_EXIT;
1470   }
1471 
1472   if (!SadData->ManualSet) {
1473     //
1474     // TODO: Check SA lifetime and sequence number
1475     //
1476   }
1477 
1478   //
1479   // Allocate buffer for decryption and authentication.
1480   //
1481   ProcessBuffer = AllocateZeroPool (EspSize);
1482   if (ProcessBuffer == NULL) {
1483     Status = EFI_OUT_OF_RESOURCES;
1484     goto ON_EXIT;
1485   }
1486 
1487   NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);
1488 
1489   //
1490   // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.
1491   //
1492   IcvSize   = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
1493   IvSize    = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1494   BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1495 
1496   //
1497   // Make sure the ESP packet is not mal-formt.
1498   // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.
1499   // 2. Check whether the left payload size is multiple of IvSize.
1500   //
1501   MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize;
1502   if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) {
1503     Status = EFI_ACCESS_DENIED;
1504     goto ON_EXIT;
1505   }
1506   if ((EspSize - MiscSize) % BlockSize != 0) {
1507     Status = EFI_ACCESS_DENIED;
1508     goto ON_EXIT;
1509   }
1510 
1511   //
1512   // Authenticate the ESP packet.
1513   //
1514   if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1515     Status = IpSecEspAuthVerifyPayload (
1516                ProcessBuffer,
1517                EspSize,
1518                SadEntry,
1519                IcvSize
1520                );
1521     if (EFI_ERROR (Status)) {
1522       goto ON_EXIT;
1523     }
1524   }
1525   //
1526   // Decrypt the payload by the SAD entry if it has decrypt key.
1527   //
1528   if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1529     Status = IpSecCryptoIoDecrypt (
1530                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
1531                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
1532                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
1533                ProcessBuffer + sizeof (EFI_ESP_HEADER),
1534                ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize,
1535                EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize,
1536                ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize
1537                );
1538     if (EFI_ERROR (Status)) {
1539       goto ON_EXIT;
1540     }
1541   }
1542 
1543   //
1544   // Parse EspTail and compute the plain payload size.
1545   //
1546   EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));
1547   PaddingSize       = EspTail->PaddingLength;
1548   NextHeader        = EspTail->NextHeader;
1549 
1550   if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) {
1551     Status = EFI_ACCESS_DENIED;
1552     goto ON_EXIT;
1553   }
1554   PlainPayloadSize  = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize;
1555 
1556   //
1557   // TODO: handle anti-replay window
1558   //
1559   //
1560   // Decryption and authentication with esp has been done, so it's time to
1561   // reload the new packet, create recycle event and fixup ip header.
1562   //
1563   RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
1564   if (RecycleContext == NULL) {
1565     Status = EFI_OUT_OF_RESOURCES;
1566     goto ON_EXIT;
1567   }
1568 
1569   Status = gBS->CreateEvent (
1570                   EVT_NOTIFY_SIGNAL,
1571                   TPL_NOTIFY,
1572                   IpSecRecycleCallback,
1573                   RecycleContext,
1574                   RecycleEvent
1575                   );
1576   if (EFI_ERROR (Status)) {
1577     goto ON_EXIT;
1578   }
1579 
1580   //
1581   // The caller will take responsible to handle the original fragment table
1582   //
1583   *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
1584   if (*FragmentTable == NULL) {
1585     Status = EFI_OUT_OF_RESOURCES;
1586     goto ON_EXIT;
1587   }
1588 
1589   RecycleContext->PayloadBuffer       = ProcessBuffer;
1590   RecycleContext->FragmentTable       = *FragmentTable;
1591 
1592   //
1593   // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out
1594   //
1595   if (SadData->Mode == EfiIPsecTunnel) {
1596     InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
1597     IpSecTunnelInboundPacket (
1598       IpHead,
1599       InnerHead,
1600       IpVersion,
1601       SadData,
1602       LastHead
1603       );
1604 
1605     if (IpVersion == IP_VERSION_4) {
1606       (*FragmentTable)[0].FragmentBuffer  = InnerHead ;
1607       (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1608 
1609     }else {
1610       (*FragmentTable)[0].FragmentBuffer  = InnerHead;
1611       (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1612     }
1613   } else {
1614     (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
1615     (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1616   }
1617 
1618   *FragmentCount                      = 1;
1619 
1620   //
1621   // Update the total length field in ip header since processed by esp.
1622   //
1623   if (!SadData->Mode == EfiIPsecTunnel) {
1624     if (IpVersion == IP_VERSION_4) {
1625       ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize));
1626     } else {
1627       IpSecHeadSize                              = IpSecGetPlainExtHeadSize (IpHead, LastHead);
1628       ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));
1629     }
1630     //
1631     // Update the next layer field in ip header since esp header inserted.
1632     //
1633     *LastHead = NextHeader;
1634   }
1635 
1636 
1637   //
1638   // Update the SPD association of the SAD entry.
1639   //
1640   *SpdSelector = SadData->SpdSelector;
1641 
1642 ON_EXIT:
1643   if (Payload != NULL) {
1644     NetbufFree (Payload);
1645   }
1646 
1647   if (EFI_ERROR (Status)) {
1648     if (ProcessBuffer != NULL) {
1649       FreePool (ProcessBuffer);
1650     }
1651 
1652     if (RecycleContext != NULL) {
1653       FreePool (RecycleContext);
1654     }
1655 
1656     if (*RecycleEvent != NULL) {
1657       gBS->CloseEvent (*RecycleEvent);
1658     }
1659   }
1660 
1661   return Status;
1662 }
1663 
1664 /**
1665   The actual entry to the relative function processes the output traffic using the ESP protocol.
1666 
1667   This function is the subfunction of IpSecProtectOutboundPacket(). It protected
1668   the sending packet by encrypting its payload and inserting ESP header in the orginal
1669   IP header, then return the IpHeader and IPsec protected Fragmentable.
1670 
1671   @param[in]      IpVersion          The version of IP.
1672   @param[in, out] IpHead             Points to IP header containing the orginal IP header
1673                                      to be processed on input, and inserted ESP header
1674                                      on return.
1675   @param[in, out] LastHead           The Last Header in IP header.
1676   @param[in, out] OptionsBuffer      Pointer to the options buffer.
1677   @param[in, out] OptionsLength      Length of the options buffer.
1678   @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
1679                                      IPsec on input, and with IPsec protected
1680                                      on return.
1681   @param[in, out] FragmentCount      The number of fragments.
1682   @param[in]      SadEntry           The related SAD entry.
1683   @param[out]     RecycleEvent       The event for recycling of resources.
1684 
1685   @retval EFI_SUCCESS              The operation was successful.
1686   @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
1687 
1688 **/
1689 EFI_STATUS
IpSecEspOutboundPacket(IN UINT8 IpVersion,IN OUT VOID * IpHead,IN OUT UINT8 * LastHead,IN OUT VOID ** OptionsBuffer,IN OUT UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN OUT UINT32 * FragmentCount,IN IPSEC_SAD_ENTRY * SadEntry,OUT EFI_EVENT * RecycleEvent)1690 IpSecEspOutboundPacket (
1691   IN UINT8                           IpVersion,
1692   IN OUT VOID                        *IpHead,
1693   IN OUT UINT8                       *LastHead,
1694   IN OUT VOID                        **OptionsBuffer,
1695   IN OUT UINT32                      *OptionsLength,
1696   IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
1697   IN OUT UINT32                      *FragmentCount,
1698   IN     IPSEC_SAD_ENTRY             *SadEntry,
1699      OUT EFI_EVENT                   *RecycleEvent
1700   )
1701 {
1702   EFI_STATUS            Status;
1703   UINTN                 Index;
1704   EFI_IPSEC_SA_ID       *SaId;
1705   IPSEC_SAD_DATA        *SadData;
1706   IPSEC_RECYCLE_CONTEXT *RecycleContext;
1707   UINT8                 *ProcessBuffer;
1708   UINTN                 BytesCopied;
1709   INTN                  EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4
1710   UINTN                 EspSize;         // Total size of esp wrapped ip payload
1711   UINTN                 IvSize;          // Size of IV, optional, might be 0
1712   UINTN                 PlainPayloadSize;// Original IP payload size
1713   UINTN                 PaddingSize;     // Size of padding
1714   UINTN                 EncryptSize;     // Size of data to be encrypted, start after IV and
1715                                          // stop before ICV
1716   UINTN                 IcvSize;         // Size of ICV, optional, might be 0
1717   UINT8                 *RestOfPayload;  // Start of Payload after IV
1718   UINT8                 *Padding;        // Start address of padding
1719   EFI_ESP_HEADER        *EspHeader;      // Start address of ESP frame
1720   EFI_ESP_TAIL          *EspTail;        // Address behind padding
1721   UINT8                 *InnerHead;
1722   HASH_DATA_FRAGMENT    HashFragment[1];
1723 
1724   Status          = EFI_ACCESS_DENIED;
1725   SaId            = SadEntry->Id;
1726   SadData         = SadEntry->Data;
1727   ProcessBuffer   = NULL;
1728   RecycleContext  = NULL;
1729   *RecycleEvent   = NULL;
1730   InnerHead       = NULL;
1731 
1732   if (!SadData->ManualSet &&
1733       SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&
1734       SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL
1735       ) {
1736     //
1737     // Invalid manual SAD entry configuration.
1738     //
1739     goto ON_EXIT;
1740   }
1741 
1742   //
1743   // Create OutHeader according to Inner Header
1744   //
1745   if (SadData->Mode == EfiIPsecTunnel) {
1746     InnerHead = IpSecTunnelOutboundPacket (
1747                   IpHead,
1748                   IpVersion,
1749                   SadData,
1750                   LastHead,
1751                   OptionsBuffer,
1752                   OptionsLength,
1753                   FragmentTable,
1754                   FragmentCount
1755                   );
1756 
1757     if (InnerHead == NULL) {
1758       return EFI_INVALID_PARAMETER;
1759     }
1760 
1761   }
1762 
1763   //
1764   // Calculate enctrypt block size, need iv by default and 4 bytes alignment.
1765   //
1766   EncryptBlockSize  = 4;
1767 
1768   if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1769     EncryptBlockSize  = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1770 
1771     if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {
1772       goto ON_EXIT;
1773     }
1774   }
1775 
1776   //
1777   // Calculate the plain payload size accroding to the fragment table.
1778   //
1779   PlainPayloadSize = 0;
1780   for (Index = 0; Index < *FragmentCount; Index++) {
1781     PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;
1782   }
1783 
1784   //
1785   // Add IPHeader size for Tunnel Mode
1786   //
1787   if (SadData->Mode == EfiIPsecTunnel) {
1788     if (IpVersion == IP_VERSION_4) {
1789       PlainPayloadSize += sizeof (IP4_HEAD);
1790     } else {
1791       PlainPayloadSize += sizeof (EFI_IP6_HEADER);
1792     }
1793     //
1794     // OPtions should be encryption into it
1795     //
1796     PlainPayloadSize += *OptionsLength;
1797   }
1798 
1799 
1800   //
1801   // Calculate icv size, optional by default and 4 bytes alignment.
1802   //
1803   IcvSize = 0;
1804   if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1805     IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
1806     if (IcvSize % 4 != 0) {
1807       goto ON_EXIT;
1808     }
1809   }
1810 
1811   //
1812   // Calcuate the total size of esp wrapped ip payload.
1813   //
1814   IvSize        = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1815   EncryptSize   = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;
1816   PaddingSize   = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);
1817   EspSize       = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;
1818 
1819   ProcessBuffer = AllocateZeroPool (EspSize);
1820   if (ProcessBuffer == NULL) {
1821     Status = EFI_OUT_OF_RESOURCES;
1822     goto ON_EXIT;
1823   }
1824 
1825   //
1826   // Calculate esp header and esp tail including header, payload and padding.
1827   //
1828   EspHeader     = (EFI_ESP_HEADER *) ProcessBuffer;
1829   RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;
1830   Padding       = RestOfPayload + PlainPayloadSize;
1831   EspTail       = (EFI_ESP_TAIL *) (Padding + PaddingSize);
1832 
1833   //
1834   // Fill the sn and spi fields in esp header.
1835   //
1836   EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);
1837   //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);
1838   EspHeader->Spi            = HTONL (SaId->Spi);
1839 
1840   //
1841   // Copy the rest of payload (after iv) from the original fragment buffer.
1842   //
1843   BytesCopied = 0;
1844 
1845   //
1846   // For Tunnel Mode
1847   //
1848   if (SadData->Mode == EfiIPsecTunnel) {
1849     if (IpVersion == IP_VERSION_4) {
1850       //
1851       // HeadLen, Total Length
1852       //
1853       ((IP4_HEAD *)InnerHead)->HeadLen  = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2);
1854       ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize);
1855       ((IP4_HEAD *)InnerHead)->Checksum = 0;
1856       ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum (
1857                                                   (UINT8 *)InnerHead,
1858                                                   sizeof(IP4_HEAD)
1859                                                   ));
1860       CopyMem (
1861         RestOfPayload + BytesCopied,
1862         InnerHead,
1863         sizeof (IP4_HEAD) + *OptionsLength
1864         );
1865       BytesCopied += sizeof (IP4_HEAD) + *OptionsLength;
1866 
1867     } else {
1868     ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER)));
1869       CopyMem (
1870         RestOfPayload + BytesCopied,
1871         InnerHead,
1872         sizeof (EFI_IP6_HEADER) + *OptionsLength
1873         );
1874       BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength;
1875     }
1876   }
1877 
1878   for (Index = 0; Index < *FragmentCount; Index++) {
1879     CopyMem (
1880       (RestOfPayload + BytesCopied),
1881       (*FragmentTable)[Index].FragmentBuffer,
1882       (*FragmentTable)[Index].FragmentLength
1883       );
1884     BytesCopied += (*FragmentTable)[Index].FragmentLength;
1885   }
1886   //
1887   // Fill the padding buffer by natural number sequence.
1888   //
1889   for (Index = 0; Index < PaddingSize; Index++) {
1890     Padding[Index] = (UINT8) (Index + 1);
1891   }
1892   //
1893   // Fill the padding length and next header fields in esp tail.
1894   //
1895   EspTail->PaddingLength  = (UINT8) PaddingSize;
1896   EspTail->NextHeader     = *LastHead;
1897 
1898   //
1899   // Fill the next header for Tunnel mode.
1900   //
1901   if (SadData->Mode == EfiIPsecTunnel) {
1902     if (IpVersion == IP_VERSION_4) {
1903       EspTail->NextHeader = 4;
1904     } else {
1905       EspTail->NextHeader = 41;
1906     }
1907   }
1908 
1909   //
1910   // Generate iv at random by crypt library.
1911   //
1912   Status = IpSecGenerateIv (
1913              (UINT8 *) (EspHeader + 1),
1914              IvSize
1915              );
1916 
1917 
1918   if (EFI_ERROR (Status)) {
1919     goto ON_EXIT;
1920   }
1921 
1922   //
1923   // Encryption the payload (after iv) by the SAD entry if has encrypt key.
1924   //
1925   if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1926     Status = IpSecCryptoIoEncrypt (
1927                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
1928                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
1929                SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
1930                (UINT8 *)(EspHeader + 1),
1931                RestOfPayload,
1932                EncryptSize,
1933                RestOfPayload
1934                );
1935 
1936     if (EFI_ERROR (Status)) {
1937       goto ON_EXIT;
1938     }
1939   }
1940 
1941   //
1942   // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.
1943   //
1944   if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1945 
1946     HashFragment[0].Data     = ProcessBuffer;
1947     HashFragment[0].DataSize = EspSize - IcvSize;
1948     Status = IpSecCryptoIoHmac (
1949                SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,
1950                SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
1951                SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,
1952                HashFragment,
1953                1,
1954                ProcessBuffer + EspSize - IcvSize,
1955                IcvSize
1956                );
1957     if (EFI_ERROR (Status)) {
1958       goto ON_EXIT;
1959     }
1960   }
1961 
1962   //
1963   // Encryption and authentication with esp has been done, so it's time to
1964   // reload the new packet, create recycle event and fixup ip header.
1965   //
1966   RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
1967   if (RecycleContext == NULL) {
1968     Status = EFI_OUT_OF_RESOURCES;
1969     goto ON_EXIT;
1970   }
1971 
1972   Status = gBS->CreateEvent (
1973                   EVT_NOTIFY_SIGNAL,
1974                   TPL_NOTIFY,
1975                   IpSecRecycleCallback,
1976                   RecycleContext,
1977                   RecycleEvent
1978                   );
1979   if (EFI_ERROR (Status)) {
1980     goto ON_EXIT;
1981   }
1982   //
1983   // Caller take responsible to handle the original fragment table.
1984   //
1985   *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
1986   if (*FragmentTable == NULL) {
1987     Status = EFI_OUT_OF_RESOURCES;
1988     goto ON_EXIT;
1989   }
1990 
1991   RecycleContext->FragmentTable       = *FragmentTable;
1992   RecycleContext->PayloadBuffer       = ProcessBuffer;
1993   (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer;
1994   (*FragmentTable)[0].FragmentLength  = (UINT32) EspSize;
1995   *FragmentCount                      = 1;
1996 
1997   //
1998   // Update the total length field in ip header since processed by esp.
1999   //
2000   if (IpVersion == IP_VERSION_4) {
2001     ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize));
2002   } else {
2003     ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);
2004   }
2005 
2006   //
2007   // If tunnel mode, it should change the outer Ip header with tunnel source address
2008   // and destination tunnel address.
2009   //
2010   if (SadData->Mode == EfiIPsecTunnel) {
2011     if (IpVersion == IP_VERSION_4) {
2012       CopyMem (
2013         &((IP4_HEAD *) IpHead)->Src,
2014         &SadData->TunnelSourceAddress.v4,
2015         sizeof (EFI_IPv4_ADDRESS)
2016         );
2017       CopyMem (
2018         &((IP4_HEAD *) IpHead)->Dst,
2019         &SadData->TunnelDestAddress.v4,
2020         sizeof (EFI_IPv4_ADDRESS)
2021         );
2022     } else {
2023       CopyMem (
2024         &((EFI_IP6_HEADER *) IpHead)->SourceAddress,
2025         &SadData->TunnelSourceAddress.v6,
2026         sizeof (EFI_IPv6_ADDRESS)
2027         );
2028       CopyMem (
2029         &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
2030         &SadData->TunnelDestAddress.v6,
2031         sizeof (EFI_IPv6_ADDRESS)
2032         );
2033     }
2034   }
2035 
2036   //
2037   // Update the next layer field in ip header since esp header inserted.
2038   //
2039   *LastHead = IPSEC_ESP_PROTOCOL;
2040 
2041   //
2042   // Increase the sn number in SAD entry according to rfc4303.
2043   //
2044   SadData->SequenceNumber++;
2045 
2046 ON_EXIT:
2047   if (EFI_ERROR (Status)) {
2048     if (ProcessBuffer != NULL) {
2049       FreePool (ProcessBuffer);
2050     }
2051 
2052     if (RecycleContext != NULL) {
2053       FreePool (RecycleContext);
2054     }
2055 
2056     if (*RecycleEvent != NULL) {
2057       gBS->CloseEvent (*RecycleEvent);
2058     }
2059   }
2060 
2061   return Status;
2062 }
2063 
2064 /**
2065   This function processes the inbound traffic with IPsec.
2066 
2067   It checks the received packet security property, trims the ESP/AH header, and then
2068   returns without an IPsec protected IP Header and FragmentTable.
2069 
2070   @param[in]      IpVersion          The version of IP.
2071   @param[in, out] IpHead             Points to IP header containing the ESP/AH header
2072                                      to be trimed on input, and without ESP/AH header
2073                                      on return.
2074   @param[in, out] LastHead           The Last Header in IP header on return.
2075   @param[in, out] OptionsBuffer      Pointer to the options buffer.
2076   @param[in, out] OptionsLength      Length of the options buffer.
2077   @param[in, out] FragmentTable      Pointer to a list of fragments in form of IPsec
2078                                      protected on input, and without IPsec protected
2079                                      on return.
2080   @param[in, out] FragmentCount      The number of fragments.
2081   @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.
2082   @param[out]     RecycleEvent       The event for recycling of resources.
2083 
2084   @retval EFI_SUCCESS              The operation was successful.
2085   @retval EFI_UNSUPPORTED          The IPSEC protocol is not supported.
2086 
2087 **/
2088 EFI_STATUS
IpSecProtectInboundPacket(IN UINT8 IpVersion,IN OUT VOID * IpHead,IN OUT UINT8 * LastHead,IN OUT VOID ** OptionsBuffer,IN OUT UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN OUT UINT32 * FragmentCount,OUT EFI_IPSEC_SPD_SELECTOR ** SpdEntry,OUT EFI_EVENT * RecycleEvent)2089 IpSecProtectInboundPacket (
2090   IN     UINT8                       IpVersion,
2091   IN OUT VOID                        *IpHead,
2092   IN OUT UINT8                       *LastHead,
2093   IN OUT VOID                        **OptionsBuffer,
2094   IN OUT UINT32                      *OptionsLength,
2095   IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
2096   IN OUT UINT32                      *FragmentCount,
2097      OUT EFI_IPSEC_SPD_SELECTOR      **SpdEntry,
2098      OUT EFI_EVENT                   *RecycleEvent
2099   )
2100 {
2101   if (*LastHead == IPSEC_ESP_PROTOCOL) {
2102     //
2103     // Process the esp ipsec header of the inbound traffic.
2104     //
2105     return IpSecEspInboundPacket (
2106              IpVersion,
2107              IpHead,
2108              LastHead,
2109              OptionsBuffer,
2110              OptionsLength,
2111              FragmentTable,
2112              FragmentCount,
2113              SpdEntry,
2114              RecycleEvent
2115              );
2116   }
2117   //
2118   // The other protocols are not supported.
2119   //
2120   return EFI_UNSUPPORTED;
2121 }
2122 
2123 /**
2124   This fucntion processes the output traffic with IPsec.
2125 
2126   It protected the sending packet by encrypting it payload and inserting ESP/AH header
2127   in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.
2128 
2129   @param[in]      IpVersion          The version of IP.
2130   @param[in, out] IpHead             Point to IP header containing the orginal IP header
2131                                      to be processed on input, and inserted ESP/AH header
2132                                      on return.
2133   @param[in, out] LastHead           The Last Header in IP header.
2134   @param[in, out] OptionsBuffer      Pointer to the options buffer.
2135   @param[in, out] OptionsLength      Length of the options buffer.
2136   @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
2137                                      IPsec on input, and with IPsec protected
2138                                      on return.
2139   @param[in, out] FragmentCount      Number of fragments.
2140   @param[in]      SadEntry           Related SAD entry.
2141   @param[out]     RecycleEvent       Event for recycling of resources.
2142 
2143   @retval EFI_SUCCESS              The operation is successful.
2144   @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.
2145 
2146 **/
2147 EFI_STATUS
IpSecProtectOutboundPacket(IN UINT8 IpVersion,IN OUT VOID * IpHead,IN OUT UINT8 * LastHead,IN OUT VOID ** OptionsBuffer,IN OUT UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN OUT UINT32 * FragmentCount,IN IPSEC_SAD_ENTRY * SadEntry,OUT EFI_EVENT * RecycleEvent)2148 IpSecProtectOutboundPacket (
2149   IN     UINT8                       IpVersion,
2150   IN OUT VOID                        *IpHead,
2151   IN OUT UINT8                       *LastHead,
2152   IN OUT VOID                        **OptionsBuffer,
2153   IN OUT UINT32                      *OptionsLength,
2154   IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
2155   IN OUT UINT32                      *FragmentCount,
2156   IN     IPSEC_SAD_ENTRY             *SadEntry,
2157      OUT EFI_EVENT                   *RecycleEvent
2158   )
2159 {
2160   if (SadEntry->Id->Proto == EfiIPsecESP) {
2161     //
2162     // Process the esp ipsec header of the outbound traffic.
2163     //
2164     return IpSecEspOutboundPacket (
2165              IpVersion,
2166              IpHead,
2167              LastHead,
2168              OptionsBuffer,
2169              OptionsLength,
2170              FragmentTable,
2171              FragmentCount,
2172              SadEntry,
2173              RecycleEvent
2174              );
2175   }
2176   //
2177   // The other protocols are not supported.
2178   //
2179   return EFI_UNSUPPORTED;
2180 }
2181