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