2 The implementation of IPsec.
4 (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "IpSecImpl.h"
12 #include "IkeService.h"
13 #include "IpSecDebug.h"
14 #include "IpSecCryptIo.h"
15 #include "IpSecConfigImpl.h"
18 Check if the specified Address is the Valid Address Range.
20 This function checks if the bytes after prefixed length are all Zero in this
21 Address. This Address is supposed to point to a range address. That means it
22 should gives the correct prefixed address and the bytes outside the prefixed are
25 @param[in] IpVersion The IP version.
26 @param[in] Address Points to EFI_IP_ADDRESS to be checked.
27 @param[in] PrefixLength The PrefixeLength of this address.
29 @retval TRUE The address is a vaild address range.
30 @retval FALSE The address is not a vaild address range.
34 IpSecValidAddressRange (
36 IN EFI_IP_ADDRESS
*Address
,
45 EFI_IP_ADDRESS ZeroAddr
;
47 if (PrefixLength
== 0) {
51 AddrLen
= (UINT8
) ((IpVersion
== IP_VERSION_4
) ? 32 : 128);
53 if (AddrLen
<= PrefixLength
) {
57 Div
= (UINT8
) (PrefixLength
/ 8);
58 Mod
= (UINT8
) (PrefixLength
% 8);
59 Addr
= (UINT8
*) Address
;
60 ZeroMem (&ZeroAddr
, sizeof (EFI_IP_ADDRESS
));
63 // Check whether the mod part of host scope is zero or not.
66 Mask
= (UINT8
) (0xFF << (8 - Mod
));
68 if ((Addr
[Div
] | Mask
) != Mask
) {
75 // Check whether the div part of host scope is zero or not.
80 sizeof (EFI_IP_ADDRESS
) - Div
89 Extrct the Address Range from a Address.
91 This function keep the prefix address and zero other part address.
93 @param[in] Address Point to a specified address.
94 @param[in] PrefixLength The prefix length.
95 @param[out] Range Contain the return Address Range.
99 IpSecExtractAddressRange (
100 IN EFI_IP_ADDRESS
*Address
,
101 IN UINT8 PrefixLength
,
102 OUT EFI_IP_ADDRESS
*Range
110 if (PrefixLength
== 0) {
114 Div
= (UINT8
) (PrefixLength
/ 8);
115 Mod
= (UINT8
) (PrefixLength
% 8);
116 Addr
= (UINT8
*) Range
;
118 CopyMem (Range
, Address
, sizeof (EFI_IP_ADDRESS
));
121 // Zero the mod part of host scope.
124 Mask
= (UINT8
) (0xFF << (8 - Mod
));
125 Addr
[Div
] = (UINT8
) (Addr
[Div
] & Mask
);
129 // Zero the div part of host scope.
131 ZeroMem (&Addr
[Div
], sizeof (EFI_IP_ADDRESS
) - Div
);
136 Checks if the IP Address in the address range of AddressInfos specified.
138 @param[in] IpVersion The IP version.
139 @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.
140 @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check
141 the IP Address is matched.
142 @param[in] AddressCount The total numbers of the AddressInfo.
144 @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.
145 @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.
149 IpSecMatchIpAddress (
151 IN EFI_IP_ADDRESS
*IpAddr
,
152 IN EFI_IP_ADDRESS_INFO
*AddressInfo
,
153 IN UINT32 AddressCount
156 EFI_IP_ADDRESS Range
;
162 for (Index
= 0; Index
< AddressCount
; Index
++) {
164 // Check whether the target address is in the address range
165 // if it's a valid range of address.
167 if (IpSecValidAddressRange (
169 &AddressInfo
[Index
].Address
,
170 AddressInfo
[Index
].PrefixLength
173 // Get the range of the target address belongs to.
175 ZeroMem (&Range
, sizeof (EFI_IP_ADDRESS
));
176 IpSecExtractAddressRange (
178 AddressInfo
[Index
].PrefixLength
,
184 &AddressInfo
[Index
].Address
,
185 sizeof (EFI_IP_ADDRESS
)
188 // The target address is in the address range.
197 &AddressInfo
[Index
].Address
,
198 sizeof (EFI_IP_ADDRESS
)
201 // The target address is exact same as the address.
211 Check if the specified Protocol and Prot is supported by the specified SPD Entry.
213 This function is the subfunction of IPsecLookUpSpdEntry() that is used to
214 check if the sent/received IKE packet has the related SPD entry support.
216 @param[in] Protocol The Protocol to be checked.
217 @param[in] IpPayload Point to IP Payload to be check.
218 @param[in] SpdProtocol The Protocol supported by SPD.
219 @param[in] SpdLocalPort The Local Port in SPD.
220 @param[in] SpdRemotePort The Remote Port in SPD.
221 @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.
223 @retval TRUE The Protocol and Port are supported by the SPD Entry.
224 @retval FALSE The Protocol and Port are not supported by the SPD Entry.
228 IpSecMatchNextLayerProtocol (
231 IN UINT16 SpdProtocol
,
232 IN UINT16 SpdLocalPort
,
233 IN UINT16 SpdRemotePort
,
234 IN BOOLEAN IsOutbound
239 if (SpdProtocol
== EFI_IPSEC_ANY_PROTOCOL
) {
245 if (SpdProtocol
== Protocol
) {
247 case EFI_IP_PROTO_UDP
:
248 case EFI_IP_PROTO_TCP
:
250 // For udp and tcp, (0, 0) means no need to check local and remote
251 // port. The payload is passed from upper level, which means it should
252 // be in network order.
254 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
255 IsMatch
= (BOOLEAN
) (IsMatch
||
258 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->SrcPort
) == SpdLocalPort
&&
259 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->DstPort
) == SpdRemotePort
263 IsMatch
= (BOOLEAN
) (IsMatch
||
266 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->DstPort
) == SpdLocalPort
&&
267 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->SrcPort
) == SpdRemotePort
272 case EFI_IP_PROTO_ICMP
:
274 // For icmpv4, type code is replaced with local port and remote port,
275 // and (0, 0) means no need to check.
277 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
278 IsMatch
= (BOOLEAN
) (IsMatch
||
279 (BOOLEAN
) (((IP4_ICMP_HEAD
*) IpPayload
)->Type
== SpdLocalPort
&&
280 ((IP4_ICMP_HEAD
*) IpPayload
)->Code
== SpdRemotePort
287 // For icmpv6, type code is replaced with local port and remote port,
288 // and (0, 0) means no need to check.
290 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
292 IsMatch
= (BOOLEAN
) (IsMatch
||
293 (BOOLEAN
) (((IP6_ICMP_HEAD
*) IpPayload
)->Type
== SpdLocalPort
&&
294 ((IP6_ICMP_HEAD
*) IpPayload
)->Code
== SpdRemotePort
309 Find the SAD through a specified SPD's SAD list.
311 @param[in] SadList SAD list related to a specified SPD entry.
312 @param[in] DestAddress The destination address used to find the SAD entry.
313 @param[in] IpVersion The IP version. Ip4 or Ip6.
315 @return The pointer to a certain SAD entry.
319 IpSecLookupSadBySpd (
320 IN LIST_ENTRY
*SadList
,
321 IN EFI_IP_ADDRESS
*DestAddress
,
326 IPSEC_SAD_ENTRY
*SadEntry
;
328 NET_LIST_FOR_EACH (Entry
, SadList
) {
330 SadEntry
= IPSEC_SAD_ENTRY_FROM_SPD (Entry
);
332 // Find the right SAD entry which contains the appointed dest address.
334 if (IpSecMatchIpAddress (
337 SadEntry
->Data
->SpdSelector
->RemoteAddress
,
338 SadEntry
->Data
->SpdSelector
->RemoteAddressCount
348 Find the SAD through whole SAD list.
350 @param[in] Spi The SPI used to search the SAD entry.
351 @param[in] DestAddress The destination used to search the SAD entry.
352 @param[in] IpVersion The IP version. Ip4 or Ip6.
354 @return the pointer to a certain SAD entry.
358 IpSecLookupSadBySpi (
360 IN EFI_IP_ADDRESS
*DestAddress
,
366 IPSEC_SAD_ENTRY
*SadEntry
;
368 SadList
= &mConfigData
[IPsecConfigDataTypeSad
];
370 NET_LIST_FOR_EACH (Entry
, SadList
) {
372 SadEntry
= IPSEC_SAD_ENTRY_FROM_LIST (Entry
);
375 // Find the right SAD entry which contain the appointed spi and dest addr.
377 if (SadEntry
->Id
->Spi
== Spi
) {
378 if (SadEntry
->Data
->Mode
== EfiIPsecTunnel
) {
381 &SadEntry
->Data
->TunnelDestAddress
,
382 sizeof (EFI_IP_ADDRESS
)
387 if (SadEntry
->Data
->SpdSelector
!= NULL
&&
388 IpSecMatchIpAddress (
391 SadEntry
->Data
->SpdSelector
->RemoteAddress
,
392 SadEntry
->Data
->SpdSelector
->RemoteAddressCount
404 Look up if there is existing SAD entry for specified IP packet sending.
406 This function is called by the IPsecProcess when there is some IP packet needed to
407 send out. This function checks if there is an existing SAD entry that can be serviced
408 to this IP packet sending. If no existing SAD entry could be used, this
409 function will invoke an IPsec Key Exchange Negotiation.
411 @param[in] Private Points to private data.
412 @param[in] NicHandle Points to a NIC handle.
413 @param[in] IpVersion The version of IP.
414 @param[in] IpHead The IP Header of packet to be sent out.
415 @param[in] IpPayload The IP Payload to be sent out.
416 @param[in] OldLastHead The Last protocol of the IP packet.
417 @param[in] SpdEntry Points to a related SPD entry.
418 @param[out] SadEntry Contains the Point of a related SAD entry.
420 @retval EFI_DEVICE_ERROR One of following conditions is TRUE:
421 - If don't find related UDP service.
422 - Sequence Number is used up.
423 - Extension Sequence Number is used up.
424 @retval EFI_NOT_READY No existing SAD entry could be used.
425 @retval EFI_SUCCESS Find the related SAD entry.
429 IpSecLookupSadEntry (
430 IN IPSEC_PRIVATE_DATA
*Private
,
431 IN EFI_HANDLE NicHandle
,
435 IN UINT8 OldLastHead
,
436 IN IPSEC_SPD_ENTRY
*SpdEntry
,
437 OUT IPSEC_SAD_ENTRY
**SadEntry
440 IKE_UDP_SERVICE
*UdpService
;
441 IPSEC_SAD_ENTRY
*Entry
;
442 IPSEC_SAD_DATA
*Data
;
443 EFI_IP_ADDRESS DestIp
;
447 UdpService
= IkeLookupUdp (Private
, NicHandle
, IpVersion
);
449 if (UdpService
== NULL
) {
450 return EFI_DEVICE_ERROR
;
453 // Parse the destination address from ip header.
455 ZeroMem (&DestIp
, sizeof (EFI_IP_ADDRESS
));
456 if (IpVersion
== IP_VERSION_4
) {
459 &((IP4_HEAD
*) IpHead
)->Dst
,
465 &((EFI_IP6_HEADER
*) IpHead
)->DestinationAddress
,
466 sizeof (EFI_IP_ADDRESS
)
471 // Find the SAD entry in the spd.sas list according to the dest address.
473 Entry
= IpSecLookupSadBySpd (&SpdEntry
->Data
->Sas
, &DestIp
, IpVersion
);
476 if (OldLastHead
!= IP6_ICMP
||
477 (OldLastHead
== IP6_ICMP
&& *IpPayload
== ICMP_V6_ECHO_REQUEST
)
480 // Start ike negotiation process except the request packet of ping.
482 if (SpdEntry
->Data
->ProcessingPolicy
->Mode
== EfiIPsecTunnel
) {
486 &SpdEntry
->Data
->ProcessingPolicy
->TunnelOption
->RemoteTunnelAddress
498 return EFI_NOT_READY
;
503 if (!Data
->ManualSet
) {
504 if (Data
->ESNEnabled
) {
506 // Validate the 64bit sn number if 64bit sn enabled.
508 if ((UINT64
) (Data
->SequenceNumber
+ 1) == 0) {
510 // TODO: Re-negotiate SA
512 return EFI_DEVICE_ERROR
;
516 // Validate the 32bit sn number if 64bit sn disabled.
518 SeqNum32
= (UINT32
) Data
->SequenceNumber
;
519 if ((UINT32
) (SeqNum32
+ 1) == 0) {
521 // TODO: Re-negotiate SA
523 return EFI_DEVICE_ERROR
;
534 Find a PAD entry according to a remote IP address.
536 @param[in] IpVersion The version of IP.
537 @param[in] IpAddr Points to remote IP address.
539 @return the pointer of related PAD entry.
543 IpSecLookupPadEntry (
545 IN EFI_IP_ADDRESS
*IpAddr
550 EFI_IP_ADDRESS_INFO
*IpAddrInfo
;
551 IPSEC_PAD_ENTRY
*PadEntry
;
553 PadList
= &mConfigData
[IPsecConfigDataTypePad
];
555 for (Entry
= PadList
->ForwardLink
; Entry
!= PadList
; Entry
= Entry
->ForwardLink
) {
557 PadEntry
= IPSEC_PAD_ENTRY_FROM_LIST (Entry
);
558 IpAddrInfo
= &PadEntry
->Id
->Id
.IpAddress
;
560 // Find the right pad entry which contain the appointed dest addr.
562 if (IpSecMatchIpAddress (IpVersion
, IpAddr
, IpAddrInfo
, 1)) {
571 Check if the specified IP packet can be serviced by this SPD entry.
573 @param[in] SpdEntry Point to SPD entry.
574 @param[in] IpVersion Version of IP.
575 @param[in] IpHead Point to IP header.
576 @param[in] IpPayload Point to IP payload.
577 @param[in] Protocol The Last protocol of IP packet.
578 @param[in] IsOutbound Traffic direction.
579 @param[out] Action The support action of SPD entry.
581 @retval EFI_SUCCESS Find the related SPD.
582 @retval EFI_NOT_FOUND Not find the related SPD entry;
586 IpSecLookupSpdEntry (
587 IN IPSEC_SPD_ENTRY
*SpdEntry
,
592 IN BOOLEAN IsOutbound
,
593 OUT EFI_IPSEC_ACTION
*Action
596 EFI_IPSEC_SPD_SELECTOR
*SpdSel
;
599 EFI_IP_ADDRESS SrcAddr
;
600 EFI_IP_ADDRESS DstAddr
;
603 ASSERT (SpdEntry
!= NULL
);
604 SpdSel
= SpdEntry
->Selector
;
605 Ip4
= (IP4_HEAD
*) IpHead
;
606 Ip6
= (EFI_IP6_HEADER
*) IpHead
;
608 ZeroMem (&SrcAddr
, sizeof (EFI_IP_ADDRESS
));
609 ZeroMem (&DstAddr
, sizeof (EFI_IP_ADDRESS
));
612 // Parse the source and destination address from ip header.
614 if (IpVersion
== IP_VERSION_4
) {
615 CopyMem (&SrcAddr
, &Ip4
->Src
, sizeof (IP4_ADDR
));
616 CopyMem (&DstAddr
, &Ip4
->Dst
, sizeof (IP4_ADDR
));
618 CopyMem (&SrcAddr
, &Ip6
->SourceAddress
, sizeof (EFI_IPv6_ADDRESS
));
619 CopyMem (&DstAddr
, &Ip6
->DestinationAddress
, sizeof (EFI_IPv6_ADDRESS
));
622 // Check the local and remote addresses for outbound traffic
624 SpdMatch
= (BOOLEAN
)(IsOutbound
&&
625 IpSecMatchIpAddress (
628 SpdSel
->LocalAddress
,
629 SpdSel
->LocalAddressCount
631 IpSecMatchIpAddress (
634 SpdSel
->RemoteAddress
,
635 SpdSel
->RemoteAddressCount
640 // Check the local and remote addresses for inbound traffic
642 SpdMatch
= (BOOLEAN
) (SpdMatch
||
644 IpSecMatchIpAddress (
647 SpdSel
->LocalAddress
,
648 SpdSel
->LocalAddressCount
650 IpSecMatchIpAddress (
653 SpdSel
->RemoteAddress
,
654 SpdSel
->RemoteAddressCount
659 // Check the next layer protocol and local and remote ports.
661 SpdMatch
= (BOOLEAN
) (SpdMatch
&&
662 IpSecMatchNextLayerProtocol (
665 SpdSel
->NextLayerProtocol
,
674 // Find the right SPD entry if match the 5 key elements.
676 *Action
= SpdEntry
->Data
->Action
;
680 return EFI_NOT_FOUND
;
684 The call back function of NetbufFromExt.
686 @param[in] Arg The argument passed from the caller.
691 IpSecOnRecyclePacket (
698 This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP
701 @param[in] Event The related event.
702 @param[in] Context The data passed by the caller.
707 IpSecRecycleCallback (
712 IPSEC_RECYCLE_CONTEXT
*RecycleContext
;
714 RecycleContext
= (IPSEC_RECYCLE_CONTEXT
*) Context
;
716 if (RecycleContext
->FragmentTable
!= NULL
) {
717 FreePool (RecycleContext
->FragmentTable
);
720 if (RecycleContext
->PayloadBuffer
!= NULL
) {
721 FreePool (RecycleContext
->PayloadBuffer
);
724 FreePool (RecycleContext
);
725 gBS
->CloseEvent (Event
);
730 Calculate the extension hader of IP. The return length only doesn't contain
731 the fixed IP header length.
733 @param[in] IpHead Points to an IP head to be calculated.
734 @param[in] LastHead Points to the last header of the IP header.
736 @return The length of the extension header.
740 IpSecGetPlainExtHeadSize (
747 Size
= (UINT16
) (LastHead
- (UINT8
*) IpHead
);
749 if (Size
> sizeof (EFI_IP6_HEADER
)) {
751 // * (LastHead+1) point the last header's length but not include the first
752 // 8 octers, so this formluation add 8 at the end.
754 Size
= (UINT16
) (Size
- sizeof (EFI_IP6_HEADER
) + *(LastHead
+ 1) + 8);
763 Verify if the Authentication payload is correct.
765 @param[in] EspBuffer Points to the ESP wrapped buffer.
766 @param[in] EspSize The size of the ESP wrapped buffer.
767 @param[in] SadEntry The related SAD entry to store the authentication
769 @param[in] IcvSize The length of ICV.
771 @retval EFI_SUCCESS The authentication data is correct.
772 @retval EFI_ACCESS_DENIED The authentication data is not correct.
776 IpSecEspAuthVerifyPayload (
779 IN IPSEC_SAD_ENTRY
*SadEntry
,
786 HASH_DATA_FRAGMENT HashFragment
[1];
789 // Calculate the size of authentication payload.
791 AuthSize
= EspSize
- IcvSize
;
794 // Calculate the icv buffer and size of the payload.
796 HashFragment
[0].Data
= EspBuffer
;
797 HashFragment
[0].DataSize
= AuthSize
;
799 Status
= IpSecCryptoIoHmac (
800 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthAlgoId
,
801 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthKey
,
802 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthKeyLength
,
808 if (EFI_ERROR (Status
)) {
813 // Compare the calculated icv and the appended original icv.
815 if (CompareMem (EspBuffer
+ AuthSize
, IcvBuffer
, IcvSize
) == 0) {
819 DEBUG ((DEBUG_ERROR
, "Error auth verify payload\n"));
820 return EFI_ACCESS_DENIED
;
824 Search the related SAD entry by the input .
826 @param[in] IpHead The pointer to IP header.
827 @param[in] IpVersion The version of IP (IP4 or IP6).
828 @param[in] Spi The SPI used to search the related SAD entry.
831 @retval NULL Not find the related SAD entry.
832 @retval IPSEC_SAD_ENTRY Return the related SAD entry.
836 IpSecFoundSadFromInboundPacket (
842 EFI_IP_ADDRESS DestIp
;
845 // Parse destination address from ip header.
847 ZeroMem (&DestIp
, sizeof (EFI_IP_ADDRESS
));
848 if (IpVersion
== IP_VERSION_4
) {
851 &((IP4_HEAD
*) IpHead
)->Dst
,
857 &((EFI_IP6_HEADER
*) IpHead
)->DestinationAddress
,
858 sizeof (EFI_IPv6_ADDRESS
)
863 // Lookup SAD entry according to the spi and dest address.
865 return IpSecLookupSadBySpi (Spi
, &DestIp
, IpVersion
);
869 Validate the IP6 extension header format for both the packets we received
870 and that we will transmit.
872 @param[in] NextHeader The next header field in IPv6 basic header.
873 @param[in] ExtHdrs The first bye of the option.
874 @param[in] ExtHdrsLen The length of the whole option.
875 @param[out] LastHeader The pointer of NextHeader of the last extension
876 header processed by IP6.
877 @param[out] RealExtsLen The length of extension headers processed by IP6 layer.
878 This is an optional parameter that may be NULL.
880 @retval TRUE The option is properly formated.
881 @retval FALSE The option is malformated.
885 IpSecIsIp6ExtsValid (
886 IN UINT8
*NextHeader
,
888 IN UINT32 ExtHdrsLen
,
889 OUT UINT8
**LastHeader
,
890 OUT UINT32
*RealExtsLen OPTIONAL
900 if (RealExtsLen
!= NULL
) {
904 *LastHeader
= NextHeader
;
906 if (ExtHdrs
== NULL
&& ExtHdrsLen
== 0) {
910 if ((ExtHdrs
== NULL
&& ExtHdrsLen
!= 0) || (ExtHdrs
!= NULL
&& ExtHdrsLen
== 0)) {
919 while (Pointer
<= ExtHdrsLen
) {
921 switch (*NextHeader
) {
930 case IP6_DESTINATION
:
931 if (*NextHeader
== IP6_DESTINATION
) {
939 NextHeader
= ExtHdrs
+ Pointer
;
942 Option
= ExtHdrs
+ Pointer
;
943 OptionLen
= (UINT8
) ((*Option
+ 1) * 8 - 2);
947 Pointer
= Pointer
+ OptionLen
;
955 // RFC2402, AH header should after fragment header.
961 NextHeader
= ExtHdrs
+ Pointer
;
962 Pointer
= Pointer
+ 8;
970 Option
= ExtHdrs
+ Pointer
;
974 // RFC2402, Payload length is specified in 32-bit words, minus "2".
976 OptionLen
= (UINT8
) ((*Option
+ 2) * 4);
977 Pointer
= Pointer
+ OptionLen
;
981 *LastHeader
= NextHeader
;
982 if (RealExtsLen
!= NULL
) {
983 *RealExtsLen
= Pointer
;
990 *LastHeader
= NextHeader
;
992 if (RealExtsLen
!= NULL
) {
993 *RealExtsLen
= Pointer
;
1000 The actual entry to process the tunnel header and inner header for tunnel mode
1003 This function is the subfunction of IpSecEspInboundPacket(). It change the destination
1004 Ip address to the station address and recalculate the uplayyer's checksum.
1007 @param[in, out] IpHead Points to the IP header containing the ESP header
1008 to be trimed on input, and without ESP header
1010 @param[in] IpPayload The decrypted Ip payload. It start from the inner
1012 @param[in] IpVersion The version of IP.
1013 @param[in] SadData Pointer of the relevant SAD.
1014 @param[in, out] LastHead The Last Header in IP header on return.
1018 IpSecTunnelInboundPacket (
1019 IN OUT UINT8
*IpHead
,
1020 IN UINT8
*IpPayload
,
1022 IN IPSEC_SAD_DATA
*SadData
,
1023 IN OUT UINT8
*LastHead
1026 EFI_UDP_HEADER
*UdpHeader
;
1027 TCP_HEAD
*TcpHeader
;
1029 UINT16 PseudoChecksum
;
1030 UINT16 PacketChecksum
;
1032 IP6_ICMP_HEAD
*Icmp6Head
;
1036 if (IpVersion
== IP_VERSION_4
) {
1038 // Zero OutIP header use this to indicate the input packet is under
1039 // IPsec Tunnel protected.
1046 &((IP4_HEAD
*)IpPayload
)->Dst
,
1047 &SadData
->TunnelDestAddress
.v4
,
1048 sizeof (EFI_IPv4_ADDRESS
)
1052 // Recalculate IpHeader Checksum
1054 if (((IP4_HEAD
*)(IpPayload
))->Checksum
!= 0 ) {
1055 ((IP4_HEAD
*)(IpPayload
))->Checksum
= 0;
1056 ((IP4_HEAD
*)(IpPayload
))->Checksum
= (UINT16
) (~NetblockChecksum (
1058 ((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2
1065 // Recalcualte PseudoChecksum
1067 switch (((IP4_HEAD
*)IpPayload
)->Protocol
) {
1068 case EFI_IP_PROTO_UDP
:
1069 UdpHeader
= (EFI_UDP_HEADER
*)((UINT8
*)IpPayload
+ (((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2));
1070 Checksum
= & UdpHeader
->Checksum
;
1074 case EFI_IP_PROTO_TCP
:
1075 TcpHeader
= (TCP_HEAD
*) ((UINT8
*)IpPayload
+ (((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2));
1076 Checksum
= &TcpHeader
->Checksum
;
1083 PacketChecksum
= NetblockChecksum (
1084 (UINT8
*)IpPayload
+ (((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2),
1085 NTOHS (((IP4_HEAD
*)IpPayload
)->TotalLen
) - (((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2)
1087 PseudoChecksum
= NetPseudoHeadChecksum (
1088 ((IP4_HEAD
*)IpPayload
)->Src
,
1089 ((IP4_HEAD
*)IpPayload
)->Dst
,
1090 ((IP4_HEAD
*)IpPayload
)->Protocol
,
1094 if (Checksum
!= NULL
) {
1095 *Checksum
= NetAddChecksum (PacketChecksum
, PseudoChecksum
);
1096 *Checksum
= (UINT16
) ~(NetAddChecksum (*Checksum
, HTONS((UINT16
)(NTOHS (((IP4_HEAD
*)IpPayload
)->TotalLen
) - (((IP4_HEAD
*)IpPayload
)->HeadLen
<< 2)))));
1100 // Zero OutIP header use this to indicate the input packet is under
1101 // IPsec Tunnel protected.
1105 sizeof (EFI_IP6_HEADER
)
1108 &((EFI_IP6_HEADER
*)IpPayload
)->DestinationAddress
,
1109 &SadData
->TunnelDestAddress
.v6
,
1110 sizeof (EFI_IPv6_ADDRESS
)
1114 // Get the Extension Header and Header length.
1116 IpSecIsIp6ExtsValid (
1117 &((EFI_IP6_HEADER
*)IpPayload
)->NextHeader
,
1118 IpPayload
+ sizeof (EFI_IP6_HEADER
),
1119 ((EFI_IP6_HEADER
*)IpPayload
)->PayloadLength
,
1125 // Recalcualte PseudoChecksum
1127 switch (*LastHead
) {
1128 case EFI_IP_PROTO_UDP
:
1129 UdpHeader
= (EFI_UDP_HEADER
*)((UINT8
*)IpPayload
+ sizeof (EFI_IP6_HEADER
) + OptionLen
);
1130 Checksum
= &UdpHeader
->Checksum
;
1134 case EFI_IP_PROTO_TCP
:
1135 TcpHeader
= (TCP_HEAD
*)(IpPayload
+ sizeof (EFI_IP6_HEADER
) + OptionLen
);
1136 Checksum
= &TcpHeader
->Checksum
;
1141 Icmp6Head
= (IP6_ICMP_HEAD
*) (IpPayload
+ sizeof (EFI_IP6_HEADER
) + OptionLen
);
1142 Checksum
= &Icmp6Head
->Checksum
;
1146 PacketChecksum
= NetblockChecksum (
1147 IpPayload
+ sizeof (EFI_IP6_HEADER
) + OptionLen
,
1148 NTOHS(((EFI_IP6_HEADER
*)IpPayload
)->PayloadLength
) - OptionLen
1150 PseudoChecksum
= NetIp6PseudoHeadChecksum (
1151 &((EFI_IP6_HEADER
*)IpPayload
)->SourceAddress
,
1152 &((EFI_IP6_HEADER
*)IpPayload
)->DestinationAddress
,
1157 if (Checksum
!= NULL
) {
1158 *Checksum
= NetAddChecksum (PacketChecksum
, PseudoChecksum
);
1159 *Checksum
= (UINT16
) ~(NetAddChecksum (
1161 HTONS ((UINT16
)((NTOHS (((EFI_IP6_HEADER
*)(IpPayload
))->PayloadLength
)) - OptionLen
))
1168 The actual entry to create inner header for tunnel mode inbound traffic.
1170 This function is the subfunction of IpSecEspOutboundPacket(). It create
1171 the sending packet by encrypting its payload and inserting ESP header in the orginal
1172 IP header, then return the IpHeader and IPsec protected Fragmentable.
1174 @param[in, out] IpHead Points to IP header containing the orginal IP header
1175 to be processed on input, and inserted ESP header
1177 @param[in] IpVersion The version of IP.
1178 @param[in] SadData The related SAD data.
1179 @param[in, out] LastHead The Last Header in IP header.
1180 @param[in] OptionsBuffer Pointer to the options buffer.
1181 @param[in] OptionsLength Length of the options buffer.
1182 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
1183 IPsec on input, and with IPsec protected
1185 @param[in] FragmentCount The number of fragments.
1189 IpSecTunnelOutboundPacket (
1190 IN OUT UINT8
*IpHead
,
1192 IN IPSEC_SAD_DATA
*SadData
,
1193 IN OUT UINT8
*LastHead
,
1194 IN VOID
**OptionsBuffer
,
1195 IN UINT32
*OptionsLength
,
1196 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
1197 IN UINT32
*FragmentCount
1202 UINT16 PacketChecksum
;
1204 UINT16 PseudoChecksum
;
1205 IP6_ICMP_HEAD
*IcmpHead
;
1208 if (OptionsLength
== NULL
) {
1212 if (IpVersion
== IP_VERSION_4
) {
1213 InnerHead
= AllocateZeroPool (sizeof (IP4_HEAD
) + *OptionsLength
);
1214 if (InnerHead
== NULL
) {
1224 InnerHead
+ sizeof (IP4_HEAD
),
1229 InnerHead
= AllocateZeroPool (sizeof (EFI_IP6_HEADER
) + *OptionsLength
);
1230 if (InnerHead
== NULL
) {
1237 sizeof (EFI_IP6_HEADER
)
1240 InnerHead
+ sizeof (EFI_IP6_HEADER
),
1245 if (OptionsBuffer
!= NULL
) {
1246 if (*OptionsLength
!= 0) {
1248 *OptionsBuffer
= NULL
;
1254 // 2. Reassamlbe Fragment into Packet
1256 Packet
= NetbufFromExt (
1257 (NET_FRAGMENT
*)(*FragmentTable
),
1261 IpSecOnRecyclePacket
,
1264 if (Packet
== NULL
) {
1265 FreePool (InnerHead
);
1270 // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo
1273 switch (*LastHead
) {
1274 case EFI_IP_PROTO_UDP
:
1275 Packet
->Udp
= (EFI_UDP_HEADER
*) NetbufGetByte (Packet
, 0, 0);
1276 ASSERT (Packet
->Udp
!= NULL
);
1277 Checksum
= &Packet
->Udp
->Checksum
;
1281 case EFI_IP_PROTO_TCP
:
1282 Packet
->Tcp
= (TCP_HEAD
*) NetbufGetByte (Packet
, 0, 0);
1283 ASSERT (Packet
->Tcp
!= NULL
);
1284 Checksum
= &Packet
->Tcp
->Checksum
;
1289 IcmpHead
= (IP6_ICMP_HEAD
*) NetbufGetByte (Packet
, 0, NULL
);
1290 ASSERT (IcmpHead
!= NULL
);
1291 Checksum
= &IcmpHead
->Checksum
;
1299 PacketChecksum
= NetbufChecksum (Packet
);
1301 if (IpVersion
== IP_VERSION_4
) {
1303 // Replace the source address of Inner Header.
1306 &((IP4_HEAD
*)InnerHead
)->Src
,
1307 &SadData
->SpdSelector
->LocalAddress
[0].Address
.v4
,
1308 sizeof (EFI_IPv4_ADDRESS
)
1311 PacketChecksum
= NetbufChecksum (Packet
);
1312 PseudoChecksum
= NetPseudoHeadChecksum (
1313 ((IP4_HEAD
*)InnerHead
)->Src
,
1314 ((IP4_HEAD
*)InnerHead
)->Dst
,
1321 // Replace the source address of Inner Header.
1324 &((EFI_IP6_HEADER
*)InnerHead
)->SourceAddress
,
1325 &(SadData
->SpdSelector
->LocalAddress
[0].Address
.v6
),
1326 sizeof (EFI_IPv6_ADDRESS
)
1328 PacketChecksum
= NetbufChecksum (Packet
);
1329 PseudoChecksum
= NetIp6PseudoHeadChecksum (
1330 &((EFI_IP6_HEADER
*)InnerHead
)->SourceAddress
,
1331 &((EFI_IP6_HEADER
*)InnerHead
)->DestinationAddress
,
1337 if (Checksum
!= NULL
) {
1338 *Checksum
= NetAddChecksum (PacketChecksum
, PseudoChecksum
);
1339 *Checksum
= (UINT16
) ~(NetAddChecksum ((UINT16
)*Checksum
, HTONS ((UINT16
) Packet
->TotalSize
)));
1342 if (Packet
!= NULL
) {
1343 NetbufFree (Packet
);
1349 The actual entry to relative function processes the inbound traffic of ESP header.
1351 This function is the subfunction of IpSecProtectInboundPacket(). It checks the
1352 received packet security property and trim the ESP header and then returns without
1353 an IPsec protected IP Header and FramgmentTable.
1355 @param[in] IpVersion The version of IP.
1356 @param[in, out] IpHead Points to the IP header containing the ESP header
1357 to be trimed on input, and without ESP header
1359 @param[out] LastHead The Last Header in IP header on return.
1360 @param[in, out] OptionsBuffer Pointer to the options buffer.
1361 @param[in, out] OptionsLength Length of the options buffer.
1362 @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec
1363 protected on input, and without IPsec protected
1365 @param[in, out] FragmentCount The number of fragments.
1366 @param[out] SpdSelector Pointer to contain the address of SPD selector on return.
1367 @param[out] RecycleEvent The event for recycling of resources.
1369 @retval EFI_SUCCESS The operation was successful.
1370 @retval EFI_ACCESS_DENIED One or more following conditions is TRUE:
1371 - ESP header was not found or mal-format.
1372 - The related SAD entry was not found.
1373 - The related SAD entry does not support the ESP protocol.
1374 @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
1378 IpSecEspInboundPacket (
1380 IN OUT VOID
*IpHead
,
1381 OUT UINT8
*LastHead
,
1382 IN OUT VOID
**OptionsBuffer
,
1383 IN OUT UINT32
*OptionsLength
,
1384 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
1385 IN OUT UINT32
*FragmentCount
,
1386 OUT EFI_IPSEC_SPD_SELECTOR
**SpdSelector
,
1387 OUT EFI_EVENT
*RecycleEvent
1396 UINTN PlainPayloadSize
;
1399 UINT8
*ProcessBuffer
;
1400 EFI_ESP_HEADER
*EspHeader
;
1401 EFI_ESP_TAIL
*EspTail
;
1402 EFI_IPSEC_SA_ID
*SaId
;
1403 IPSEC_SAD_DATA
*SadData
;
1404 IPSEC_SAD_ENTRY
*SadEntry
;
1405 IPSEC_RECYCLE_CONTEXT
*RecycleContext
;
1407 UINT16 IpSecHeadSize
;
1410 Status
= EFI_SUCCESS
;
1412 ProcessBuffer
= NULL
;
1413 RecycleContext
= NULL
;
1414 *RecycleEvent
= NULL
;
1415 PlainPayloadSize
= 0;
1419 // Build netbuf from fragment table first.
1421 Payload
= NetbufFromExt (
1422 (NET_FRAGMENT
*) *FragmentTable
,
1425 sizeof (EFI_ESP_HEADER
),
1426 IpSecOnRecyclePacket
,
1429 if (Payload
== NULL
) {
1430 Status
= EFI_OUT_OF_RESOURCES
;
1435 // Get the esp size and esp header from netbuf.
1437 EspSize
= Payload
->TotalSize
;
1438 EspHeader
= (EFI_ESP_HEADER
*) NetbufGetByte (Payload
, 0, NULL
);
1440 if (EspHeader
== NULL
) {
1441 Status
= EFI_ACCESS_DENIED
;
1446 // Parse destination address from ip header and found the related SAD Entry.
1448 SadEntry
= IpSecFoundSadFromInboundPacket (
1451 NTOHL (EspHeader
->Spi
)
1454 if (SadEntry
== NULL
) {
1455 Status
= EFI_ACCESS_DENIED
;
1459 SaId
= SadEntry
->Id
;
1460 SadData
= SadEntry
->Data
;
1463 // Only support esp protocol currently.
1465 if (SaId
->Proto
!= EfiIPsecESP
) {
1466 Status
= EFI_ACCESS_DENIED
;
1470 if (!SadData
->ManualSet
) {
1472 // TODO: Check SA lifetime and sequence number
1477 // Allocate buffer for decryption and authentication.
1479 ProcessBuffer
= AllocateZeroPool (EspSize
);
1480 if (ProcessBuffer
== NULL
) {
1481 Status
= EFI_OUT_OF_RESOURCES
;
1485 NetbufCopy (Payload
, 0, (UINT32
) EspSize
, ProcessBuffer
);
1488 // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.
1490 IcvSize
= IpSecGetIcvLength (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthAlgoId
);
1491 IvSize
= IpSecGetEncryptIvLength (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
);
1492 BlockSize
= IpSecGetEncryptBlockSize (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
);
1495 // Make sure the ESP packet is not mal-formt.
1496 // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.
1497 // 2. Check whether the left payload size is multiple of IvSize.
1499 MiscSize
= sizeof (EFI_ESP_HEADER
) + IvSize
+ IcvSize
;
1500 if (EspSize
<= (MiscSize
+ sizeof (EFI_ESP_TAIL
))) {
1501 Status
= EFI_ACCESS_DENIED
;
1504 if ((EspSize
- MiscSize
) % BlockSize
!= 0) {
1505 Status
= EFI_ACCESS_DENIED
;
1510 // Authenticate the ESP packet.
1512 if (SadData
->AlgoInfo
.EspAlgoInfo
.AuthKey
!= NULL
) {
1513 Status
= IpSecEspAuthVerifyPayload (
1519 if (EFI_ERROR (Status
)) {
1524 // Decrypt the payload by the SAD entry if it has decrypt key.
1526 if (SadData
->AlgoInfo
.EspAlgoInfo
.EncKey
!= NULL
) {
1527 Status
= IpSecCryptoIoDecrypt (
1528 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
,
1529 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncKey
,
1530 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncKeyLength
<< 3,
1531 ProcessBuffer
+ sizeof (EFI_ESP_HEADER
),
1532 ProcessBuffer
+ sizeof (EFI_ESP_HEADER
) + IvSize
,
1533 EspSize
- sizeof (EFI_ESP_HEADER
) - IvSize
- IcvSize
,
1534 ProcessBuffer
+ sizeof (EFI_ESP_HEADER
) + IvSize
1536 if (EFI_ERROR (Status
)) {
1542 // Parse EspTail and compute the plain payload size.
1544 EspTail
= (EFI_ESP_TAIL
*) (ProcessBuffer
+ EspSize
- IcvSize
- sizeof (EFI_ESP_TAIL
));
1545 PaddingSize
= EspTail
->PaddingLength
;
1546 NextHeader
= EspTail
->NextHeader
;
1548 if (EspSize
<= (MiscSize
+ sizeof (EFI_ESP_TAIL
) + PaddingSize
)) {
1549 Status
= EFI_ACCESS_DENIED
;
1552 PlainPayloadSize
= EspSize
- MiscSize
- sizeof (EFI_ESP_TAIL
) - PaddingSize
;
1555 // TODO: handle anti-replay window
1558 // Decryption and authentication with esp has been done, so it's time to
1559 // reload the new packet, create recycle event and fixup ip header.
1561 RecycleContext
= AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT
));
1562 if (RecycleContext
== NULL
) {
1563 Status
= EFI_OUT_OF_RESOURCES
;
1567 Status
= gBS
->CreateEvent (
1570 IpSecRecycleCallback
,
1574 if (EFI_ERROR (Status
)) {
1579 // The caller will take responsible to handle the original fragment table
1581 *FragmentTable
= AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA
));
1582 if (*FragmentTable
== NULL
) {
1583 Status
= EFI_OUT_OF_RESOURCES
;
1587 RecycleContext
->PayloadBuffer
= ProcessBuffer
;
1588 RecycleContext
->FragmentTable
= *FragmentTable
;
1591 // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out
1593 if (SadData
->Mode
== EfiIPsecTunnel
) {
1594 InnerHead
= ProcessBuffer
+ sizeof (EFI_ESP_HEADER
) + IvSize
;
1595 IpSecTunnelInboundPacket (
1603 if (IpVersion
== IP_VERSION_4
) {
1604 (*FragmentTable
)[0].FragmentBuffer
= InnerHead
;
1605 (*FragmentTable
)[0].FragmentLength
= (UINT32
) PlainPayloadSize
;
1608 (*FragmentTable
)[0].FragmentBuffer
= InnerHead
;
1609 (*FragmentTable
)[0].FragmentLength
= (UINT32
) PlainPayloadSize
;
1612 (*FragmentTable
)[0].FragmentBuffer
= ProcessBuffer
+ sizeof (EFI_ESP_HEADER
) + IvSize
;
1613 (*FragmentTable
)[0].FragmentLength
= (UINT32
) PlainPayloadSize
;
1619 // Update the total length field in ip header since processed by esp.
1621 if (SadData
->Mode
!= EfiIPsecTunnel
) {
1622 if (IpVersion
== IP_VERSION_4
) {
1623 ((IP4_HEAD
*) IpHead
)->TotalLen
= HTONS ((UINT16
) ((((IP4_HEAD
*) IpHead
)->HeadLen
<< 2) + PlainPayloadSize
));
1625 IpSecHeadSize
= IpSecGetPlainExtHeadSize (IpHead
, LastHead
);
1626 ((EFI_IP6_HEADER
*) IpHead
)->PayloadLength
= HTONS ((UINT16
)(IpSecHeadSize
+ PlainPayloadSize
));
1629 // Update the next layer field in ip header since esp header inserted.
1631 *LastHead
= NextHeader
;
1636 // Update the SPD association of the SAD entry.
1638 *SpdSelector
= SadData
->SpdSelector
;
1641 if (Payload
!= NULL
) {
1642 NetbufFree (Payload
);
1645 if (EFI_ERROR (Status
)) {
1646 if (ProcessBuffer
!= NULL
) {
1647 FreePool (ProcessBuffer
);
1650 if (RecycleContext
!= NULL
) {
1651 FreePool (RecycleContext
);
1654 if (*RecycleEvent
!= NULL
) {
1655 gBS
->CloseEvent (*RecycleEvent
);
1663 The actual entry to the relative function processes the output traffic using the ESP protocol.
1665 This function is the subfunction of IpSecProtectOutboundPacket(). It protected
1666 the sending packet by encrypting its payload and inserting ESP header in the orginal
1667 IP header, then return the IpHeader and IPsec protected Fragmentable.
1669 @param[in] IpVersion The version of IP.
1670 @param[in, out] IpHead Points to IP header containing the orginal IP header
1671 to be processed on input, and inserted ESP header
1673 @param[in, out] LastHead The Last Header in IP header.
1674 @param[in, out] OptionsBuffer Pointer to the options buffer.
1675 @param[in, out] OptionsLength Length of the options buffer.
1676 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
1677 IPsec on input, and with IPsec protected
1679 @param[in, out] FragmentCount The number of fragments.
1680 @param[in] SadEntry The related SAD entry.
1681 @param[out] RecycleEvent The event for recycling of resources.
1683 @retval EFI_SUCCESS The operation was successful.
1684 @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.
1688 IpSecEspOutboundPacket (
1690 IN OUT VOID
*IpHead
,
1691 IN OUT UINT8
*LastHead
,
1692 IN OUT VOID
**OptionsBuffer
,
1693 IN OUT UINT32
*OptionsLength
,
1694 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
1695 IN OUT UINT32
*FragmentCount
,
1696 IN IPSEC_SAD_ENTRY
*SadEntry
,
1697 OUT EFI_EVENT
*RecycleEvent
1702 EFI_IPSEC_SA_ID
*SaId
;
1703 IPSEC_SAD_DATA
*SadData
;
1704 IPSEC_RECYCLE_CONTEXT
*RecycleContext
;
1705 UINT8
*ProcessBuffer
;
1707 INTN EncryptBlockSize
;// Size of encryption block, 4 bytes aligned and >= 4
1708 UINTN EspSize
; // Total size of esp wrapped ip payload
1709 UINTN IvSize
; // Size of IV, optional, might be 0
1710 UINTN PlainPayloadSize
;// Original IP payload size
1711 UINTN PaddingSize
; // Size of padding
1712 UINTN EncryptSize
; // Size of data to be encrypted, start after IV and
1714 UINTN IcvSize
; // Size of ICV, optional, might be 0
1715 UINT8
*RestOfPayload
; // Start of Payload after IV
1716 UINT8
*Padding
; // Start address of padding
1717 EFI_ESP_HEADER
*EspHeader
; // Start address of ESP frame
1718 EFI_ESP_TAIL
*EspTail
; // Address behind padding
1720 HASH_DATA_FRAGMENT HashFragment
[1];
1722 Status
= EFI_ACCESS_DENIED
;
1723 SaId
= SadEntry
->Id
;
1724 SadData
= SadEntry
->Data
;
1725 ProcessBuffer
= NULL
;
1726 RecycleContext
= NULL
;
1727 *RecycleEvent
= NULL
;
1730 if (!SadData
->ManualSet
&&
1731 SadData
->AlgoInfo
.EspAlgoInfo
.EncKey
== NULL
&&
1732 SadData
->AlgoInfo
.EspAlgoInfo
.AuthKey
== NULL
1735 // Invalid manual SAD entry configuration.
1741 // Create OutHeader according to Inner Header
1743 if (SadData
->Mode
== EfiIPsecTunnel
) {
1744 InnerHead
= IpSecTunnelOutboundPacket (
1755 if (InnerHead
== NULL
) {
1756 return EFI_INVALID_PARAMETER
;
1762 // Calculate enctrypt block size, need iv by default and 4 bytes alignment.
1764 EncryptBlockSize
= 4;
1766 if (SadData
->AlgoInfo
.EspAlgoInfo
.EncKey
!= NULL
) {
1767 EncryptBlockSize
= IpSecGetEncryptBlockSize (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
);
1769 if (EncryptBlockSize
< 0 || (EncryptBlockSize
!= 1 && EncryptBlockSize
% 4 != 0)) {
1775 // Calculate the plain payload size according to the fragment table.
1777 PlainPayloadSize
= 0;
1778 for (Index
= 0; Index
< *FragmentCount
; Index
++) {
1779 PlainPayloadSize
+= (*FragmentTable
)[Index
].FragmentLength
;
1783 // Add IPHeader size for Tunnel Mode
1785 if (SadData
->Mode
== EfiIPsecTunnel
) {
1786 if (IpVersion
== IP_VERSION_4
) {
1787 PlainPayloadSize
+= sizeof (IP4_HEAD
);
1789 PlainPayloadSize
+= sizeof (EFI_IP6_HEADER
);
1792 // OPtions should be encryption into it
1794 PlainPayloadSize
+= *OptionsLength
;
1799 // Calculate icv size, optional by default and 4 bytes alignment.
1802 if (SadData
->AlgoInfo
.EspAlgoInfo
.AuthKey
!= NULL
) {
1803 IcvSize
= IpSecGetIcvLength (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthAlgoId
);
1804 if (IcvSize
% 4 != 0) {
1810 // Calcuate the total size of esp wrapped ip payload.
1812 IvSize
= IpSecGetEncryptIvLength (SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
);
1813 EncryptSize
= (PlainPayloadSize
+ sizeof (EFI_ESP_TAIL
) + EncryptBlockSize
- 1) / EncryptBlockSize
* EncryptBlockSize
;
1814 PaddingSize
= EncryptSize
- PlainPayloadSize
- sizeof (EFI_ESP_TAIL
);
1815 EspSize
= sizeof (EFI_ESP_HEADER
) + IvSize
+ EncryptSize
+ IcvSize
;
1817 ProcessBuffer
= AllocateZeroPool (EspSize
);
1818 if (ProcessBuffer
== NULL
) {
1819 Status
= EFI_OUT_OF_RESOURCES
;
1824 // Calculate esp header and esp tail including header, payload and padding.
1826 EspHeader
= (EFI_ESP_HEADER
*) ProcessBuffer
;
1827 RestOfPayload
= (UINT8
*) (EspHeader
+ 1) + IvSize
;
1828 Padding
= RestOfPayload
+ PlainPayloadSize
;
1829 EspTail
= (EFI_ESP_TAIL
*) (Padding
+ PaddingSize
);
1832 // Fill the sn and spi fields in esp header.
1834 EspHeader
->SequenceNumber
= HTONL ((UINT32
) SadData
->SequenceNumber
+ 1);
1835 //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);
1836 EspHeader
->Spi
= HTONL (SaId
->Spi
);
1839 // Copy the rest of payload (after iv) from the original fragment buffer.
1846 if (SadData
->Mode
== EfiIPsecTunnel
) {
1847 if (IpVersion
== IP_VERSION_4
) {
1849 // HeadLen, Total Length
1851 ((IP4_HEAD
*)InnerHead
)->HeadLen
= (UINT8
) ((sizeof (IP4_HEAD
) + *OptionsLength
) >> 2);
1852 ((IP4_HEAD
*)InnerHead
)->TotalLen
= HTONS ((UINT16
) PlainPayloadSize
);
1853 ((IP4_HEAD
*)InnerHead
)->Checksum
= 0;
1854 ((IP4_HEAD
*)InnerHead
)->Checksum
= (UINT16
) (~NetblockChecksum (
1859 RestOfPayload
+ BytesCopied
,
1861 sizeof (IP4_HEAD
) + *OptionsLength
1863 BytesCopied
+= sizeof (IP4_HEAD
) + *OptionsLength
;
1866 ((EFI_IP6_HEADER
*)InnerHead
)->PayloadLength
= HTONS ((UINT16
) (PlainPayloadSize
- sizeof (EFI_IP6_HEADER
)));
1868 RestOfPayload
+ BytesCopied
,
1870 sizeof (EFI_IP6_HEADER
) + *OptionsLength
1872 BytesCopied
+= sizeof (EFI_IP6_HEADER
) + *OptionsLength
;
1876 for (Index
= 0; Index
< *FragmentCount
; Index
++) {
1878 (RestOfPayload
+ BytesCopied
),
1879 (*FragmentTable
)[Index
].FragmentBuffer
,
1880 (*FragmentTable
)[Index
].FragmentLength
1882 BytesCopied
+= (*FragmentTable
)[Index
].FragmentLength
;
1885 // Fill the padding buffer by natural number sequence.
1887 for (Index
= 0; Index
< PaddingSize
; Index
++) {
1888 Padding
[Index
] = (UINT8
) (Index
+ 1);
1891 // Fill the padding length and next header fields in esp tail.
1893 EspTail
->PaddingLength
= (UINT8
) PaddingSize
;
1894 EspTail
->NextHeader
= *LastHead
;
1897 // Fill the next header for Tunnel mode.
1899 if (SadData
->Mode
== EfiIPsecTunnel
) {
1900 if (IpVersion
== IP_VERSION_4
) {
1901 EspTail
->NextHeader
= 4;
1903 EspTail
->NextHeader
= 41;
1908 // Generate iv at random by crypt library.
1910 Status
= IpSecGenerateIv (
1911 (UINT8
*) (EspHeader
+ 1),
1916 if (EFI_ERROR (Status
)) {
1921 // Encryption the payload (after iv) by the SAD entry if has encrypt key.
1923 if (SadData
->AlgoInfo
.EspAlgoInfo
.EncKey
!= NULL
) {
1924 Status
= IpSecCryptoIoEncrypt (
1925 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncAlgoId
,
1926 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncKey
,
1927 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.EncKeyLength
<< 3,
1928 (UINT8
*)(EspHeader
+ 1),
1934 if (EFI_ERROR (Status
)) {
1940 // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.
1942 if (SadData
->AlgoInfo
.EspAlgoInfo
.AuthKey
!= NULL
) {
1944 HashFragment
[0].Data
= ProcessBuffer
;
1945 HashFragment
[0].DataSize
= EspSize
- IcvSize
;
1946 Status
= IpSecCryptoIoHmac (
1947 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthAlgoId
,
1948 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthKey
,
1949 SadEntry
->Data
->AlgoInfo
.EspAlgoInfo
.AuthKeyLength
,
1952 ProcessBuffer
+ EspSize
- IcvSize
,
1955 if (EFI_ERROR (Status
)) {
1961 // Encryption and authentication with esp has been done, so it's time to
1962 // reload the new packet, create recycle event and fixup ip header.
1964 RecycleContext
= AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT
));
1965 if (RecycleContext
== NULL
) {
1966 Status
= EFI_OUT_OF_RESOURCES
;
1970 Status
= gBS
->CreateEvent (
1973 IpSecRecycleCallback
,
1977 if (EFI_ERROR (Status
)) {
1981 // Caller take responsible to handle the original fragment table.
1983 *FragmentTable
= AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA
));
1984 if (*FragmentTable
== NULL
) {
1985 Status
= EFI_OUT_OF_RESOURCES
;
1989 RecycleContext
->FragmentTable
= *FragmentTable
;
1990 RecycleContext
->PayloadBuffer
= ProcessBuffer
;
1991 (*FragmentTable
)[0].FragmentBuffer
= ProcessBuffer
;
1992 (*FragmentTable
)[0].FragmentLength
= (UINT32
) EspSize
;
1996 // Update the total length field in ip header since processed by esp.
1998 if (IpVersion
== IP_VERSION_4
) {
1999 ((IP4_HEAD
*) IpHead
)->TotalLen
= HTONS ((UINT16
) ((((IP4_HEAD
*) IpHead
)->HeadLen
<< 2) + EspSize
));
2001 ((EFI_IP6_HEADER
*) IpHead
)->PayloadLength
= (UINT16
) (IpSecGetPlainExtHeadSize (IpHead
, LastHead
) + EspSize
);
2005 // If tunnel mode, it should change the outer Ip header with tunnel source address
2006 // and destination tunnel address.
2008 if (SadData
->Mode
== EfiIPsecTunnel
) {
2009 if (IpVersion
== IP_VERSION_4
) {
2011 &((IP4_HEAD
*) IpHead
)->Src
,
2012 &SadData
->TunnelSourceAddress
.v4
,
2013 sizeof (EFI_IPv4_ADDRESS
)
2016 &((IP4_HEAD
*) IpHead
)->Dst
,
2017 &SadData
->TunnelDestAddress
.v4
,
2018 sizeof (EFI_IPv4_ADDRESS
)
2022 &((EFI_IP6_HEADER
*) IpHead
)->SourceAddress
,
2023 &SadData
->TunnelSourceAddress
.v6
,
2024 sizeof (EFI_IPv6_ADDRESS
)
2027 &((EFI_IP6_HEADER
*) IpHead
)->DestinationAddress
,
2028 &SadData
->TunnelDestAddress
.v6
,
2029 sizeof (EFI_IPv6_ADDRESS
)
2035 // Update the next layer field in ip header since esp header inserted.
2037 *LastHead
= IPSEC_ESP_PROTOCOL
;
2040 // Increase the sn number in SAD entry according to rfc4303.
2042 SadData
->SequenceNumber
++;
2045 if (EFI_ERROR (Status
)) {
2046 if (ProcessBuffer
!= NULL
) {
2047 FreePool (ProcessBuffer
);
2050 if (RecycleContext
!= NULL
) {
2051 FreePool (RecycleContext
);
2054 if (*RecycleEvent
!= NULL
) {
2055 gBS
->CloseEvent (*RecycleEvent
);
2063 This function processes the inbound traffic with IPsec.
2065 It checks the received packet security property, trims the ESP/AH header, and then
2066 returns without an IPsec protected IP Header and FragmentTable.
2068 @param[in] IpVersion The version of IP.
2069 @param[in, out] IpHead Points to IP header containing the ESP/AH header
2070 to be trimed on input, and without ESP/AH header
2072 @param[in, out] LastHead The Last Header in IP header on return.
2073 @param[in, out] OptionsBuffer Pointer to the options buffer.
2074 @param[in, out] OptionsLength Length of the options buffer.
2075 @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec
2076 protected on input, and without IPsec protected
2078 @param[in, out] FragmentCount The number of fragments.
2079 @param[out] SpdEntry Pointer to contain the address of SPD entry on return.
2080 @param[out] RecycleEvent The event for recycling of resources.
2082 @retval EFI_SUCCESS The operation was successful.
2083 @retval EFI_UNSUPPORTED The IPSEC protocol is not supported.
2087 IpSecProtectInboundPacket (
2089 IN OUT VOID
*IpHead
,
2090 IN OUT UINT8
*LastHead
,
2091 IN OUT VOID
**OptionsBuffer
,
2092 IN OUT UINT32
*OptionsLength
,
2093 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
2094 IN OUT UINT32
*FragmentCount
,
2095 OUT EFI_IPSEC_SPD_SELECTOR
**SpdEntry
,
2096 OUT EFI_EVENT
*RecycleEvent
2099 if (*LastHead
== IPSEC_ESP_PROTOCOL
) {
2101 // Process the esp ipsec header of the inbound traffic.
2103 return IpSecEspInboundPacket (
2116 // The other protocols are not supported.
2118 return EFI_UNSUPPORTED
;
2122 This fucntion processes the output traffic with IPsec.
2124 It protected the sending packet by encrypting it payload and inserting ESP/AH header
2125 in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.
2127 @param[in] IpVersion The version of IP.
2128 @param[in, out] IpHead Point to IP header containing the orginal IP header
2129 to be processed on input, and inserted ESP/AH header
2131 @param[in, out] LastHead The Last Header in IP header.
2132 @param[in, out] OptionsBuffer Pointer to the options buffer.
2133 @param[in, out] OptionsLength Length of the options buffer.
2134 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
2135 IPsec on input, and with IPsec protected
2137 @param[in, out] FragmentCount Number of fragments.
2138 @param[in] SadEntry Related SAD entry.
2139 @param[out] RecycleEvent Event for recycling of resources.
2141 @retval EFI_SUCCESS The operation is successful.
2142 @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.
2146 IpSecProtectOutboundPacket (
2148 IN OUT VOID
*IpHead
,
2149 IN OUT UINT8
*LastHead
,
2150 IN OUT VOID
**OptionsBuffer
,
2151 IN OUT UINT32
*OptionsLength
,
2152 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
2153 IN OUT UINT32
*FragmentCount
,
2154 IN IPSEC_SAD_ENTRY
*SadEntry
,
2155 OUT EFI_EVENT
*RecycleEvent
2158 if (SadEntry
->Id
->Proto
== EfiIPsecESP
) {
2160 // Process the esp ipsec header of the outbound traffic.
2162 return IpSecEspOutboundPacket (
2175 // The other protocols are not supported.
2177 return EFI_UNSUPPORTED
;