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