]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IpSecDxe/IpSecImpl.c
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
[mirror_edk2.git] / NetworkPkg / IpSecDxe / IpSecImpl.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 The implementation of IPsec Protocol\r
3\r
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "IpSecConfigImpl.h"\r
17\r
18EFI_IPSEC_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE };\r
19\r
20extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];\r
21\r
22/**\r
23 Check if the specified Address is the Valid Address Range.\r
24\r
25 This function checks if the bytes after prefixed length are all Zero in this\r
26 Address. This Address is supposed to point to a range address, meaning it only\r
27 gives the correct prefixed address.\r
28\r
29 @param[in] IpVersion The IP version.\r
30 @param[in] Address Points to EFI_IP_ADDRESS to be checked.\r
31 @param[in] PrefixLength The PrefixeLength of this address.\r
32\r
33 @retval TRUE The address is a vaild address range.\r
34 @retval FALSE The address is not a vaild address range.\r
35\r
36**/\r
37BOOLEAN\r
38IpSecValidAddressRange (\r
39 IN UINT8 IpVersion,\r
40 IN EFI_IP_ADDRESS *Address,\r
41 IN UINT8 PrefixLength\r
42 )\r
43{\r
44 UINT8 Div;\r
45 UINT8 Mod;\r
46 UINT8 Mask;\r
47 UINT8 AddrLen;\r
48 UINT8 *Addr;\r
49 EFI_IP_ADDRESS ZeroAddr;\r
50\r
51 if (PrefixLength == 0) {\r
52 return TRUE;\r
53 }\r
54\r
55 AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);\r
56\r
57 if (AddrLen <= PrefixLength) {\r
58 return FALSE;\r
59 }\r
60\r
61 Div = (UINT8) (PrefixLength / 8);\r
62 Mod = (UINT8) (PrefixLength % 8);\r
63 Addr = (UINT8 *) Address;\r
64 ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
65\r
66 //\r
67 // Check whether the mod part of host scope is zero or not.\r
68 //\r
69 if (Mod > 0) {\r
70 Mask = (UINT8) (0xFF << (8 - Mod));\r
71\r
72 if ((Addr[Div] | Mask) != Mask) {\r
73 return FALSE;\r
74 }\r
75\r
76 Div++;\r
77 }\r
78 //\r
79 // Check whether the div part of host scope is zero or not.\r
80 //\r
81 if (CompareMem (\r
82 &Addr[Div],\r
83 &ZeroAddr,\r
84 sizeof (EFI_IP_ADDRESS) - Div\r
85 ) != 0) {\r
86 return FALSE;\r
87 }\r
88\r
89 return TRUE;\r
90}\r
91\r
92/**\r
93 Extrct the Address Range from a Address.\r
94\r
95 This function keep the prefix address and zero other part address.\r
96\r
97 @param[in] Address Point to a specified address.\r
98 @param[in] PrefixLength The prefix length.\r
99 @param[out] Range Contain the return Address Range.\r
100\r
101**/\r
102VOID\r
103IpSecExtractAddressRange (\r
104 IN EFI_IP_ADDRESS *Address,\r
105 IN UINT8 PrefixLength,\r
106 OUT EFI_IP_ADDRESS *Range\r
107 )\r
108{\r
109 UINT8 Div;\r
110 UINT8 Mod;\r
111 UINT8 Mask;\r
112 UINT8 *Addr;\r
113\r
114 if (PrefixLength == 0) {\r
115 return ;\r
116 }\r
117\r
118 Div = (UINT8) (PrefixLength / 8);\r
119 Mod = (UINT8) (PrefixLength % 8);\r
120 Addr = (UINT8 *) Range;\r
121\r
122 CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));\r
123\r
124 //\r
125 // Zero the mod part of host scope.\r
126 //\r
127 if (Mod > 0) {\r
128 Mask = (UINT8) (0xFF << (8 - Mod));\r
129 Addr[Div] = (UINT8) (Addr[Div] & Mask);\r
130 Div++;\r
131 }\r
132 //\r
133 // Zero the div part of host scope.\r
134 //\r
135 ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);\r
136\r
137}\r
138\r
139/**\r
140 Checks if the IP Address in the address range of AddressInfos specified.\r
141\r
142 @param[in] IpVersion The IP version.\r
143 @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.\r
144 @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check\r
145 the IP Address is matched.\r
146 @param[in] AddressCount The total numbers of the AddressInfo.\r
147\r
148 @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.\r
149 @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.\r
150\r
151**/\r
152BOOLEAN\r
153IpSecMatchIpAddress (\r
154 IN UINT8 IpVersion,\r
155 IN EFI_IP_ADDRESS *IpAddr,\r
156 IN EFI_IP_ADDRESS_INFO *AddressInfo,\r
157 IN UINT32 AddressCount\r
158 )\r
159{\r
160 EFI_IP_ADDRESS Range;\r
161 UINT32 Index;\r
162 BOOLEAN IsMatch;\r
163\r
164 IsMatch = FALSE;\r
165\r
166 for (Index = 0; Index < AddressCount; Index++) {\r
167 //\r
168 // Check whether the target address is in the address range\r
169 // if it's a valid range of address.\r
170 //\r
171 if (IpSecValidAddressRange (\r
172 IpVersion,\r
173 &AddressInfo[Index].Address,\r
174 AddressInfo[Index].PrefixLength\r
175 )) {\r
176 //\r
177 // Get the range of the target address belongs to.\r
178 //\r
179 ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));\r
180 IpSecExtractAddressRange (\r
181 IpAddr,\r
182 AddressInfo[Index].PrefixLength,\r
183 &Range\r
184 );\r
185\r
186 if (CompareMem (\r
187 &Range,\r
188 &AddressInfo[Index].Address,\r
189 sizeof (EFI_IP_ADDRESS)\r
190 ) == 0) {\r
191 //\r
192 // The target address is in the address range.\r
193 //\r
194 IsMatch = TRUE;\r
195 break;\r
196 }\r
197 }\r
198\r
199 if (CompareMem (\r
200 IpAddr,\r
201 &AddressInfo[Index].Address,\r
202 sizeof (EFI_IP_ADDRESS)\r
203 ) == 0) {\r
204 //\r
205 // The target address is exact same as the address.\r
206 //\r
207 IsMatch = TRUE;\r
208 break;\r
209 }\r
210 }\r
211\r
212 return IsMatch;\r
213}\r
214\r
215/**\r
216 Check if the specified Protocol and Prot is supported by the specified SPD Entry.\r
217\r
218 This function is the subfunction of IPsecLookUpSpdEntry() that is used to\r
219 check if the sent/received IKE packet has the related SPD entry support.\r
220\r
221 @param[in] Protocol The Protocol to be checked.\r
222 @param[in] IpPayload Point to IP Payload to be check.\r
223 @param[in] SpdProtocol The Protocol supported by SPD.\r
224 @param[in] SpdLocalPort The Local Port in SPD.\r
225 @param[in] SpdRemotePort The Remote Port in SPD.\r
226 @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.\r
227\r
228 @retval TRUE The Protocol and Port are supported by the SPD Entry.\r
229 @retval FALSE The Protocol and Port are not supported by the SPD Entry.\r
230\r
231**/\r
232BOOLEAN\r
233IpSecMatchNextLayerProtocol (\r
234 IN UINT8 Protocol,\r
235 IN UINT8 *IpPayload,\r
236 IN UINT16 SpdProtocol,\r
237 IN UINT16 SpdLocalPort,\r
238 IN UINT16 SpdRemotePort,\r
239 IN BOOLEAN IsOutbound\r
240 )\r
241{\r
242 BOOLEAN IsMatch;\r
243\r
244 if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {\r
245 return TRUE;\r
246 }\r
247\r
248 IsMatch = FALSE;\r
249\r
250 if (SpdProtocol == Protocol) {\r
251 switch (Protocol) {\r
252 case EFI_IP_PROTO_UDP:\r
253 case EFI_IP_PROTO_TCP:\r
254 //\r
255 // For udp and tcp, (0, 0) means no need to check local and remote\r
256 // port. The payload is passed from upper level, which means it should\r
257 // be in network order.\r
258 //\r
259 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
260 IsMatch = (BOOLEAN) (IsMatch ||\r
261 (IsOutbound &&\r
262 (BOOLEAN)(\r
263 NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&\r
264 NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort\r
265 )\r
266 ));\r
267\r
268 IsMatch = (BOOLEAN) (IsMatch ||\r
269 (!IsOutbound &&\r
270 (BOOLEAN)(\r
271 NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&\r
272 NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort\r
273 )\r
274 ));\r
275 break;\r
276\r
277 case EFI_IP_PROTO_ICMP:\r
278 //\r
279 // For icmpv4, type code is replaced with local port and remote port,\r
280 // and (0, 0) means no need to check.\r
281 //\r
282 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
283 IsMatch = (BOOLEAN) (IsMatch ||\r
284 (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
285 ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
286 )\r
287 );\r
288 break;\r
289\r
290 case IP6_ICMP:\r
291 //\r
292 // For icmpv6, type code is replaced with local port and remote port,\r
293 // and (0, 0) means no need to check.\r
294 //\r
295 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
296\r
297 IsMatch = (BOOLEAN) (IsMatch ||\r
298 (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
299 ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
300 )\r
301 );\r
302 break;\r
303\r
304 default:\r
305 IsMatch = TRUE;\r
306 break;\r
307 }\r
308 }\r
309\r
310 return IsMatch;\r
311}\r
312\r
313/**\r
314 Find the SAD through a specified SPD's SAD list.\r
315\r
316 @param[in] SadList SAD list related to a specified SPD entry.\r
317 @param[in] DestAddress The destination address used to find the SAD entry.\r
318\r
319 @return The pointer to a certain SAD entry.\r
320\r
321**/\r
322IPSEC_SAD_ENTRY *\r
323IpSecLookupSadBySpd (\r
324 IN LIST_ENTRY *SadList,\r
325 IN EFI_IP_ADDRESS *DestAddress\r
326 )\r
327{\r
328 LIST_ENTRY *Entry;\r
329 IPSEC_SAD_ENTRY *SadEntry;\r
330\r
331 for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
332\r
333 SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
334 //\r
335 // Find the right sad entry which contains the appointed dest address.\r
336 //\r
337 if (CompareMem (\r
338 &SadEntry->Id->DestAddress,\r
339 DestAddress,\r
340 sizeof (EFI_IP_ADDRESS)\r
341 ) == 0) {\r
342 return SadEntry;\r
343 }\r
344 }\r
345\r
346 return NULL;\r
347}\r
348\r
349/**\r
350 Find the SAD through whole SAD list.\r
351\r
352 @param[in] Spi The SPI used to search the SAD entry.\r
353 @param[in] DestAddress The destination used to search the SAD entry.\r
354\r
355 @return the pointer to a certain SAD entry.\r
356\r
357**/\r
358IPSEC_SAD_ENTRY *\r
359IpSecLookupSadBySpi (\r
360 IN UINT32 Spi,\r
361 IN EFI_IP_ADDRESS *DestAddress\r
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
370 for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
371\r
372 SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
373 //\r
374 // Find the right sad entry which contain the appointed spi and dest addr.\r
375 //\r
376 if (SadEntry->Id->Spi == Spi && CompareMem (\r
377 &SadEntry->Id->DestAddress,\r
378 DestAddress,\r
379 sizeof (EFI_IP_ADDRESS)\r
380 ) == 0) {\r
381\r
382 return SadEntry;\r
383 }\r
384 }\r
385\r
386 return NULL;\r
387}\r
388\r
389/**\r
390 Look up if there is existing SAD entry for specified IP packet sending.\r
391\r
392 This function is called by the IPsecProcess when there is some IP packet needed to\r
393 send out. This function checks if there is an existing SAD entry that can be serviced\r
394 to this IP packet sending. If no existing SAD entry could be used, this\r
395 function will invoke an IPsec Key Exchange Negotiation.\r
396\r
397 @param[in] Private Points to private data.\r
398 @param[in] NicHandle Points to a NIC handle.\r
399 @param[in] IpVersion The version of IP.\r
400 @param[in] IpHead The IP Header of packet to be sent out.\r
401 @param[in] IpPayload The IP Payload to be sent out.\r
402 @param[in] OldLastHead The Last protocol of the IP packet.\r
403 @param[in] SpdEntry Points to a related SPD entry.\r
404 @param[out] SadEntry Contains the Point of a related SAD entry.\r
405\r
406 @retval EFI_DEVICE_ERROR One of following conditions is TRUE:\r
407 - If don't find related UDP service.\r
408 - Sequence Number is used up.\r
409 - Extension Sequence Number is used up.\r
410 @retval EFI_DEVICE_ERROR GC_TODO: Add description for return value.\r
411 @retval EFI_NOT_READY No existing SAD entry could be used.\r
412 @retval EFI_SUCCESS Find the related SAD entry.\r
413\r
414**/\r
415EFI_STATUS\r
416IpSecLookupSadEntry (\r
417 IN IPSEC_PRIVATE_DATA *Private,\r
418 IN EFI_HANDLE NicHandle,\r
419 IN UINT8 IpVersion,\r
420 IN VOID *IpHead,\r
421 IN UINT8 *IpPayload,\r
422 IN UINT8 OldLastHead,\r
423 IN IPSEC_SPD_ENTRY *SpdEntry,\r
424 OUT IPSEC_SAD_ENTRY **SadEntry\r
425 )\r
426{\r
427 IPSEC_SAD_ENTRY *Entry;\r
428 IPSEC_SAD_DATA *Data;\r
429 EFI_IP_ADDRESS DestIp;\r
430 UINT32 SeqNum32;\r
431\r
432 *SadEntry = NULL;\r
433 //\r
434 // Parse the destination address from ip header.\r
435 //\r
436 ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
437 if (IpVersion == IP_VERSION_4) {\r
438 CopyMem (\r
439 &DestIp,\r
440 &((IP4_HEAD *) IpHead)->Dst,\r
441 sizeof (IP4_ADDR)\r
442 );\r
443 } else {\r
444 CopyMem (\r
445 &DestIp,\r
446 &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
447 sizeof (EFI_IP_ADDRESS)\r
448 );\r
449 }\r
450 //\r
451 // Find the sad entry in the spd.sas list according to the dest address.\r
452 //\r
453 Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);\r
454\r
455 if (Entry == NULL) {\r
456\r
457 if (OldLastHead != IP6_ICMP ||\r
458 (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)\r
459 ) {\r
460 //\r
461 // TODO: Start ike negotiation process except the request packet of ping.\r
462 //\r
463 //IkeNegotiate (UdpService, SpdEntry, &DestIp);\r
464 }\r
465\r
466 return EFI_NOT_READY;\r
467 }\r
468\r
469 Data = Entry->Data;\r
470\r
471 if (!Data->ManualSet) {\r
472 if (Data->ESNEnabled) {\r
473 //\r
474 // Validate the 64bit sn number if 64bit sn enabled.\r
475 //\r
476 if (Data->SequenceNumber + 1 < Data->SequenceNumber) {\r
477 //\r
478 // TODO: Re-negotiate SA\r
479 //\r
480 return EFI_DEVICE_ERROR;\r
481 }\r
482 } else {\r
483 //\r
484 // Validate the 32bit sn number if 64bit sn disabled.\r
485 //\r
486 SeqNum32 = (UINT32) Data->SequenceNumber;\r
487 if (SeqNum32 + 1 < SeqNum32) {\r
488 //\r
489 // TODO: Re-negotiate SA\r
490 //\r
491 return EFI_DEVICE_ERROR;\r
492 }\r
493 }\r
494 }\r
495\r
496 *SadEntry = Entry;\r
497\r
498 return EFI_SUCCESS;\r
499}\r
500\r
501/**\r
502 Find a PAD entry according to a remote IP address.\r
503\r
504 @param[in] IpVersion The version of IP.\r
505 @param[in] IpAddr Points to remote IP address.\r
506\r
507 @return the pointer of related PAD entry.\r
508\r
509**/\r
510IPSEC_PAD_ENTRY *\r
511IpSecLookupPadEntry (\r
512 IN UINT8 IpVersion,\r
513 IN EFI_IP_ADDRESS *IpAddr\r
514 )\r
515{\r
516 LIST_ENTRY *PadList;\r
517 LIST_ENTRY *Entry;\r
518 EFI_IP_ADDRESS_INFO *IpAddrInfo;\r
519 IPSEC_PAD_ENTRY *PadEntry;\r
520\r
521 PadList = &mConfigData[IPsecConfigDataTypePad];\r
522\r
523 for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {\r
524\r
525 PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
526 IpAddrInfo = &PadEntry->Id->Id.IpAddress;\r
527 //\r
528 // Find the right pad entry which contain the appointed dest addr.\r
529 //\r
530 if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {\r
531 return PadEntry;\r
532 }\r
533 }\r
534\r
535 return NULL;\r
536}\r
537\r
538/**\r
539 Check if the specified IP packet can be serviced by this SPD entry.\r
540\r
541 @param[in] SpdEntry Point to SPD entry.\r
542 @param[in] IpVersion Version of IP.\r
543 @param[in] IpHead Point to IP header.\r
544 @param[in] IpPayload Point to IP payload.\r
545 @param[in] Protocol The Last protocol of IP packet.\r
546 @param[in] IsOutbound Traffic direction.\r
547\r
548 @retval EFI_IPSEC_ACTION The support action of SPD entry.\r
549 @retval -1 If the input packet header doesn't match the SpdEntry.\r
550\r
551**/\r
552EFI_IPSEC_ACTION\r
553IpSecLookupSpdEntry (\r
554 IN IPSEC_SPD_ENTRY *SpdEntry,\r
555 IN UINT8 IpVersion,\r
556 IN VOID *IpHead,\r
557 IN UINT8 *IpPayload,\r
558 IN UINT8 Protocol,\r
559 IN BOOLEAN IsOutbound\r
560 )\r
561{\r
562 EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
563 IP4_HEAD *Ip4;\r
564 EFI_IP6_HEADER *Ip6;\r
565 EFI_IP_ADDRESS SrcAddr;\r
566 EFI_IP_ADDRESS DstAddr;\r
567 BOOLEAN SpdMatch;\r
568\r
569 ASSERT (SpdEntry != NULL);\r
570 SpdSel = SpdEntry->Selector;\r
571 Ip4 = (IP4_HEAD *) IpHead;\r
572 Ip6 = (EFI_IP6_HEADER *) IpHead;\r
573\r
574 ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));\r
575 ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));\r
576\r
577 //\r
578 // Parse the source and destination address from ip header.\r
579 //\r
580 if (IpVersion == IP_VERSION_4) {\r
581 CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));\r
582 CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));\r
583 } else {\r
584 CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
585 CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));\r
586 }\r
587 //\r
588 // Check the local and remote addresses for outbound traffic\r
589 //\r
590 SpdMatch = (BOOLEAN)(IsOutbound &&\r
591 IpSecMatchIpAddress (\r
592 IpVersion,\r
593 &SrcAddr,\r
594 SpdSel->LocalAddress,\r
595 SpdSel->LocalAddressCount\r
596 ) &&\r
597 IpSecMatchIpAddress (\r
598 IpVersion,\r
599 &DstAddr,\r
600 SpdSel->RemoteAddress,\r
601 SpdSel->RemoteAddressCount\r
602 )\r
603 );\r
604\r
605 //\r
606 // Check the local and remote addresses for inbound traffic\r
607 //\r
608 SpdMatch = (BOOLEAN) (SpdMatch ||\r
609 (!IsOutbound &&\r
610 IpSecMatchIpAddress (\r
611 IpVersion,\r
612 &DstAddr,\r
613 SpdSel->LocalAddress,\r
614 SpdSel->LocalAddressCount\r
615 ) &&\r
616 IpSecMatchIpAddress (\r
617 IpVersion,\r
618 &SrcAddr,\r
619 SpdSel->RemoteAddress,\r
620 SpdSel->RemoteAddressCount\r
621 )\r
622 ));\r
623\r
624 //\r
625 // Check the next layer protocol and local and remote ports.\r
626 //\r
627 SpdMatch = (BOOLEAN) (SpdMatch &&\r
628 IpSecMatchNextLayerProtocol (\r
629 Protocol,\r
630 IpPayload,\r
631 SpdSel->NextLayerProtocol,\r
632 SpdSel->LocalPort,\r
633 SpdSel->RemotePort,\r
634 IsOutbound\r
635 )\r
636 );\r
637\r
638 if (SpdMatch) {\r
639 //\r
640 // Find the right spd entry if match the 5 key elements.\r
641 //\r
642 return SpdEntry->Data->Action;\r
643 }\r
644\r
645 return (EFI_IPSEC_ACTION) - 1;\r
646}\r
647\r
648/**\r
649 Handles IPsec packet processing for inbound and outbound IP packets.\r
650\r
651 The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.\r
652 The behavior is that it can perform one of the following actions:\r
653 bypass the packet, discard the packet, or protect the packet.\r
654\r
655 @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.\r
656 @param[in] NicHandle Instance of the network interface.\r
657 @param[in] IpVersion IPV4 or IPV6.\r
658 @param[in, out] IpHead Pointer to the IP Header.\r
659 @param[in] LastHead The protocol of the next layer to be processed by IPsec.\r
660 @param[in] OptionsBuffer Pointer to the options buffer.\r
661 @param[in] OptionsLength Length of the options buffer.\r
662 @param[in, out] FragmentTable Pointer to a list of fragments.\r
663 @param[in] FragmentCount Number of fragments.\r
664 @param[in] TrafficDirection Traffic direction.\r
665 @param[out] RecycleSignal Event for recycling of resources.\r
666\r
667 @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.\r
668 @retval EFI_SUCCESS The packet was protected.\r
669 @retval EFI_ACCESS_DENIED The packet was discarded.\r
670\r
671**/\r
672EFI_STATUS\r
673EFIAPI\r
674IpSecProcess (\r
675 IN EFI_IPSEC_PROTOCOL *This,\r
676 IN EFI_HANDLE NicHandle,\r
677 IN UINT8 IpVersion,\r
678 IN OUT VOID *IpHead,\r
679 IN UINT8 *LastHead,\r
680 IN VOID *OptionsBuffer,\r
681 IN UINT32 OptionsLength,\r
682 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
683 IN UINT32 *FragmentCount,\r
684 IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,\r
685 OUT EFI_EVENT *RecycleSignal\r
686 )\r
687{\r
688 IPSEC_PRIVATE_DATA *Private;\r
689 IPSEC_SPD_ENTRY *SpdEntry;\r
690 IPSEC_SAD_ENTRY *SadEntry;\r
691 LIST_ENTRY *SpdList;\r
692 LIST_ENTRY *Entry;\r
693 EFI_IPSEC_ACTION Action;\r
694 EFI_STATUS Status;\r
695 UINT8 *IpPayload;\r
696 UINT8 OldLastHead;\r
697 BOOLEAN IsOutbound;\r
698\r
699 Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);\r
700 IpPayload = (*FragmentTable)[0].FragmentBuffer;\r
701 IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);\r
702 OldLastHead = *LastHead;\r
703 *RecycleSignal = NULL;\r
704\r
705 if (!IsOutbound) {\r
706 //\r
707 // For inbound traffic, process the ipsec header of the packet.\r
708 //\r
709 Status = IpSecProtectInboundPacket (\r
710 IpVersion,\r
711 IpHead,\r
712 LastHead,\r
713 OptionsBuffer,\r
714 OptionsLength,\r
715 FragmentTable,\r
716 FragmentCount,\r
717 &SpdEntry,\r
718 RecycleSignal\r
719 );\r
720\r
721 if (Status == EFI_ACCESS_DENIED) {\r
722 //\r
723 // The packet is denied to access.\r
724 //\r
725 goto ON_EXIT;\r
726 }\r
727\r
728 if (Status == EFI_SUCCESS) {\r
729 //\r
730 // Check the spd entry if the packet is accessible.\r
731 //\r
732 if (SpdEntry == NULL) {\r
733 Status = EFI_ACCESS_DENIED;\r
734 goto ON_EXIT;\r
735 }\r
736 Action = IpSecLookupSpdEntry (\r
737 SpdEntry,\r
738 IpVersion,\r
739 IpHead,\r
740 IpPayload,\r
741 *LastHead,\r
742 IsOutbound\r
743 );\r
744\r
745 if (Action != EfiIPsecActionProtect) {\r
746 //\r
747 // Discard the packet if the spd entry is not protect.\r
748 //\r
749 gBS->SignalEvent (*RecycleSignal);\r
750 *RecycleSignal = NULL;\r
751 Status = EFI_ACCESS_DENIED;\r
752 }\r
753\r
754 goto ON_EXIT;\r
755 }\r
756 }\r
757\r
758 Status = EFI_ACCESS_DENIED;\r
759 SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
760\r
761 for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {\r
762 //\r
763 // For outbound and non-ipsec Inbound traffic: check the spd entry.\r
764 //\r
765 SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
766 Action = IpSecLookupSpdEntry (\r
767 SpdEntry,\r
768 IpVersion,\r
769 IpHead,\r
770 IpPayload,\r
771 OldLastHead,\r
772 IsOutbound\r
773 );\r
774\r
775 switch (Action) {\r
776\r
777 case EfiIPsecActionProtect:\r
778\r
779 if (IsOutbound) {\r
780 //\r
781 // For outbound traffic, lookup the sad entry.\r
782 //\r
783 Status = IpSecLookupSadEntry (\r
784 Private,\r
785 NicHandle,\r
786 IpVersion,\r
787 IpHead,\r
788 IpPayload,\r
789 OldLastHead,\r
790 SpdEntry,\r
791 &SadEntry\r
792 );\r
793\r
794 if (SadEntry != NULL) {\r
795 //\r
796 // Process the packet by the found sad entry.\r
797 //\r
798 Status = IpSecProtectOutboundPacket (\r
799 IpVersion,\r
800 IpHead,\r
801 LastHead,\r
802 OptionsBuffer,\r
803 OptionsLength,\r
804 FragmentTable,\r
805 FragmentCount,\r
806 SadEntry,\r
807 RecycleSignal\r
808 );\r
809\r
810 } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
811 //\r
812 // TODO: if no need return not ready to upper layer, change here.\r
813 //\r
814 Status = EFI_SUCCESS;\r
815 }\r
816 } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
817 //\r
818 // For inbound icmpv6 traffic except ping request, accept the packet\r
819 // although no sad entry associated with protect spd entry.\r
820 //\r
821 IpSecLookupSadEntry (\r
822 Private,\r
823 NicHandle,\r
824 IpVersion,\r
825 IpHead,\r
826 IpPayload,\r
827 OldLastHead,\r
828 SpdEntry,\r
829 &SadEntry\r
830 );\r
831 if (SadEntry == NULL) {\r
832 Status = EFI_SUCCESS;\r
833 }\r
834 }\r
835\r
836 goto ON_EXIT;\r
837\r
838 case EfiIPsecActionBypass:\r
839 Status = EFI_SUCCESS;\r
840 goto ON_EXIT;\r
841\r
842 case EfiIPsecActionDiscard:\r
843 goto ON_EXIT;\r
844\r
845 default:\r
846 //\r
847 // Discard the packet if no spd entry match.\r
848 //\r
849 break;\r
850 }\r
851 }\r
852\r
853ON_EXIT:\r
854 return Status;\r
855}\r
856\r