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