1/** @file
2  The implementation of IPsec.
3
4  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5  Copyright (c) 2009 - 2011, 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**/
39BOOLEAN
40IpSecValidAddressRange (
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**/
104VOID
105IpSecExtractAddressRange (
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**/
154BOOLEAN
155IpSecMatchIpAddress (
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**/
233BOOLEAN
234IpSecMatchNextLayerProtocol (
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**/
324IPSEC_SAD_ENTRY *
325IpSecLookupSadBySpd (
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**/
363IPSEC_SAD_ENTRY *
364IpSecLookupSadBySpi (
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**/
434EFI_STATUS
435IpSecLookupSadEntry (
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**/
548IPSEC_PAD_ENTRY *
549IpSecLookupPadEntry (
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**/
591EFI_STATUS
592IpSecLookupSpdEntry (
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**/
695VOID
696EFIAPI
697IpSecOnRecyclePacket (
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**/
711VOID
712EFIAPI
713IpSecRecycleCallback (
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**/
745UINT16
746IpSecGetPlainExtHeadSize (
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**/
781EFI_STATUS
782IpSecEspAuthVerifyPayload (
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**/
841IPSEC_SAD_ENTRY *
842IpSecFoundSadFromInboundPacket (
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**/
890BOOLEAN
891IpSecIsIp6ExtsValid (
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**/
1023VOID
1024IpSecTunnelInboundPacket (
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  @retval EFI_SUCCESS              The operation was successful.
1194  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
1195
1196**/
1197UINT8 *
1198IpSecTunnelOutboundPacket (
1199  IN OUT UINT8                   *IpHead,
1200  IN     UINT8                   IpVersion,
1201  IN     IPSEC_SAD_DATA          *SadData,
1202  IN OUT UINT8                   *LastHead,
1203  IN     VOID                    **OptionsBuffer,
1204  IN     UINT32                  *OptionsLength,
1205  IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
1206  IN     UINT32                  *FragmentCount
1207  )
1208{
1209  UINT8         *InnerHead;
1210  NET_BUF       *Packet;
1211  UINT16        PacketChecksum;
1212  UINT16        *Checksum;
1213  UINT16        PseudoChecksum;
1214  IP6_ICMP_HEAD *IcmpHead;
1215
1216  Checksum = NULL;
1217  if (OptionsLength == NULL) {
1218    return NULL;
1219  }
1220
1221  if (IpVersion == IP_VERSION_4) {
1222    InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength);
1223    ASSERT (InnerHead != NULL);
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    ASSERT (InnerHead != NULL);
1237    CopyMem (
1238      InnerHead,
1239      IpHead,
1240      sizeof (EFI_IP6_HEADER)
1241      );
1242    CopyMem (
1243      InnerHead + sizeof (EFI_IP6_HEADER),
1244      *OptionsBuffer,
1245      *OptionsLength
1246      );
1247  }
1248  if (OptionsBuffer != NULL) {
1249    if (*OptionsLength != 0) {
1250
1251      *OptionsBuffer = NULL;
1252      *OptionsLength = 0;
1253    }
1254  }
1255
1256  //
1257  // 2. Reassamlbe Fragment into Packet
1258  //
1259  Packet = NetbufFromExt (
1260             (NET_FRAGMENT *)(*FragmentTable),
1261             *FragmentCount,
1262             0,
1263             0,
1264             IpSecOnRecyclePacket,
1265             NULL
1266             );
1267  ASSERT (Packet != NULL);
1268  //
1269  // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo
1270  //    CheckSum.
1271  //
1272  switch (*LastHead) {
1273  case EFI_IP_PROTO_UDP:
1274    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0);
1275    ASSERT (Packet->Udp != NULL);
1276    Checksum = &Packet->Udp->Checksum;
1277    *Checksum = 0;
1278    break;
1279
1280  case EFI_IP_PROTO_TCP:
1281    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0);
1282    ASSERT (Packet->Tcp != NULL);
1283    Checksum = &Packet->Tcp->Checksum;
1284    *Checksum = 0;
1285    break;
1286
1287  case IP6_ICMP:
1288    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
1289    ASSERT (IcmpHead != NULL);
1290    Checksum = &IcmpHead->Checksum;
1291    *Checksum = 0;
1292    break;
1293
1294  default:
1295    break;
1296  }
1297
1298  PacketChecksum = NetbufChecksum (Packet);
1299
1300  if (IpVersion == IP_VERSION_4) {
1301    //
1302    // Replace the source address of Inner Header.
1303    //
1304    CopyMem (
1305      &((IP4_HEAD *)InnerHead)->Src,
1306      &SadData->SpdSelector->LocalAddress[0].Address.v4,
1307      sizeof (EFI_IPv4_ADDRESS)
1308      );
1309
1310    PacketChecksum = NetbufChecksum (Packet);
1311    PseudoChecksum = NetPseudoHeadChecksum (
1312                       ((IP4_HEAD *)InnerHead)->Src,
1313                       ((IP4_HEAD *)InnerHead)->Dst,
1314                       *LastHead,
1315                       0
1316                       );
1317
1318   } else {
1319     //
1320     // Replace the source address of Inner Header.
1321     //
1322     CopyMem (
1323       &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
1324       &(SadData->SpdSelector->LocalAddress[0].Address.v6),
1325       sizeof (EFI_IPv6_ADDRESS)
1326       );
1327     PacketChecksum = NetbufChecksum (Packet);
1328     PseudoChecksum = NetIp6PseudoHeadChecksum (
1329                      &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
1330                      &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress,
1331                      *LastHead,
1332                      0
1333                      );
1334
1335   }
1336   if (Checksum != NULL) {
1337     *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
1338     *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize)));
1339   }
1340
1341  if (Packet != NULL) {
1342    NetbufFree (Packet);
1343  }
1344  return InnerHead;
1345}
1346
1347/**
1348  The actual entry to relative function processes the inbound traffic of ESP header.
1349
1350  This function is the subfunction of IpSecProtectInboundPacket(). It checks the
1351  received packet security property and trim the ESP header and then returns without
1352  an IPsec protected IP Header and FramgmentTable.
1353
1354  @param[in]      IpVersion          The version of IP.
1355  @param[in, out] IpHead             Points to the IP header containing the ESP header
1356                                     to be trimed on input, and without ESP header
1357                                     on return.
1358  @param[out]     LastHead           The Last Header in IP header on return.
1359  @param[in, out] OptionsBuffer      Pointer to the options buffer.
1360  @param[in, out] OptionsLength      Length of the options buffer.
1361  @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec
1362                                     protected on input, and without IPsec protected
1363                                     on return.
1364  @param[in, out] FragmentCount      The number of fragments.
1365  @param[out]     SpdSelector        Pointer to contain the address of SPD selector on return.
1366  @param[out]     RecycleEvent       The event for recycling of resources.
1367
1368  @retval EFI_SUCCESS              The operation was successful.
1369  @retval EFI_ACCESS_DENIED        One or more following conditions is TRUE:
1370                                   - ESP header was not found or mal-format.
1371                                   - The related SAD entry was not found.
1372                                   - The related SAD entry does not support the ESP protocol.
1373  @retval EFI_OUT_OF_RESOURCES     The required system resource can't be allocated.
1374
1375**/
1376EFI_STATUS
1377IpSecEspInboundPacket (
1378  IN     UINT8                       IpVersion,
1379  IN OUT VOID                        *IpHead,
1380     OUT UINT8                       *LastHead,
1381  IN OUT VOID                        **OptionsBuffer,
1382  IN OUT UINT32                      *OptionsLength,
1383  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
1384  IN OUT UINT32                      *FragmentCount,
1385     OUT EFI_IPSEC_SPD_SELECTOR      **SpdSelector,
1386     OUT EFI_EVENT                   *RecycleEvent
1387  )
1388{
1389  EFI_STATUS            Status;
1390  NET_BUF               *Payload;
1391  UINTN                 EspSize;
1392  UINTN                 IvSize;
1393  UINTN                 BlockSize;
1394  UINTN                 MiscSize;
1395  UINTN                 PlainPayloadSize;
1396  UINTN                 PaddingSize;
1397  UINTN                 IcvSize;
1398  UINT8                 *ProcessBuffer;
1399  EFI_ESP_HEADER        *EspHeader;
1400  EFI_ESP_TAIL          *EspTail;
1401  EFI_IPSEC_SA_ID       *SaId;
1402  IPSEC_SAD_DATA        *SadData;
1403  IPSEC_SAD_ENTRY       *SadEntry;
1404  IPSEC_RECYCLE_CONTEXT *RecycleContext;
1405  UINT8                 NextHeader;
1406  UINT16                IpSecHeadSize;
1407  UINT8                 *InnerHead;
1408
1409  Status            = EFI_SUCCESS;
1410  Payload           = NULL;
1411  ProcessBuffer     = NULL;
1412  RecycleContext    = NULL;
1413  *RecycleEvent     = NULL;
1414  PlainPayloadSize  = 0;
1415  NextHeader        = 0;
1416
1417  //
1418  // Build netbuf from fragment table first.
1419  //
1420  Payload = NetbufFromExt (
1421              (NET_FRAGMENT *) *FragmentTable,
1422              *FragmentCount,
1423              0,
1424              sizeof (EFI_ESP_HEADER),
1425              IpSecOnRecyclePacket,
1426              NULL
1427              );
1428  if (Payload == NULL) {
1429    Status = EFI_OUT_OF_RESOURCES;
1430    goto ON_EXIT;
1431  }
1432
1433  //
1434  // Get the esp size and esp header from netbuf.
1435  //
1436  EspSize   = Payload->TotalSize;
1437  EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);
1438
1439  if (EspHeader == NULL) {
1440    Status = EFI_ACCESS_DENIED;
1441    goto ON_EXIT;
1442  }
1443
1444  //
1445  // Parse destination address from ip header and found the related SAD Entry.
1446  //
1447  SadEntry = IpSecFoundSadFromInboundPacket (
1448               IpHead,
1449               IpVersion,
1450               NTOHL (EspHeader->Spi)
1451               );
1452
1453  if (SadEntry == NULL) {
1454    Status = EFI_ACCESS_DENIED;
1455    goto ON_EXIT;
1456  }
1457
1458  SaId    = SadEntry->Id;
1459  SadData = SadEntry->Data;
1460
1461  //
1462  // Only support esp protocol currently.
1463  //
1464  if (SaId->Proto != EfiIPsecESP) {
1465    Status = EFI_ACCESS_DENIED;
1466    goto ON_EXIT;
1467  }
1468
1469  if (!SadData->ManualSet) {
1470    //
1471    // TODO: Check SA lifetime and sequence number
1472    //
1473  }
1474
1475  //
1476  // Allocate buffer for decryption and authentication.
1477  //
1478  ProcessBuffer = AllocateZeroPool (EspSize);
1479  if (ProcessBuffer == NULL) {
1480    Status = EFI_OUT_OF_RESOURCES;
1481    goto ON_EXIT;
1482  }
1483
1484  NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);
1485
1486  //
1487  // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.
1488  //
1489  IcvSize   = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
1490  IvSize    = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1491  BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1492
1493  //
1494  // Make sure the ESP packet is not mal-formt.
1495  // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.
1496  // 2. Check whether the left payload size is multiple of IvSize.
1497  //
1498  MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize;
1499  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) {
1500    Status = EFI_ACCESS_DENIED;
1501    goto ON_EXIT;
1502  }
1503  if ((EspSize - MiscSize) % BlockSize != 0) {
1504    Status = EFI_ACCESS_DENIED;
1505    goto ON_EXIT;
1506  }
1507
1508  //
1509  // Authenticate the ESP packet.
1510  //
1511  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1512    Status = IpSecEspAuthVerifyPayload (
1513               ProcessBuffer,
1514               EspSize,
1515               SadEntry,
1516               IcvSize
1517               );
1518    if (EFI_ERROR (Status)) {
1519      goto ON_EXIT;
1520    }
1521  }
1522  //
1523  // Decrypt the payload by the SAD entry if it has decrypt key.
1524  //
1525  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1526    Status = IpSecCryptoIoDecrypt (
1527               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
1528               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
1529               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
1530               ProcessBuffer + sizeof (EFI_ESP_HEADER),
1531               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize,
1532               EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize,
1533               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize
1534               );
1535    if (EFI_ERROR (Status)) {
1536      goto ON_EXIT;
1537    }
1538  }
1539
1540  //
1541  // Parse EspTail and compute the plain payload size.
1542  //
1543  EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));
1544  PaddingSize       = EspTail->PaddingLength;
1545  NextHeader        = EspTail->NextHeader;
1546
1547  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) {
1548    Status = EFI_ACCESS_DENIED;
1549    goto ON_EXIT;
1550  }
1551  PlainPayloadSize  = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize;
1552
1553  //
1554  // TODO: handle anti-replay window
1555  //
1556  //
1557  // Decryption and authentication with esp has been done, so it's time to
1558  // reload the new packet, create recycle event and fixup ip header.
1559  //
1560  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
1561  if (RecycleContext == NULL) {
1562    Status = EFI_OUT_OF_RESOURCES;
1563    goto ON_EXIT;
1564  }
1565
1566  Status = gBS->CreateEvent (
1567                  EVT_NOTIFY_SIGNAL,
1568                  TPL_NOTIFY,
1569                  IpSecRecycleCallback,
1570                  RecycleContext,
1571                  RecycleEvent
1572                  );
1573  if (EFI_ERROR (Status)) {
1574    goto ON_EXIT;
1575  }
1576
1577  //
1578  // The caller will take responsible to handle the original fragment table
1579  //
1580  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
1581  if (*FragmentTable == NULL) {
1582    Status = EFI_OUT_OF_RESOURCES;
1583    goto ON_EXIT;
1584  }
1585
1586  RecycleContext->PayloadBuffer       = ProcessBuffer;
1587  RecycleContext->FragmentTable       = *FragmentTable;
1588
1589  //
1590  // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out
1591  //
1592  if (SadData->Mode == EfiIPsecTunnel) {
1593    InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
1594    IpSecTunnelInboundPacket (
1595      IpHead,
1596      InnerHead,
1597      IpVersion,
1598      SadData,
1599      LastHead
1600      );
1601
1602    if (IpVersion == IP_VERSION_4) {
1603      (*FragmentTable)[0].FragmentBuffer  = InnerHead ;
1604      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1605
1606    }else {
1607      (*FragmentTable)[0].FragmentBuffer  = InnerHead;
1608      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1609    }
1610  } else {
1611    (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
1612    (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
1613  }
1614
1615  *FragmentCount                      = 1;
1616
1617  //
1618  // Update the total length field in ip header since processed by esp.
1619  //
1620  if (SadData->Mode != EfiIPsecTunnel) {
1621    if (IpVersion == IP_VERSION_4) {
1622      ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize));
1623    } else {
1624      IpSecHeadSize                              = IpSecGetPlainExtHeadSize (IpHead, LastHead);
1625      ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));
1626    }
1627    //
1628    // Update the next layer field in ip header since esp header inserted.
1629    //
1630    *LastHead = NextHeader;
1631  }
1632
1633
1634  //
1635  // Update the SPD association of the SAD entry.
1636  //
1637  *SpdSelector = SadData->SpdSelector;
1638
1639ON_EXIT:
1640  if (Payload != NULL) {
1641    NetbufFree (Payload);
1642  }
1643
1644  if (EFI_ERROR (Status)) {
1645    if (ProcessBuffer != NULL) {
1646      FreePool (ProcessBuffer);
1647    }
1648
1649    if (RecycleContext != NULL) {
1650      FreePool (RecycleContext);
1651    }
1652
1653    if (*RecycleEvent != NULL) {
1654      gBS->CloseEvent (*RecycleEvent);
1655    }
1656  }
1657
1658  return Status;
1659}
1660
1661/**
1662  The actual entry to the relative function processes the output traffic using the ESP protocol.
1663
1664  This function is the subfunction of IpSecProtectOutboundPacket(). It protected
1665  the sending packet by encrypting its payload and inserting ESP header in the orginal
1666  IP header, then return the IpHeader and IPsec protected Fragmentable.
1667
1668  @param[in]      IpVersion          The version of IP.
1669  @param[in, out] IpHead             Points to IP header containing the orginal IP header
1670                                     to be processed on input, and inserted ESP header
1671                                     on return.
1672  @param[in, out] LastHead           The Last Header in IP header.
1673  @param[in, out] OptionsBuffer      Pointer to the options buffer.
1674  @param[in, out] OptionsLength      Length of the options buffer.
1675  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
1676                                     IPsec on input, and with IPsec protected
1677                                     on return.
1678  @param[in, out] FragmentCount      The number of fragments.
1679  @param[in]      SadEntry           The related SAD entry.
1680  @param[out]     RecycleEvent       The event for recycling of resources.
1681
1682  @retval EFI_SUCCESS              The operation was successful.
1683  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
1684
1685**/
1686EFI_STATUS
1687IpSecEspOutboundPacket (
1688  IN UINT8                           IpVersion,
1689  IN OUT VOID                        *IpHead,
1690  IN OUT UINT8                       *LastHead,
1691  IN OUT VOID                        **OptionsBuffer,
1692  IN OUT UINT32                      *OptionsLength,
1693  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
1694  IN OUT UINT32                      *FragmentCount,
1695  IN     IPSEC_SAD_ENTRY             *SadEntry,
1696     OUT EFI_EVENT                   *RecycleEvent
1697  )
1698{
1699  EFI_STATUS            Status;
1700  UINTN                 Index;
1701  EFI_IPSEC_SA_ID       *SaId;
1702  IPSEC_SAD_DATA        *SadData;
1703  IPSEC_RECYCLE_CONTEXT *RecycleContext;
1704  UINT8                 *ProcessBuffer;
1705  UINTN                 BytesCopied;
1706  INTN                  EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4
1707  UINTN                 EspSize;         // Total size of esp wrapped ip payload
1708  UINTN                 IvSize;          // Size of IV, optional, might be 0
1709  UINTN                 PlainPayloadSize;// Original IP payload size
1710  UINTN                 PaddingSize;     // Size of padding
1711  UINTN                 EncryptSize;     // Size of data to be encrypted, start after IV and
1712                                         // stop before ICV
1713  UINTN                 IcvSize;         // Size of ICV, optional, might be 0
1714  UINT8                 *RestOfPayload;  // Start of Payload after IV
1715  UINT8                 *Padding;        // Start address of padding
1716  EFI_ESP_HEADER        *EspHeader;      // Start address of ESP frame
1717  EFI_ESP_TAIL          *EspTail;        // Address behind padding
1718  UINT8                 *InnerHead;
1719  HASH_DATA_FRAGMENT    HashFragment[1];
1720
1721  Status          = EFI_ACCESS_DENIED;
1722  SaId            = SadEntry->Id;
1723  SadData         = SadEntry->Data;
1724  ProcessBuffer   = NULL;
1725  RecycleContext  = NULL;
1726  *RecycleEvent   = NULL;
1727  InnerHead       = NULL;
1728
1729  if (!SadData->ManualSet &&
1730      SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&
1731      SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL
1732      ) {
1733    //
1734    // Invalid manual SAD entry configuration.
1735    //
1736    goto ON_EXIT;
1737  }
1738
1739  //
1740  // Create OutHeader according to Inner Header
1741  //
1742  if (SadData->Mode == EfiIPsecTunnel) {
1743    InnerHead = IpSecTunnelOutboundPacket (
1744                  IpHead,
1745                  IpVersion,
1746                  SadData,
1747                  LastHead,
1748                  OptionsBuffer,
1749                  OptionsLength,
1750                  FragmentTable,
1751                  FragmentCount
1752                  );
1753
1754    if (InnerHead == NULL) {
1755      return EFI_INVALID_PARAMETER;
1756    }
1757
1758  }
1759
1760  //
1761  // Calculate enctrypt block size, need iv by default and 4 bytes alignment.
1762  //
1763  EncryptBlockSize  = 4;
1764
1765  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1766    EncryptBlockSize  = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1767
1768    if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {
1769      goto ON_EXIT;
1770    }
1771  }
1772
1773  //
1774  // Calculate the plain payload size according to the fragment table.
1775  //
1776  PlainPayloadSize = 0;
1777  for (Index = 0; Index < *FragmentCount; Index++) {
1778    PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;
1779  }
1780
1781  //
1782  // Add IPHeader size for Tunnel Mode
1783  //
1784  if (SadData->Mode == EfiIPsecTunnel) {
1785    if (IpVersion == IP_VERSION_4) {
1786      PlainPayloadSize += sizeof (IP4_HEAD);
1787    } else {
1788      PlainPayloadSize += sizeof (EFI_IP6_HEADER);
1789    }
1790    //
1791    // OPtions should be encryption into it
1792    //
1793    PlainPayloadSize += *OptionsLength;
1794  }
1795
1796
1797  //
1798  // Calculate icv size, optional by default and 4 bytes alignment.
1799  //
1800  IcvSize = 0;
1801  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1802    IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
1803    if (IcvSize % 4 != 0) {
1804      goto ON_EXIT;
1805    }
1806  }
1807
1808  //
1809  // Calcuate the total size of esp wrapped ip payload.
1810  //
1811  IvSize        = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
1812  EncryptSize   = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;
1813  PaddingSize   = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);
1814  EspSize       = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;
1815
1816  ProcessBuffer = AllocateZeroPool (EspSize);
1817  if (ProcessBuffer == NULL) {
1818    Status = EFI_OUT_OF_RESOURCES;
1819    goto ON_EXIT;
1820  }
1821
1822  //
1823  // Calculate esp header and esp tail including header, payload and padding.
1824  //
1825  EspHeader     = (EFI_ESP_HEADER *) ProcessBuffer;
1826  RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;
1827  Padding       = RestOfPayload + PlainPayloadSize;
1828  EspTail       = (EFI_ESP_TAIL *) (Padding + PaddingSize);
1829
1830  //
1831  // Fill the sn and spi fields in esp header.
1832  //
1833  EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);
1834  //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);
1835  EspHeader->Spi            = HTONL (SaId->Spi);
1836
1837  //
1838  // Copy the rest of payload (after iv) from the original fragment buffer.
1839  //
1840  BytesCopied = 0;
1841
1842  //
1843  // For Tunnel Mode
1844  //
1845  if (SadData->Mode == EfiIPsecTunnel) {
1846    if (IpVersion == IP_VERSION_4) {
1847      //
1848      // HeadLen, Total Length
1849      //
1850      ((IP4_HEAD *)InnerHead)->HeadLen  = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2);
1851      ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize);
1852      ((IP4_HEAD *)InnerHead)->Checksum = 0;
1853      ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum (
1854                                                  (UINT8 *)InnerHead,
1855                                                  sizeof(IP4_HEAD)
1856                                                  ));
1857      CopyMem (
1858        RestOfPayload + BytesCopied,
1859        InnerHead,
1860        sizeof (IP4_HEAD) + *OptionsLength
1861        );
1862      BytesCopied += sizeof (IP4_HEAD) + *OptionsLength;
1863
1864    } else {
1865    ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER)));
1866      CopyMem (
1867        RestOfPayload + BytesCopied,
1868        InnerHead,
1869        sizeof (EFI_IP6_HEADER) + *OptionsLength
1870        );
1871      BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength;
1872    }
1873  }
1874
1875  for (Index = 0; Index < *FragmentCount; Index++) {
1876    CopyMem (
1877      (RestOfPayload + BytesCopied),
1878      (*FragmentTable)[Index].FragmentBuffer,
1879      (*FragmentTable)[Index].FragmentLength
1880      );
1881    BytesCopied += (*FragmentTable)[Index].FragmentLength;
1882  }
1883  //
1884  // Fill the padding buffer by natural number sequence.
1885  //
1886  for (Index = 0; Index < PaddingSize; Index++) {
1887    Padding[Index] = (UINT8) (Index + 1);
1888  }
1889  //
1890  // Fill the padding length and next header fields in esp tail.
1891  //
1892  EspTail->PaddingLength  = (UINT8) PaddingSize;
1893  EspTail->NextHeader     = *LastHead;
1894
1895  //
1896  // Fill the next header for Tunnel mode.
1897  //
1898  if (SadData->Mode == EfiIPsecTunnel) {
1899    if (IpVersion == IP_VERSION_4) {
1900      EspTail->NextHeader = 4;
1901    } else {
1902      EspTail->NextHeader = 41;
1903    }
1904  }
1905
1906  //
1907  // Generate iv at random by crypt library.
1908  //
1909  Status = IpSecGenerateIv (
1910             (UINT8 *) (EspHeader + 1),
1911             IvSize
1912             );
1913
1914
1915  if (EFI_ERROR (Status)) {
1916    goto ON_EXIT;
1917  }
1918
1919  //
1920  // Encryption the payload (after iv) by the SAD entry if has encrypt key.
1921  //
1922  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
1923    Status = IpSecCryptoIoEncrypt (
1924               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
1925               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
1926               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
1927               (UINT8 *)(EspHeader + 1),
1928               RestOfPayload,
1929               EncryptSize,
1930               RestOfPayload
1931               );
1932
1933    if (EFI_ERROR (Status)) {
1934      goto ON_EXIT;
1935    }
1936  }
1937
1938  //
1939  // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.
1940  //
1941  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
1942
1943    HashFragment[0].Data     = ProcessBuffer;
1944    HashFragment[0].DataSize = EspSize - IcvSize;
1945    Status = IpSecCryptoIoHmac (
1946               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,
1947               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
1948               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,
1949               HashFragment,
1950               1,
1951               ProcessBuffer + EspSize - IcvSize,
1952               IcvSize
1953               );
1954    if (EFI_ERROR (Status)) {
1955      goto ON_EXIT;
1956    }
1957  }
1958
1959  //
1960  // Encryption and authentication with esp has been done, so it's time to
1961  // reload the new packet, create recycle event and fixup ip header.
1962  //
1963  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
1964  if (RecycleContext == NULL) {
1965    Status = EFI_OUT_OF_RESOURCES;
1966    goto ON_EXIT;
1967  }
1968
1969  Status = gBS->CreateEvent (
1970                  EVT_NOTIFY_SIGNAL,
1971                  TPL_NOTIFY,
1972                  IpSecRecycleCallback,
1973                  RecycleContext,
1974                  RecycleEvent
1975                  );
1976  if (EFI_ERROR (Status)) {
1977    goto ON_EXIT;
1978  }
1979  //
1980  // Caller take responsible to handle the original fragment table.
1981  //
1982  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
1983  if (*FragmentTable == NULL) {
1984    Status = EFI_OUT_OF_RESOURCES;
1985    goto ON_EXIT;
1986  }
1987
1988  RecycleContext->FragmentTable       = *FragmentTable;
1989  RecycleContext->PayloadBuffer       = ProcessBuffer;
1990  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer;
1991  (*FragmentTable)[0].FragmentLength  = (UINT32) EspSize;
1992  *FragmentCount                      = 1;
1993
1994  //
1995  // Update the total length field in ip header since processed by esp.
1996  //
1997  if (IpVersion == IP_VERSION_4) {
1998    ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize));
1999  } else {
2000    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);
2001  }
2002
2003  //
2004  // If tunnel mode, it should change the outer Ip header with tunnel source address
2005  // and destination tunnel address.
2006  //
2007  if (SadData->Mode == EfiIPsecTunnel) {
2008    if (IpVersion == IP_VERSION_4) {
2009      CopyMem (
2010        &((IP4_HEAD *) IpHead)->Src,
2011        &SadData->TunnelSourceAddress.v4,
2012        sizeof (EFI_IPv4_ADDRESS)
2013        );
2014      CopyMem (
2015        &((IP4_HEAD *) IpHead)->Dst,
2016        &SadData->TunnelDestAddress.v4,
2017        sizeof (EFI_IPv4_ADDRESS)
2018        );
2019    } else {
2020      CopyMem (
2021        &((EFI_IP6_HEADER *) IpHead)->SourceAddress,
2022        &SadData->TunnelSourceAddress.v6,
2023        sizeof (EFI_IPv6_ADDRESS)
2024        );
2025      CopyMem (
2026        &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
2027        &SadData->TunnelDestAddress.v6,
2028        sizeof (EFI_IPv6_ADDRESS)
2029        );
2030    }
2031  }
2032
2033  //
2034  // Update the next layer field in ip header since esp header inserted.
2035  //
2036  *LastHead = IPSEC_ESP_PROTOCOL;
2037
2038  //
2039  // Increase the sn number in SAD entry according to rfc4303.
2040  //
2041  SadData->SequenceNumber++;
2042
2043ON_EXIT:
2044  if (EFI_ERROR (Status)) {
2045    if (ProcessBuffer != NULL) {
2046      FreePool (ProcessBuffer);
2047    }
2048
2049    if (RecycleContext != NULL) {
2050      FreePool (RecycleContext);
2051    }
2052
2053    if (*RecycleEvent != NULL) {
2054      gBS->CloseEvent (*RecycleEvent);
2055    }
2056  }
2057
2058  return Status;
2059}
2060
2061/**
2062  This function processes the inbound traffic with IPsec.
2063
2064  It checks the received packet security property, trims the ESP/AH header, and then
2065  returns without an IPsec protected IP Header and FragmentTable.
2066
2067  @param[in]      IpVersion          The version of IP.
2068  @param[in, out] IpHead             Points to IP header containing the ESP/AH header
2069                                     to be trimed on input, and without ESP/AH header
2070                                     on return.
2071  @param[in, out] LastHead           The Last Header in IP header on return.
2072  @param[in, out] OptionsBuffer      Pointer to the options buffer.
2073  @param[in, out] OptionsLength      Length of the options buffer.
2074  @param[in, out] FragmentTable      Pointer to a list of fragments in form of IPsec
2075                                     protected on input, and without IPsec protected
2076                                     on return.
2077  @param[in, out] FragmentCount      The number of fragments.
2078  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.
2079  @param[out]     RecycleEvent       The event for recycling of resources.
2080
2081  @retval EFI_SUCCESS              The operation was successful.
2082  @retval EFI_UNSUPPORTED          The IPSEC protocol is not supported.
2083
2084**/
2085EFI_STATUS
2086IpSecProtectInboundPacket (
2087  IN     UINT8                       IpVersion,
2088  IN OUT VOID                        *IpHead,
2089  IN OUT UINT8                       *LastHead,
2090  IN OUT VOID                        **OptionsBuffer,
2091  IN OUT UINT32                      *OptionsLength,
2092  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
2093  IN OUT UINT32                      *FragmentCount,
2094     OUT EFI_IPSEC_SPD_SELECTOR      **SpdEntry,
2095     OUT EFI_EVENT                   *RecycleEvent
2096  )
2097{
2098  if (*LastHead == IPSEC_ESP_PROTOCOL) {
2099    //
2100    // Process the esp ipsec header of the inbound traffic.
2101    //
2102    return IpSecEspInboundPacket (
2103             IpVersion,
2104             IpHead,
2105             LastHead,
2106             OptionsBuffer,
2107             OptionsLength,
2108             FragmentTable,
2109             FragmentCount,
2110             SpdEntry,
2111             RecycleEvent
2112             );
2113  }
2114  //
2115  // The other protocols are not supported.
2116  //
2117  return EFI_UNSUPPORTED;
2118}
2119
2120/**
2121  This fucntion processes the output traffic with IPsec.
2122
2123  It protected the sending packet by encrypting it payload and inserting ESP/AH header
2124  in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.
2125
2126  @param[in]      IpVersion          The version of IP.
2127  @param[in, out] IpHead             Point to IP header containing the orginal IP header
2128                                     to be processed on input, and inserted ESP/AH header
2129                                     on return.
2130  @param[in, out] LastHead           The Last Header in IP header.
2131  @param[in, out] OptionsBuffer      Pointer to the options buffer.
2132  @param[in, out] OptionsLength      Length of the options buffer.
2133  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
2134                                     IPsec on input, and with IPsec protected
2135                                     on return.
2136  @param[in, out] FragmentCount      Number of fragments.
2137  @param[in]      SadEntry           Related SAD entry.
2138  @param[out]     RecycleEvent       Event for recycling of resources.
2139
2140  @retval EFI_SUCCESS              The operation is successful.
2141  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.
2142
2143**/
2144EFI_STATUS
2145IpSecProtectOutboundPacket (
2146  IN     UINT8                       IpVersion,
2147  IN OUT VOID                        *IpHead,
2148  IN OUT UINT8                       *LastHead,
2149  IN OUT VOID                        **OptionsBuffer,
2150  IN OUT UINT32                      *OptionsLength,
2151  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
2152  IN OUT UINT32                      *FragmentCount,
2153  IN     IPSEC_SAD_ENTRY             *SadEntry,
2154     OUT EFI_EVENT                   *RecycleEvent
2155  )
2156{
2157  if (SadEntry->Id->Proto == EfiIPsecESP) {
2158    //
2159    // Process the esp ipsec header of the outbound traffic.
2160    //
2161    return IpSecEspOutboundPacket (
2162             IpVersion,
2163             IpHead,
2164             LastHead,
2165             OptionsBuffer,
2166             OptionsLength,
2167             FragmentTable,
2168             FragmentCount,
2169             SadEntry,
2170             RecycleEvent
2171             );
2172  }
2173  //
2174  // The other protocols are not supported.
2175  //
2176  return EFI_UNSUPPORTED;
2177}
2178