2 The implementation of IPsec Protocol
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "IpSecConfigImpl.h"
18 EFI_IPSEC2_PROTOCOL mIpSecInstance
= { IpSecProcess
, NULL
, TRUE
};
20 extern LIST_ENTRY mConfigData
[IPsecConfigDataTypeMaximum
];
23 Check if the specified Address is the Valid Address Range.
25 This function checks if the bytes after prefixed length are all Zero in this
26 Address. This Address is supposed to point to a range address, meaning it only
27 gives the correct prefixed address.
29 @param[in] IpVersion The IP version.
30 @param[in] Address Points to EFI_IP_ADDRESS to be checked.
31 @param[in] PrefixLength The PrefixeLength of this address.
33 @retval TRUE The address is a vaild address range.
34 @retval FALSE The address is not a vaild address range.
38 IpSecValidAddressRange (
40 IN EFI_IP_ADDRESS
*Address
,
49 EFI_IP_ADDRESS ZeroAddr
;
51 if (PrefixLength
== 0) {
55 AddrLen
= (UINT8
) ((IpVersion
== IP_VERSION_4
) ? 32 : 128);
57 if (AddrLen
<= PrefixLength
) {
61 Div
= (UINT8
) (PrefixLength
/ 8);
62 Mod
= (UINT8
) (PrefixLength
% 8);
63 Addr
= (UINT8
*) Address
;
64 ZeroMem (&ZeroAddr
, sizeof (EFI_IP_ADDRESS
));
67 // Check whether the mod part of host scope is zero or not.
70 Mask
= (UINT8
) (0xFF << (8 - Mod
));
72 if ((Addr
[Div
] | Mask
) != Mask
) {
79 // Check whether the div part of host scope is zero or not.
84 sizeof (EFI_IP_ADDRESS
) - Div
93 Extrct the Address Range from a Address.
95 This function keep the prefix address and zero other part address.
97 @param[in] Address Point to a specified address.
98 @param[in] PrefixLength The prefix length.
99 @param[out] Range Contain the return Address Range.
103 IpSecExtractAddressRange (
104 IN EFI_IP_ADDRESS
*Address
,
105 IN UINT8 PrefixLength
,
106 OUT EFI_IP_ADDRESS
*Range
114 if (PrefixLength
== 0) {
118 Div
= (UINT8
) (PrefixLength
/ 8);
119 Mod
= (UINT8
) (PrefixLength
% 8);
120 Addr
= (UINT8
*) Range
;
122 CopyMem (Range
, Address
, sizeof (EFI_IP_ADDRESS
));
125 // Zero the mod part of host scope.
128 Mask
= (UINT8
) (0xFF << (8 - Mod
));
129 Addr
[Div
] = (UINT8
) (Addr
[Div
] & Mask
);
133 // Zero the div part of host scope.
135 ZeroMem (&Addr
[Div
], sizeof (EFI_IP_ADDRESS
) - Div
);
140 Checks if the IP Address in the address range of AddressInfos specified.
142 @param[in] IpVersion The IP version.
143 @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.
144 @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check
145 the IP Address is matched.
146 @param[in] AddressCount The total numbers of the AddressInfo.
148 @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.
149 @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.
153 IpSecMatchIpAddress (
155 IN EFI_IP_ADDRESS
*IpAddr
,
156 IN EFI_IP_ADDRESS_INFO
*AddressInfo
,
157 IN UINT32 AddressCount
160 EFI_IP_ADDRESS Range
;
166 for (Index
= 0; Index
< AddressCount
; Index
++) {
168 // Check whether the target address is in the address range
169 // if it's a valid range of address.
171 if (IpSecValidAddressRange (
173 &AddressInfo
[Index
].Address
,
174 AddressInfo
[Index
].PrefixLength
177 // Get the range of the target address belongs to.
179 ZeroMem (&Range
, sizeof (EFI_IP_ADDRESS
));
180 IpSecExtractAddressRange (
182 AddressInfo
[Index
].PrefixLength
,
188 &AddressInfo
[Index
].Address
,
189 sizeof (EFI_IP_ADDRESS
)
192 // The target address is in the address range.
201 &AddressInfo
[Index
].Address
,
202 sizeof (EFI_IP_ADDRESS
)
205 // The target address is exact same as the address.
216 Check if the specified Protocol and Prot is supported by the specified SPD Entry.
218 This function is the subfunction of IPsecLookUpSpdEntry() that is used to
219 check if the sent/received IKE packet has the related SPD entry support.
221 @param[in] Protocol The Protocol to be checked.
222 @param[in] IpPayload Point to IP Payload to be check.
223 @param[in] SpdProtocol The Protocol supported by SPD.
224 @param[in] SpdLocalPort The Local Port in SPD.
225 @param[in] SpdRemotePort The Remote Port in SPD.
226 @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.
228 @retval TRUE The Protocol and Port are supported by the SPD Entry.
229 @retval FALSE The Protocol and Port are not supported by the SPD Entry.
233 IpSecMatchNextLayerProtocol (
236 IN UINT16 SpdProtocol
,
237 IN UINT16 SpdLocalPort
,
238 IN UINT16 SpdRemotePort
,
239 IN BOOLEAN IsOutbound
244 if (SpdProtocol
== EFI_IPSEC_ANY_PROTOCOL
) {
250 if (SpdProtocol
== Protocol
) {
252 case EFI_IP_PROTO_UDP
:
253 case EFI_IP_PROTO_TCP
:
255 // For udp and tcp, (0, 0) means no need to check local and remote
256 // port. The payload is passed from upper level, which means it should
257 // be in network order.
259 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
260 IsMatch
= (BOOLEAN
) (IsMatch
||
263 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->SrcPort
) == SpdLocalPort
&&
264 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->DstPort
) == SpdRemotePort
268 IsMatch
= (BOOLEAN
) (IsMatch
||
271 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->DstPort
) == SpdLocalPort
&&
272 NTOHS (((EFI_UDP_HEADER
*) IpPayload
)->SrcPort
) == SpdRemotePort
277 case EFI_IP_PROTO_ICMP
:
279 // For icmpv4, type code is replaced with local port and remote port,
280 // and (0, 0) means no need to check.
282 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
283 IsMatch
= (BOOLEAN
) (IsMatch
||
284 (BOOLEAN
) (((IP4_ICMP_HEAD
*) IpPayload
)->Type
== SpdLocalPort
&&
285 ((IP4_ICMP_HEAD
*) IpPayload
)->Code
== SpdRemotePort
292 // For icmpv6, type code is replaced with local port and remote port,
293 // and (0, 0) means no need to check.
295 IsMatch
= (BOOLEAN
) (SpdLocalPort
== 0 && SpdRemotePort
== 0);
297 IsMatch
= (BOOLEAN
) (IsMatch
||
298 (BOOLEAN
) (((IP6_ICMP_HEAD
*) IpPayload
)->Type
== SpdLocalPort
&&
299 ((IP6_ICMP_HEAD
*) IpPayload
)->Code
== SpdRemotePort
314 Find the SAD through a specified SPD's SAD list.
316 @param[in] SadList SAD list related to a specified SPD entry.
317 @param[in] DestAddress The destination address used to find the SAD entry.
319 @return The pointer to a certain SAD entry.
323 IpSecLookupSadBySpd (
324 IN LIST_ENTRY
*SadList
,
325 IN EFI_IP_ADDRESS
*DestAddress
329 IPSEC_SAD_ENTRY
*SadEntry
;
331 for (Entry
= SadList
->ForwardLink
; Entry
!= SadList
; Entry
= Entry
->ForwardLink
) {
333 SadEntry
= IPSEC_SAD_ENTRY_FROM_SPD (Entry
);
335 // Find the right sad entry which contains the appointed dest address.
338 &SadEntry
->Id
->DestAddress
,
340 sizeof (EFI_IP_ADDRESS
)
350 Find the SAD through whole SAD list.
352 @param[in] Spi The SPI used to search the SAD entry.
353 @param[in] DestAddress The destination used to search the SAD entry.
355 @return the pointer to a certain SAD entry.
359 IpSecLookupSadBySpi (
361 IN EFI_IP_ADDRESS
*DestAddress
366 IPSEC_SAD_ENTRY
*SadEntry
;
368 SadList
= &mConfigData
[IPsecConfigDataTypeSad
];
370 for (Entry
= SadList
->ForwardLink
; Entry
!= SadList
; Entry
= Entry
->ForwardLink
) {
372 SadEntry
= IPSEC_SAD_ENTRY_FROM_LIST (Entry
);
374 // Find the right sad entry which contain the appointed spi and dest addr.
376 if (SadEntry
->Id
->Spi
== Spi
&& CompareMem (
377 &SadEntry
->Id
->DestAddress
,
379 sizeof (EFI_IP_ADDRESS
)
390 Look up if there is existing SAD entry for specified IP packet sending.
392 This function is called by the IPsecProcess when there is some IP packet needed to
393 send out. This function checks if there is an existing SAD entry that can be serviced
394 to this IP packet sending. If no existing SAD entry could be used, this
395 function will invoke an IPsec Key Exchange Negotiation.
397 @param[in] Private Points to private data.
398 @param[in] NicHandle Points to a NIC handle.
399 @param[in] IpVersion The version of IP.
400 @param[in] IpHead The IP Header of packet to be sent out.
401 @param[in] IpPayload The IP Payload to be sent out.
402 @param[in] OldLastHead The Last protocol of the IP packet.
403 @param[in] SpdEntry Points to a related SPD entry.
404 @param[out] SadEntry Contains the Point of a related SAD entry.
406 @retval EFI_DEVICE_ERROR One of following conditions is TRUE:
407 - If don't find related UDP service.
408 - Sequence Number is used up.
409 - Extension Sequence Number is used up.
410 @retval EFI_DEVICE_ERROR GC_TODO: Add description for return value.
411 @retval EFI_NOT_READY No existing SAD entry could be used.
412 @retval EFI_SUCCESS Find the related SAD entry.
416 IpSecLookupSadEntry (
417 IN IPSEC_PRIVATE_DATA
*Private
,
418 IN EFI_HANDLE NicHandle
,
422 IN UINT8 OldLastHead
,
423 IN IPSEC_SPD_ENTRY
*SpdEntry
,
424 OUT IPSEC_SAD_ENTRY
**SadEntry
427 IPSEC_SAD_ENTRY
*Entry
;
428 IPSEC_SAD_DATA
*Data
;
429 EFI_IP_ADDRESS DestIp
;
434 // Parse the destination address from ip header.
436 ZeroMem (&DestIp
, sizeof (EFI_IP_ADDRESS
));
437 if (IpVersion
== IP_VERSION_4
) {
440 &((IP4_HEAD
*) IpHead
)->Dst
,
446 &((EFI_IP6_HEADER
*) IpHead
)->DestinationAddress
,
447 sizeof (EFI_IP_ADDRESS
)
451 // Find the sad entry in the spd.sas list according to the dest address.
453 Entry
= IpSecLookupSadBySpd (&SpdEntry
->Data
->Sas
, &DestIp
);
457 if (OldLastHead
!= IP6_ICMP
||
458 (OldLastHead
== IP6_ICMP
&& *IpPayload
== ICMP_V6_ECHO_REQUEST
)
461 // TODO: Start ike negotiation process except the request packet of ping.
463 //IkeNegotiate (UdpService, SpdEntry, &DestIp);
466 return EFI_NOT_READY
;
471 if (!Data
->ManualSet
) {
472 if (Data
->ESNEnabled
) {
474 // Validate the 64bit sn number if 64bit sn enabled.
476 if (Data
->SequenceNumber
+ 1 < Data
->SequenceNumber
) {
478 // TODO: Re-negotiate SA
480 return EFI_DEVICE_ERROR
;
484 // Validate the 32bit sn number if 64bit sn disabled.
486 SeqNum32
= (UINT32
) Data
->SequenceNumber
;
487 if (SeqNum32
+ 1 < SeqNum32
) {
489 // TODO: Re-negotiate SA
491 return EFI_DEVICE_ERROR
;
502 Find a PAD entry according to a remote IP address.
504 @param[in] IpVersion The version of IP.
505 @param[in] IpAddr Points to remote IP address.
507 @return the pointer of related PAD entry.
511 IpSecLookupPadEntry (
513 IN EFI_IP_ADDRESS
*IpAddr
518 EFI_IP_ADDRESS_INFO
*IpAddrInfo
;
519 IPSEC_PAD_ENTRY
*PadEntry
;
521 PadList
= &mConfigData
[IPsecConfigDataTypePad
];
523 for (Entry
= PadList
->ForwardLink
; Entry
!= PadList
; Entry
= Entry
->ForwardLink
) {
525 PadEntry
= IPSEC_PAD_ENTRY_FROM_LIST (Entry
);
526 IpAddrInfo
= &PadEntry
->Id
->Id
.IpAddress
;
528 // Find the right pad entry which contain the appointed dest addr.
530 if (IpSecMatchIpAddress (IpVersion
, IpAddr
, IpAddrInfo
, 1)) {
539 Check if the specified IP packet can be serviced by this SPD entry.
541 @param[in] SpdEntry Point to SPD entry.
542 @param[in] IpVersion Version of IP.
543 @param[in] IpHead Point to IP header.
544 @param[in] IpPayload Point to IP payload.
545 @param[in] Protocol The Last protocol of IP packet.
546 @param[in] IsOutbound Traffic direction.
548 @retval EFI_IPSEC_ACTION The support action of SPD entry.
549 @retval -1 If the input packet header doesn't match the SpdEntry.
553 IpSecLookupSpdEntry (
554 IN IPSEC_SPD_ENTRY
*SpdEntry
,
559 IN BOOLEAN IsOutbound
562 EFI_IPSEC_SPD_SELECTOR
*SpdSel
;
565 EFI_IP_ADDRESS SrcAddr
;
566 EFI_IP_ADDRESS DstAddr
;
569 ASSERT (SpdEntry
!= NULL
);
570 SpdSel
= SpdEntry
->Selector
;
571 Ip4
= (IP4_HEAD
*) IpHead
;
572 Ip6
= (EFI_IP6_HEADER
*) IpHead
;
574 ZeroMem (&SrcAddr
, sizeof (EFI_IP_ADDRESS
));
575 ZeroMem (&DstAddr
, sizeof (EFI_IP_ADDRESS
));
578 // Parse the source and destination address from ip header.
580 if (IpVersion
== IP_VERSION_4
) {
581 CopyMem (&SrcAddr
, &Ip4
->Src
, sizeof (IP4_ADDR
));
582 CopyMem (&DstAddr
, &Ip4
->Dst
, sizeof (IP4_ADDR
));
584 CopyMem (&SrcAddr
, &Ip6
->SourceAddress
, sizeof (EFI_IPv6_ADDRESS
));
585 CopyMem (&DstAddr
, &Ip6
->DestinationAddress
, sizeof (EFI_IPv6_ADDRESS
));
588 // Check the local and remote addresses for outbound traffic
590 SpdMatch
= (BOOLEAN
)(IsOutbound
&&
591 IpSecMatchIpAddress (
594 SpdSel
->LocalAddress
,
595 SpdSel
->LocalAddressCount
597 IpSecMatchIpAddress (
600 SpdSel
->RemoteAddress
,
601 SpdSel
->RemoteAddressCount
606 // Check the local and remote addresses for inbound traffic
608 SpdMatch
= (BOOLEAN
) (SpdMatch
||
610 IpSecMatchIpAddress (
613 SpdSel
->LocalAddress
,
614 SpdSel
->LocalAddressCount
616 IpSecMatchIpAddress (
619 SpdSel
->RemoteAddress
,
620 SpdSel
->RemoteAddressCount
625 // Check the next layer protocol and local and remote ports.
627 SpdMatch
= (BOOLEAN
) (SpdMatch
&&
628 IpSecMatchNextLayerProtocol (
631 SpdSel
->NextLayerProtocol
,
640 // Find the right spd entry if match the 5 key elements.
642 return SpdEntry
->Data
->Action
;
645 return (EFI_IPSEC_ACTION
) - 1;
649 Handles IPsec packet processing for inbound and outbound IP packets.
651 The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.
652 The behavior is that it can perform one of the following actions:
653 bypass the packet, discard the packet, or protect the packet.
655 @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.
656 @param[in] NicHandle Instance of the network interface.
657 @param[in] IpVersion IPV4 or IPV6.
658 @param[in, out] IpHead Pointer to the IP Header.
659 @param[in, out] LastHead The protocol of the next layer to be processed by IPsec.
660 @param[in, out] OptionsBuffer Pointer to the options buffer.
661 @param[in, out] OptionsLength Length of the options buffer.
662 @param[in, out] FragmentTable Pointer to a list of fragments.
663 @param[in, out] FragmentCount Number of fragments.
664 @param[in] TrafficDirection Traffic direction.
665 @param[out] RecycleSignal Event for recycling of resources.
667 @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
668 @retval EFI_SUCCESS The packet was protected.
669 @retval EFI_ACCESS_DENIED The packet was discarded.
675 IN EFI_IPSEC2_PROTOCOL
*This
,
676 IN EFI_HANDLE NicHandle
,
679 IN OUT UINT8
*LastHead
,
680 IN OUT VOID
**OptionsBuffer
,
681 IN OUT UINT32
*OptionsLength
,
682 IN OUT EFI_IPSEC_FRAGMENT_DATA
**FragmentTable
,
683 IN OUT UINT32
*FragmentCount
,
684 IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection
,
685 OUT EFI_EVENT
*RecycleSignal
688 IPSEC_PRIVATE_DATA
*Private
;
689 IPSEC_SPD_ENTRY
*SpdEntry
;
690 IPSEC_SAD_ENTRY
*SadEntry
;
693 EFI_IPSEC_ACTION Action
;
699 Private
= IPSEC_PRIVATE_DATA_FROM_IPSEC (This
);
700 IpPayload
= (*FragmentTable
)[0].FragmentBuffer
;
701 IsOutbound
= (BOOLEAN
) ((TrafficDirection
== EfiIPsecOutBound
) ? TRUE
: FALSE
);
702 OldLastHead
= *LastHead
;
703 *RecycleSignal
= NULL
;
707 // For inbound traffic, process the ipsec header of the packet.
709 Status
= IpSecProtectInboundPacket (
721 if (Status
== EFI_ACCESS_DENIED
) {
723 // The packet is denied to access.
728 if (Status
== EFI_SUCCESS
) {
730 // Check the spd entry if the packet is accessible.
732 if (SpdEntry
== NULL
) {
733 Status
= EFI_ACCESS_DENIED
;
736 Action
= IpSecLookupSpdEntry (
745 if (Action
!= EfiIPsecActionProtect
) {
747 // Discard the packet if the spd entry is not protect.
749 gBS
->SignalEvent (*RecycleSignal
);
750 *RecycleSignal
= NULL
;
751 Status
= EFI_ACCESS_DENIED
;
758 Status
= EFI_ACCESS_DENIED
;
759 SpdList
= &mConfigData
[IPsecConfigDataTypeSpd
];
761 for (Entry
= SpdList
->ForwardLink
; Entry
!= SpdList
; Entry
= Entry
->ForwardLink
) {
763 // For outbound and non-ipsec Inbound traffic: check the spd entry.
765 SpdEntry
= IPSEC_SPD_ENTRY_FROM_LIST (Entry
);
766 Action
= IpSecLookupSpdEntry (
777 case EfiIPsecActionProtect
:
781 // For outbound traffic, lookup the sad entry.
783 Status
= IpSecLookupSadEntry (
794 if (SadEntry
!= NULL
) {
796 // Process the packet by the found sad entry.
798 Status
= IpSecProtectOutboundPacket (
810 } else if (OldLastHead
== IP6_ICMP
&& *IpPayload
!= ICMP_V6_ECHO_REQUEST
) {
812 // TODO: if no need return not ready to upper layer, change here.
814 Status
= EFI_SUCCESS
;
816 } else if (OldLastHead
== IP6_ICMP
&& *IpPayload
!= ICMP_V6_ECHO_REQUEST
) {
818 // For inbound icmpv6 traffic except ping request, accept the packet
819 // although no sad entry associated with protect spd entry.
821 IpSecLookupSadEntry (
831 if (SadEntry
== NULL
) {
832 Status
= EFI_SUCCESS
;
838 case EfiIPsecActionBypass
:
839 Status
= EFI_SUCCESS
;
842 case EfiIPsecActionDiscard
:
847 // Discard the packet if no spd entry match.