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